001    /* MetalTreeUI.java
002       Copyright (C) 2005 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.plaf.metal;
040    
041    import java.awt.Graphics;
042    import java.awt.Insets;
043    import java.awt.Rectangle;
044    import java.beans.PropertyChangeEvent;
045    import java.beans.PropertyChangeListener;
046    
047    import javax.swing.JComponent;
048    import javax.swing.JTree;
049    import javax.swing.UIManager;
050    import javax.swing.tree.TreePath;
051    import javax.swing.plaf.ComponentUI;
052    import javax.swing.plaf.basic.BasicTreeUI;
053    
054    /**
055     * A UI delegate for the {@link JTree} component.
056     */
057    public class MetalTreeUI extends BasicTreeUI
058    {
059      /**
060       * Listens for property changes of the line style and updates the
061       * internal setting.
062       */
063      private class LineStyleListener
064        implements PropertyChangeListener
065      {
066    
067        public void propertyChange(PropertyChangeEvent e)
068        {
069          if (e.getPropertyName().equals(LINE_STYLE_PROPERTY))
070            decodeLineStyle(e.getNewValue());
071        }
072          
073      }
074    
075      /**
076       * The key to the lineStyle client property.
077       */
078      private static final String LINE_STYLE_PROPERTY = "JTree.lineStyle";
079    
080      /**
081       * The property value indicating no line style.
082       */
083      private static final String LINE_STYLE_VALUE_NONE = "None";
084    
085      /**
086       * The property value indicating angled line style.
087       */
088      private static final String LINE_STYLE_VALUE_ANGLED = "Angled";
089    
090      /**
091       * The property value indicating horizontal line style.
092       */
093      private static final String LINE_STYLE_VALUE_HORIZONTAL = "Horizontal";
094    
095      /**
096       * The line style for None.
097       */
098      private static final int LINE_STYLE_NONE = 0;
099    
100      /**
101       * The line style for Angled.
102       */
103      private static final int LINE_STYLE_ANGLED = 1;
104    
105      /**
106       * The line style for Horizontal.
107       */
108      private static final int LINE_STYLE_HORIZONTAL = 2;
109    
110      /**
111       * The current line style.
112       */
113      private int lineStyle;
114    
115      /**
116       * Listens for changes on the line style property and updates the
117       * internal settings.
118       */
119      private PropertyChangeListener lineStyleListener;
120    
121      /**
122       * Constructs a new instance of <code>MetalTreeUI</code>.
123       */
124      public MetalTreeUI()
125      {
126        super();
127      }
128    
129      /**
130       * Returns a new instance of <code>MetalTreeUI</code>.
131       *
132       * @param component the component for which we return an UI instance
133       *
134       * @return A new instance of <code>MetalTreeUI</code>.
135       */
136      public static ComponentUI createUI(JComponent component)
137      {
138        return new MetalTreeUI();
139      }
140      
141      /**
142       * The horizontal element of legs between nodes starts at the right of the
143       * left-hand side of the child node by default. This method makes the
144       * leg end before that.
145       */
146      protected int getHorizontalLegBuffer()
147      {
148        return super.getHorizontalLegBuffer();
149      }
150    
151      /**
152       * Configures the specified component appropriate for the look and feel.
153       * This method is invoked when the ComponentUI instance is being installed 
154       * as the UI delegate on the specified component. This method should completely 
155       * configure the component for the look and feel, including the following:
156       * 1. Install any default property values for color, fonts, borders, icons, 
157       *    opacity, etc. on the component. Whenever possible, property values
158       *    initialized by the client program should not be overridden.
159       * 2. Install a LayoutManager on the component if necessary.
160       * 3. Create/add any required sub-components to the component.
161       * 4. Create/install event listeners on the component.
162       * 5. Create/install a PropertyChangeListener on the component in order 
163       *    to detect and respond to component property changes appropriately.
164       * 6. Install keyboard UI (mnemonics, traversal, etc.) on the component.
165       * 7. Initialize any appropriate instance data. 
166       */
167      public void installUI(JComponent c)
168      {
169        super.installUI(c);
170    
171        Object lineStyleProp = c.getClientProperty(LINE_STYLE_PROPERTY);
172        decodeLineStyle(lineStyleProp);
173        if (lineStyleListener == null)
174          lineStyleListener = new LineStyleListener();
175        c.addPropertyChangeListener(lineStyleListener);
176      }
177      
178      /**
179       * Reverses configuration which was done on the specified component during 
180       * installUI. This method is invoked when this UIComponent instance is being 
181       * removed as the UI delegate for the specified component. This method should 
182       * undo the configuration performed in installUI, being careful to leave the 
183       * JComponent instance in a clean state (no extraneous listeners, 
184       * look-and-feel-specific property objects, etc.). This should include 
185       * the following:
186       * 1. Remove any UI-set borders from the component.
187       * 2. Remove any UI-set layout managers on the component.
188       * 3. Remove any UI-added sub-components from the component.
189       * 4. Remove any UI-added event/property listeners from the component.
190       * 5. Remove any UI-installed keyboard UI from the component.
191       * 6. Nullify any allocated instance data objects to allow for GC. 
192       */
193      public void uninstallUI(JComponent c)
194      {
195        super.uninstallUI(c);
196        if (lineStyleListener != null)
197          c.removePropertyChangeListener(lineStyleListener);
198        lineStyleListener = null;
199      }
200      
201      /**
202       * This function converts between the string passed into the client
203       * property and the internal representation (currently an int).
204       * 
205       * @param lineStyleFlag - String representation
206       */     
207      protected void decodeLineStyle(Object lineStyleFlag)
208      {
209        if (lineStyleFlag == null || lineStyleFlag.equals(LINE_STYLE_VALUE_ANGLED))
210          lineStyle = LINE_STYLE_ANGLED;
211        else if (lineStyleFlag.equals(LINE_STYLE_VALUE_HORIZONTAL))
212          lineStyle = LINE_STYLE_HORIZONTAL;
213        else if (lineStyleFlag.equals(LINE_STYLE_VALUE_NONE))
214          lineStyle = LINE_STYLE_NONE;
215        else
216          lineStyle = LINE_STYLE_ANGLED;
217      }
218    
219      /**
220       * Checks if the location is in expand control.
221       * 
222       * @param row - current row
223       * @param rowLevel - current level
224       * @param mouseX - current x location of the mouse click
225       * @param mouseY - current y location of the mouse click
226       */
227      protected boolean isLocationInExpandControl(int row, int rowLevel,
228                                              int mouseX, int mouseY)
229      {
230        return super.isLocationInExpandControl(tree.getPathForRow(row), 
231                                               mouseX, mouseY);
232      }
233      
234      /**
235       * Paints the specified component appropriate for the look and feel. 
236       * This method is invoked from the ComponentUI.update method when the 
237       * specified component is being painted. Subclasses should override this 
238       * method and use the specified Graphics object to render the content of 
239       * the component.
240       * 
241       * @param g - the current graphics configuration.
242       * @param c - the current component to draw
243       */
244      public void paint(Graphics g, JComponent c)
245      {
246        // Calls BasicTreeUI's paint since it takes care of painting all
247        // types of icons. 
248        super.paint(g, c);
249    
250        if (lineStyle == LINE_STYLE_HORIZONTAL)
251          paintHorizontalSeparators(g, c);
252      }
253      
254      /**
255       * Paints the horizontal separators.
256       * 
257       * @param g - the current graphics configuration.
258       * @param c - the current component to draw
259       */
260      protected void paintHorizontalSeparators(Graphics g, JComponent c)
261      {
262        g.setColor(UIManager.getColor("Tree.line"));
263        Rectangle clip = g.getClipBounds();
264        int row0 = getRowForPath(tree, getClosestPathForLocation(tree, 0, clip.y));
265        int row1 =
266          getRowForPath(tree, getClosestPathForLocation(tree, 0,
267                                                        clip.y + clip.height - 1));
268        if (row0 >= 0 && row1 >= 0)
269          {
270            for (int i = row0; i <= row1; i++)
271              {
272                TreePath p = getPathForRow(tree, i);
273                if (p != null && p.getPathCount() == 2)
274                  {
275                    Rectangle r = getPathBounds(tree, getPathForRow(tree, i));
276                    if (r != null)
277                      {
278                        g.drawLine(clip.x, r.y, clip.x + clip.width, r.y);
279                      }
280                  }
281              }
282          }
283      }
284    
285      
286      /**
287       * Paints the vertical part of the leg. The receiver should NOT modify 
288       * clipBounds, insets.
289       * 
290       * @param g - the current graphics configuration.
291       * @param clipBounds -
292       * @param insets - 
293       * @param path - the current path
294       */
295      protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
296                                        Insets insets, TreePath path)
297      {
298        if (lineStyle == LINE_STYLE_ANGLED)
299          super.paintVerticalPartOfLeg(g, clipBounds, insets, path);
300      }
301    
302      /**
303       * Paints the horizontal part of the leg. The receiver should NOT \
304       * modify clipBounds, or insets.
305       * NOTE: parentRow can be -1 if the root is not visible.
306       */
307      protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
308                                            Insets insets, Rectangle bounds,
309                                            TreePath path, int row,
310                                            boolean isExpanded, boolean hasBeenExpanded,
311                                            boolean isLeaf)
312      {
313        if (lineStyle == LINE_STYLE_ANGLED)
314          super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row, 
315                                         isExpanded, hasBeenExpanded, isLeaf);
316      }
317    }