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 */
021package org.jdesktop.swingx.renderer;
022
023import java.awt.Color;
024import java.awt.Component;
025import java.awt.Graphics;
026
027import javax.swing.Icon;
028import javax.swing.JTree;
029import javax.swing.UIManager;
030import javax.swing.border.Border;
031import javax.swing.border.LineBorder;
032import javax.swing.plaf.basic.BasicGraphicsUtils;
033import javax.swing.tree.TreePath;
034
035import 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 */
050public 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}