001    /*
002     * $Id: TreeCellContext.java 3424 2009-07-30 10:53:39Z kleopatra $
003     *
004     * Copyright 2008 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     *
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    package org.jdesktop.swingx.renderer;
022    
023    import java.awt.Color;
024    import java.awt.Component;
025    import java.awt.Graphics;
026    
027    import javax.swing.Icon;
028    import javax.swing.JTree;
029    import javax.swing.UIManager;
030    import javax.swing.border.Border;
031    import javax.swing.border.LineBorder;
032    import javax.swing.plaf.basic.BasicGraphicsUtils;
033    import javax.swing.tree.TreePath;
034    
035    import org.jdesktop.swingx.JXTree;
036    
037    /**
038     * Tree specific <code>CellContext</code>. 
039     * 
040     * <ul>
041     * <li>PENDING: use focus border as returned from list or table instead of
042     * rolling its own? The missing ui-border probably is a consequence of the
043     * border hacking as implemented in core default renderer. SwingX has a
044     * composite default which should use the "normal" border.
045     * <li> PENDING: selection colors couple explicitly to SwingX - should we go JXTree as
046     *   generic type?
047     * <li> PENDING: for a JXTree use the icons as returned by the xtree api?
048     * </ul>
049     */
050    public class TreeCellContext extends CellContext {
051        /** the icon to use for a leaf node. */
052        protected Icon leafIcon;
053    
054        /** the default icon to use for a closed folder. */
055        protected Icon closedIcon;
056    
057        /** the default icon to use for a open folder. */
058        protected Icon openIcon;
059    
060        /** the border around a focused node. */
061        private Border treeFocusBorder;
062    
063        /**
064         * Sets state of the cell's context. Note that the component might be null
065         * to indicate a cell without a concrete context. All accessors must cope
066         * with.
067         * 
068         * @param component the component the cell resides on, might be null
069         * @param value the content value of the cell
070         * @param row the cell's row index in view coordinates
071         * @param column the cell's column index in view coordinates
072         * @param selected the cell's selected state
073         * @param focused the cell's focused state
074         * @param expanded the cell's expanded state
075         * @param leaf the cell's leaf state
076         */
077        public void installContext(JTree component, Object value, int row, int column,
078                boolean selected, boolean focused, boolean expanded, boolean leaf) {
079            this.component = component;
080            installState(value, row, column, selected, focused, expanded, leaf);
081            this.dropOn = checkDropOnState();
082        }
083    
084        private boolean checkDropOnState() {
085            if ((getComponent() == null)) {
086                return false;
087            }
088            JTree.DropLocation dropLocation = getComponent().getDropLocation();
089            if (dropLocation != null
090                    && dropLocation.getChildIndex() == -1
091                    && getComponent().getRowForPath(dropLocation.getPath()) == row) {
092                return true;
093            }
094            return false;
095        }
096       
097        @Override
098        public JTree getComponent() {
099            return (JTree) super.getComponent();
100        }
101        
102    //------------------- accessors for derived state
103        
104        /**
105         * Returns the treePath for the row or null if invalid.
106         * 
107         */
108        public TreePath getTreePath() {
109            if (getComponent() == null) return null;
110            if ((row < 0) || (row >= getComponent().getRowCount())) return null;
111            return getComponent().getPathForRow(row);
112        }
113        /**
114         * {@inheritDoc}
115         * <p>
116         * PENDING: implement to return the tree cell editability!
117         */
118        @Override
119        public boolean isEditable() {
120            return false;
121            // return getComponent() != null ? getComponent().isCellEditable(
122            // getRow(), getColumn()) : false;
123        }
124    
125        /**
126         * {@inheritDoc}
127         */
128        @Override
129        protected Color getSelectionBackground() {
130            Color selection = null;
131            if (isDropOn()) {
132                selection = getDropCellBackground();
133                if (selection != null) return selection;
134            }
135            if (getComponent() instanceof JXTree) {
136                return ((JXTree) getComponent()).getSelectionBackground();
137            }
138            return UIManager.getColor("Tree.selectionBackground");
139        }
140    
141        /**
142         * {@inheritDoc}
143         */
144        @Override
145        protected Color getSelectionForeground() {
146            Color selection = null;
147            if (isDropOn()) {
148                selection = getDropCellForeground();
149                if (selection != null) return selection;
150            }
151            if (getComponent() instanceof JXTree) {
152                return ((JXTree) getComponent()).getSelectionForeground();
153            }
154            return UIManager.getColor("Tree.selectionForeground");
155        }
156    
157        /**
158         * {@inheritDoc}
159         */
160        @Override
161        protected String getUIPrefix() {
162            return "Tree.";
163        }
164    
165        /**
166         * Returns the default icon to use for leaf cell.
167         * 
168         * @return the icon to use for leaf cell.
169         */
170        protected Icon getLeafIcon() {
171            return leafIcon != null ? leafIcon : UIManager
172                    .getIcon(getUIKey("leafIcon"));
173        }
174    
175        /**
176         * Returns the default icon to use for open cell.
177         * 
178         * @return the icon to use for open cell.
179         */
180        protected Icon getOpenIcon() {
181            return openIcon != null ? openIcon : UIManager
182                    .getIcon(getUIKey("openIcon"));
183        }
184    
185        /**
186         * Returns the default icon to use for closed cell.
187         * 
188         * @return the icon to use for closed cell.
189         */
190        protected Icon getClosedIcon() {
191            return closedIcon != null ? closedIcon : UIManager
192                    .getIcon(getUIKey("closedIcon"));
193        }
194    
195        /**
196         * {@inheritDoc}
197         * <p>
198         * 
199         * Overridden to return a default depending for the leaf/open cell state.
200         */
201        @Override
202        public Icon getIcon() {
203            if (isLeaf()) {
204                return getLeafIcon();
205            }
206            if (isExpanded()) {
207                return getOpenIcon();
208            }
209            return getClosedIcon();
210        }
211    
212        @Override
213        protected Border getFocusBorder() {
214            if (treeFocusBorder == null) {
215                treeFocusBorder = new TreeFocusBorder();
216            }
217            return treeFocusBorder;
218        }
219    
220        /**
221         * Border used to draw around the content of the node. <p>
222         * PENDING: isn't that the same as around a list or table cell, but
223         * without a tree-specific key/value pair in UIManager?
224         */
225        public class TreeFocusBorder extends LineBorder {
226    
227            private Color treeBackground;
228    
229            private Color focusColor;
230    
231            public TreeFocusBorder() {
232                super(Color.BLACK);
233                treeBackground = getBackground();
234                if (treeBackground != null) {
235                    focusColor = new Color(~treeBackground.getRGB());
236                }
237            }
238    
239            @Override
240            public void paintBorder(Component c, Graphics g, int x, int y,
241                    int width, int height) {
242                Color color = UIManager.getColor("Tree.selectionBorderColor");
243                if (color != null) {
244                    lineColor = color;
245                }
246                if (isDashed()) {
247                    if (treeBackground != c.getBackground()) {
248                        treeBackground = c.getBackground();
249                        focusColor = new Color(~treeBackground.getRGB());
250                    }
251    
252                    Color old = g.getColor();
253                    g.setColor(focusColor);
254                    BasicGraphicsUtils.drawDashedRect(g, x, y, width, height);
255                    g.setColor(old);
256    
257                } else {
258                    super.paintBorder(c, g, x, y, width, height);
259                }
260    
261            }
262    
263            /**
264             * @return a boolean indicating whether the focus border
265             *   should be painted dashed style.
266             */
267            private boolean isDashed() {
268                return Boolean.TRUE.equals(UIManager
269                        .get("Tree.drawDashedFocusIndicator"));
270    
271            }
272    
273            /**
274             * {@inheritDoc}
275             */
276            @Override
277            public boolean isBorderOpaque() {
278                return false;
279            }
280    
281        }
282    
283    }