001    /* DefaultTreeCellRenderer.java 
002     Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
003     
004     This file is part of GNU Classpath.
005    
006     GNU Classpath is free software; you can redistribute it and/or modify
007     it under the terms of the GNU General Public License as published by
008     the Free Software Foundation; either version 2, or (at your option)
009     any later version.
010    
011     GNU Classpath is distributed in the hope that it will be useful, but
012     WITHOUT ANY WARRANTY; without even the implied warranty of
013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     General Public License for more details.
015    
016     You should have received a copy of the GNU General Public License
017     along with GNU Classpath; see the file COPYING.  If not, write to the
018     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019     02110-1301 USA.
020    
021     Linking this library statically or dynamically with other modules is
022     making a combined work based on this library.  Thus, the terms and
023     conditions of the GNU General Public License cover the whole
024     combination.
025    
026     As a special exception, the copyright holders of this library give you
027     permission to link this library with independent modules to produce an
028     executable, regardless of the license terms of these independent
029     modules, and to copy and distribute the resulting executable under
030     terms of your choice, provided that you also meet, for each linked
031     independent module, the terms and conditions of the license of that
032     module.  An independent module is a module which is not derived from
033     or based on this library.  If you modify this library, you may extend
034     this exception to your version of the library, but you are not
035     obligated to do so.  If you do not wish to do so, delete this
036     exception statement from your version. */
037    
038    
039    package javax.swing.tree;
040    
041    import java.awt.Color;
042    import java.awt.Component;
043    import java.awt.Dimension;
044    import java.awt.Font;
045    import java.awt.FontMetrics;
046    import java.awt.Graphics;
047    import java.awt.Insets;
048    import java.awt.Rectangle;
049    
050    import javax.swing.Icon;
051    import javax.swing.JLabel;
052    import javax.swing.JTree;
053    import javax.swing.LookAndFeel;
054    import javax.swing.SwingUtilities;
055    import javax.swing.UIManager;
056    import javax.swing.border.Border;
057    import javax.swing.plaf.UIResource;
058    
059    /**
060     * A default implementation of the {@link TreeCellRenderer} interface.
061     * 
062     * @author Andrew Selkirk
063     */
064    public class DefaultTreeCellRenderer
065      extends JLabel
066      implements TreeCellRenderer
067    {
068    
069      /**
070       * A flag indicating the current selection status.
071       */
072      protected boolean selected;
073    
074      /**
075       * A flag indicating the current focus status.
076       */
077      protected boolean hasFocus;
078    
079      /**
080       * Indicates if the focus border is also drawn around the icon.
081       */
082      private boolean drawsFocusBorderAroundIcon;
083    
084      /**
085       * The icon used to represent non-leaf nodes that are closed.
086       * 
087       * @see #setClosedIcon(Icon)
088       */
089      protected transient Icon closedIcon;
090    
091      /**
092       * The icon used to represent leaf nodes.
093       * 
094       * @see #setLeafIcon(Icon)
095       */
096      protected transient Icon leafIcon;
097    
098      /**
099       * The icon used to represent non-leaf nodes that are open.
100       * 
101       * @see #setOpenIcon(Icon)
102       */
103      protected transient Icon openIcon;
104    
105      /**
106       * The color used for text in selected cells.
107       * 
108       * @see #setTextSelectionColor(Color)
109       */
110      protected Color textSelectionColor;
111    
112      /**
113       * The color used for text in non-selected cells.
114       * 
115       * @see #setTextNonSelectionColor(Color)
116       */
117      protected Color textNonSelectionColor;
118    
119      /**
120       * The background color for selected cells.
121       * 
122       * @see #setBackgroundSelectionColor(Color)
123       */
124      protected Color backgroundSelectionColor;
125    
126      /**
127       * The background color for non-selected cells.
128       * 
129       * @see #setBackgroundNonSelectionColor(Color)
130       */
131      protected Color backgroundNonSelectionColor;
132    
133      /**
134       * The border color for selected tree cells.
135       * 
136       * @see #setBorderSelectionColor(Color)
137       */
138      protected Color borderSelectionColor;
139    
140      /**
141       * Creates a new tree cell renderer with defaults appropriate for the 
142       * current {@link LookAndFeel}.
143       */
144      public DefaultTreeCellRenderer()
145      {
146        setLeafIcon(getDefaultLeafIcon());
147        setOpenIcon(getDefaultOpenIcon());
148        setClosedIcon(getDefaultClosedIcon());
149    
150        setTextNonSelectionColor(UIManager.getColor("Tree.textForeground"));
151        setTextSelectionColor(UIManager.getColor("Tree.selectionForeground"));
152        setBackgroundNonSelectionColor(UIManager.getColor("Tree.textBackground"));
153        setBackgroundSelectionColor(UIManager.getColor("Tree.selectionBackground"));
154        setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
155        Object val = UIManager.get("Tree.drawsFocusBorderAroundIcon");
156        drawsFocusBorderAroundIcon = val != null && ((Boolean) val).booleanValue();
157      }
158    
159      /**
160       * Returns the default icon for non-leaf tree cells that are open (expanded).
161       * The icon is fetched from the defaults table for the current 
162       * {@link LookAndFeel} using the key <code>Tree.openIcon</code>.
163       * 
164       * @return The default icon.
165       */
166      public Icon getDefaultOpenIcon()
167      {
168        return UIManager.getIcon("Tree.openIcon");
169      }
170    
171      /**
172       * Returns the default icon for non-leaf tree cells that are closed (not 
173       * expanded).  The icon is fetched from the defaults table for the current 
174       * {@link LookAndFeel} using the key <code>Tree.closedIcon</code>.
175       * 
176       * @return The default icon.
177       */
178      public Icon getDefaultClosedIcon()
179      {
180        return UIManager.getIcon("Tree.closedIcon");
181      }
182    
183      /**
184       * Returns the default icon for leaf tree cells.  The icon is fetched from 
185       * the defaults table for the current {@link LookAndFeel} using the key 
186       * <code>Tree.leafIcon</code>.
187       * 
188       * @return The default icon.
189       */
190      public Icon getDefaultLeafIcon()
191      {
192        return UIManager.getIcon("Tree.leafIcon");
193      }
194    
195      /**
196       * Sets the icon to be displayed for non-leaf nodes that are open (expanded).
197       * Set this to <code>null</code> if no icon is required.
198       * 
199       * @param icon  the icon (<code>null</code> permitted).
200       * 
201       * @see #getOpenIcon()
202       */
203      public void setOpenIcon(Icon icon)
204      {
205        openIcon = icon;
206      }
207    
208      /**
209       * Returns the icon displayed for non-leaf nodes that are open (expanded).  
210       * The default value is initialised from the {@link LookAndFeel}.
211       * 
212       * @return The open icon (possibly <code>null</code>).
213       * 
214       * @see #setOpenIcon(Icon)
215       */
216      public Icon getOpenIcon()
217      {
218        return openIcon;
219      }
220    
221      /**
222       * Sets the icon to be displayed for non-leaf nodes that are closed.  Set 
223       * this to <code>null</code> if no icon is required.
224       * 
225       * @param icon  the icon (<code>null</code> permitted).
226       * 
227       * @see #getClosedIcon()
228       */
229      public void setClosedIcon(Icon icon)
230      {
231        closedIcon = icon;
232      }
233    
234      /**
235       * Returns the icon displayed for non-leaf nodes that are closed.  The 
236       * default value is initialised from the {@link LookAndFeel}.
237       * 
238       * @return The closed icon (possibly <code>null</code>).
239       * 
240       * @see #setClosedIcon(Icon)
241       */
242      public Icon getClosedIcon()
243      {
244        return closedIcon;
245      }
246    
247      /**
248       * Sets the icon to be displayed for leaf nodes.  Set this to 
249       * <code>null</code> if no icon is required.
250       * 
251       * @param icon  the icon (<code>null</code> permitted).
252       * 
253       * @see #getLeafIcon()
254       */
255      public void setLeafIcon(Icon icon)
256      {
257        leafIcon = icon;
258      }
259    
260      /**
261       * Returns the icon displayed for leaf nodes.  The default value is 
262       * initialised from the {@link LookAndFeel}.
263       * 
264       * @return The leaf icon (possibly <code>null</code>).
265       * 
266       * @see #setLeafIcon(Icon)
267       */
268      public Icon getLeafIcon()
269      {
270        return leafIcon;
271      }
272    
273      /**
274       * Sets the text color for tree cells that are selected.
275       * 
276       * @param c  the color (<code>null</code> permitted).
277       * 
278       * @see #getTextSelectionColor()
279       */
280      public void setTextSelectionColor(Color c)
281      {
282        textSelectionColor = c;
283      }
284    
285      /**
286       * Returns the text color for tree cells that are selected.
287       * The default value is obtained from the {@link LookAndFeel} defaults
288       * table using the key <code>Tree.selectionForeground</code>.
289       * 
290       * @return The text color for tree cells that are selected.
291       * 
292       * @see #setTextSelectionColor(Color)
293       */
294      public Color getTextSelectionColor()
295      {
296        return textSelectionColor;
297      }
298    
299      /**
300       * Sets the text color for tree cells that are not selected.
301       * 
302       * @param c  the color (<code>null</code> permitted).
303       * 
304       * @see #getTextNonSelectionColor()
305       */
306      public void setTextNonSelectionColor(Color c)
307      {
308        textNonSelectionColor = c;
309      }
310    
311      /**
312       * Returns the text color for tree cells that are not selected.
313       * The default value is obtained from the {@link LookAndFeel} defaults
314       * table using the key <code>Tree.selectionForeground</code>.
315       * 
316       * @return The background color for tree cells that are not selected.
317       * 
318       * @see #setTextgroundNonSelectionColor(Color)
319       */
320      public Color getTextNonSelectionColor()
321      {
322        return textNonSelectionColor;
323      }
324    
325      /**
326       * Sets the background color for tree cells that are selected.
327       * 
328       * @param c  the color (<code>null</code> permitted).
329       * 
330       * @see #getBackgroundSelectionColor()
331       */
332      public void setBackgroundSelectionColor(Color c)
333      {
334        backgroundSelectionColor = c;
335      }
336    
337      /**
338       * Returns the background color for tree cells that are selected.
339       * The default value is obtained from the {@link LookAndFeel} defaults
340       * table using the key <code>Tree.selectionBackground</code>.
341       * 
342       * @return The background color for tree cells that are selected.
343       * 
344       * @see #setBackgroundSelectionColor(Color)
345       */
346      public Color getBackgroundSelectionColor()
347      {
348        return backgroundSelectionColor;
349      }
350    
351      /**
352       * Sets the background color for tree cells that are not selected.
353       * 
354       * @param c  the color (<code>null</code> permitted).
355       * 
356       * @see #getBackgroundNonSelectionColor()
357       */
358      public void setBackgroundNonSelectionColor(Color c)
359      {
360        backgroundNonSelectionColor = c;
361      }
362    
363      /**
364       * Returns the background color for tree cells that are not selected.
365       * The default value is obtained from the {@link LookAndFeel} defaults
366       * table using the key <code>Tree.textBackground</code>.
367       * 
368       * @return The background color for tree cells that are not selected.
369       * 
370       * @see #setBackgroundNonSelectionColor(Color)
371       */
372      public Color getBackgroundNonSelectionColor()
373      {
374        return backgroundNonSelectionColor;
375      }
376    
377      /**
378       * Sets the border color for tree cells that are selected.
379       * 
380       * @param c  the color (<code>null</code> permitted).
381       * 
382       * @see #getBorderSelectionColor()
383       */
384      public void setBorderSelectionColor(Color c)
385      {
386        borderSelectionColor = c;
387      }
388    
389      /**
390       * Returns the border color for tree cells that are selected.
391       * The default value is obtained from the {@link LookAndFeel} defaults
392       * table using the key <code>Tree.selectionBorderColor</code>.
393       * 
394       * @return The border color for tree cells that are selected.
395       * 
396       * @see #setBorderSelectionColor(Color)
397       */
398      public Color getBorderSelectionColor()
399      {
400        return borderSelectionColor;
401      }
402    
403      /**
404       * Sets the font.
405       * 
406       * @param f the font.
407       * 
408       * @see #getFont()
409       */
410      public void setFont(Font f)
411      {
412        if (f != null && f instanceof UIResource)
413          f = null;
414        super.setFont(f);
415      }
416    
417      /**
418       * Sets the background color.
419       * 
420       * @param c the color.
421       */
422      public void setBackground(Color c)
423      {
424        if (c != null && c instanceof UIResource)
425          c = null;
426        super.setBackground(c);
427      }
428    
429      /**
430       * Returns a component (in fact <code>this</code>) that can be used to
431       * render a tree cell with the specified state.
432       * 
433       * @param tree  the tree that the cell belongs to.
434       * @param val  the cell value.
435       * @param selected  indicates whether or not the cell is selected.
436       * @param expanded  indicates whether or not the cell is expanded.
437       * @param leaf  indicates whether or not the cell is a leaf in the tree.
438       * @param row  the row index.
439       * @param hasFocus  indicates whether or not the cell has the focus.
440       * 
441       * @return <code>this</code>.
442       */
443      public Component getTreeCellRendererComponent(JTree tree, Object val,
444                                                    boolean selected,
445                                                    boolean expanded, boolean leaf,
446                                                    int row, boolean hasFocus)
447      {
448        if (leaf)
449          setIcon(getLeafIcon());
450        else if (expanded)
451          setIcon(getOpenIcon());
452        else
453          setIcon(getClosedIcon());
454    
455        setText(val.toString());
456        this.selected = selected;
457        this.hasFocus = hasFocus;
458        setHorizontalAlignment(LEFT);
459        setOpaque(false);
460        setVerticalAlignment(CENTER);
461        setEnabled(true);
462        super.setFont(UIManager.getFont("Tree.font"));
463    
464        if (selected)
465          {
466            super.setBackground(getBackgroundSelectionColor());
467            setForeground(getTextSelectionColor());
468            
469            if (hasFocus)
470              setBorderSelectionColor(UIManager.getLookAndFeelDefaults().
471                                      getColor("Tree.selectionBorderColor"));
472            else
473              setBorderSelectionColor(null);
474          }
475        else
476          {
477            super.setBackground(getBackgroundNonSelectionColor());
478            setForeground(getTextNonSelectionColor());
479            setBorderSelectionColor(null);
480          }
481    
482        return this;
483      }
484    
485      /**
486       * Returns the current font.
487       * 
488       * @return The current font.
489       * 
490       * @see #setFont(Font)
491       */
492      public Font getFont()
493      {
494        return super.getFont();
495      }
496    
497      /**
498       * Paints the value. The background is filled based on selected.
499       * 
500       * @param g the graphics device.
501       */
502      public void paint(Graphics g)
503      {
504        // Determine background color.
505        Color bgColor;
506        if (selected)
507          bgColor = getBackgroundSelectionColor();
508        else
509          {
510            bgColor = getBackgroundNonSelectionColor();
511            if (bgColor == null)
512              bgColor = getBackground();
513          }
514        // Paint background.
515        int xOffset = -1;
516        if (bgColor != null)
517          {
518            Icon i = getIcon();
519            xOffset = getXOffset();
520            g.setColor(bgColor);
521            g.fillRect(xOffset, 0, getWidth() - xOffset, getHeight());
522          }
523    
524        if (hasFocus)
525          {
526            if (drawsFocusBorderAroundIcon)
527              xOffset = 0;
528            else if (xOffset == -1)
529              xOffset = getXOffset();
530            paintFocus(g, xOffset, 0, getWidth() - xOffset, getHeight());
531          }
532        super.paint(g);
533      }
534    
535      /**
536       * Paints the focus indicator.
537       */
538      private void paintFocus(Graphics g, int x, int y, int w, int h)
539      {
540        Color col = getBorderSelectionColor();
541        if (col != null)
542          {
543            g.setColor(col);
544            g.drawRect(x, y, w - 1, h - 1);
545          }
546      }
547    
548      /**
549       * Determines the X offset of the label that is caused by
550       * the icon.
551       *
552       * @return the X offset of the label
553       */
554      private int getXOffset()
555      {
556        Icon i = getIcon();
557        int offs = 0;
558        if (i != null && getText() != null)
559          offs = i.getIconWidth() + Math.max(0, getIconTextGap() - 1);
560        return offs;
561      }
562    
563      /**
564       * Returns the preferred size of the cell.
565       * 
566       * @return The preferred size of the cell.
567       */
568      public Dimension getPreferredSize()
569      {
570        Dimension size = super.getPreferredSize();
571        size.width += 3;
572        return size;
573      } 
574    
575      /**
576       * For performance reasons, this method is overridden to do nothing.
577       */
578      public void validate()
579      {
580        // Overridden for performance reasons.
581      } 
582    
583      /**
584       * For performance reasons, this method is overridden to do nothing.
585       */
586      public void revalidate()
587      {
588        // Overridden for performance reasons.
589      } 
590    
591      /**
592       * For performance reasons, this method is overridden to do nothing.
593       * 
594       * @param tm ignored
595       * @param x coordinate of the region to mark as dirty
596       * @param y coordinate of the region to mark as dirty
597       * @param width dimension of the region to mark as dirty
598       * @param height dimension of the region to mark as dirty
599       */
600      public void repaint(long tm, int x, int y, int width, int height)
601      {
602        // Overridden for performance reasons.
603      } 
604    
605      /**
606       * For performance reasons, this method is overridden to do nothing.
607       * 
608       * @param area  the area to repaint.
609       */
610      public void repaint(Rectangle area)
611      {
612        // Overridden for performance reasons.
613      } 
614    
615      /**
616       * For performance reasons, this method is overridden to do nothing.
617       * 
618       * @param name  the property name.
619       * @param oldValue  the old value.
620       * @param newValue  the new value.
621       */
622      protected void firePropertyChange(String name, Object oldValue, 
623                                        Object newValue)
624      {
625        // Overridden for performance reasons.
626      }
627    
628      /**
629       * For performance reasons, this method is overridden to do nothing.
630       * 
631       * @param name  the property name.
632       * @param oldValue  the old value.
633       * @param newValue  the new value.
634       */
635      public void firePropertyChange(String name, byte oldValue, byte newValue)
636      {
637        // Overridden for performance reasons.
638      }
639    
640      /**
641       * For performance reasons, this method is overridden to do nothing.
642       * 
643       * @param name  the property name.
644       * @param oldValue  the old value.
645       * @param newValue  the new value.
646       */
647      public void firePropertyChange(String name, char oldValue, char newValue)
648      {
649        // Overridden for performance reasons.
650      }
651    
652      /**
653       * For performance reasons, this method is overridden to do nothing.
654       * 
655       * @param name  the property name.
656       * @param oldValue  the old value.
657       * @param newValue  the new value.
658       */
659      public void firePropertyChange(String name, short oldValue, short newValue)
660      {
661        // Overridden for performance reasons.
662      } 
663    
664      /**
665       * For performance reasons, this method is overridden to do nothing.
666       * 
667       * @param name  the property name.
668       * @param oldValue  the old value.
669       * @param newValue  the new value.
670       */
671      public void firePropertyChange(String name, int oldValue, int newValue)
672      {
673        // Overridden for performance reasons.
674      }
675    
676      /**
677       * For performance reasons, this method is overridden to do nothing.
678       * 
679       * @param name  the property name.
680       * @param oldValue  the old value.
681       * @param newValue  the new value.
682       */
683      public void firePropertyChange(String name, long oldValue, long newValue)
684      {
685        // Overridden for performance reasons.
686      }
687    
688      /**
689       * For performance reasons, this method is overridden to do nothing.
690       * 
691       * @param name  the property name.
692       * @param oldValue  the old value.
693       * @param newValue  the new value.
694       */
695      public void firePropertyChange(String name, float oldValue, float newValue)
696      {
697        // Overridden for performance reasons.
698      }
699    
700      /**
701       * For performance reasons, this method is overridden to do nothing.
702       * 
703       * @param name  the property name.
704       * @param oldValue  the old value.
705       * @param newValue  the new value.
706       */
707      public void firePropertyChange(String name, double oldValue, double newValue)
708      {
709        //  Overridden for performance reasons.
710      }
711    
712      /**
713       * For performance reasons, this method is overridden to do nothing.
714       * 
715       * @param name  the property name.
716       * @param oldValue  the old value.
717       * @param newValue  the new value.
718       */
719      public void firePropertyChange(String name, boolean oldValue, 
720                                     boolean newValue)
721      {
722        //  Overridden for performance reasons.
723      } 
724    
725    }