001/*
002 * $Id: CellContext.java 3778 2010-09-07 10:10:49Z kleopatra $
003 *
004 * Copyright 2006 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.Font;
025import java.io.Serializable;
026
027import javax.swing.Icon;
028import javax.swing.JComponent;
029import javax.swing.UIManager;
030import javax.swing.border.Border;
031import javax.swing.border.EmptyBorder;
032
033/**
034 * Encapsulates a snapshop of cell content and default display context 
035 * for usage by a <code>ComponentProvider</code>.
036 * <p>
037 * 
038 * One part is the super-set of properties that's traditionally passed into the 
039 * core renderers' (Table-, List-, Tree-) getXXCellRendererComponent. Raw 
040 * properties which define the context are 
041 * 
042 * <ul>
043 * <li> selected
044 * <li> focused
045 * <li> expanded
046 * <li> leaf
047 * </ul>
048 * 
049 * Similarl to a ComponentAdapter, the properties are a super-set of those for 
050 * a concrete component type. It's up to sub-classes (once the generics will be removed, until
051 * then the DefaultXXRenderers - PENDING JW: undecided - even after the generics removal, the
052 * param list in the subclasses are the same) fill any reasonable 
053 * defaults for those not applicable to the specific component context.
054 * 
055 * With those raw properties given, a CellContext looks up and returns dependent visual
056 * properties as appropriate for the concrete component. Typically, they are taken
057 * from the component if supported, or requested from the UIManager.
058 * Dependent properties are
059 * 
060 * <ul>
061 * <li> foreground and background color
062 * <li> border
063 * <li> icon (relevant for trees only)
064 * <li> editable
065 * </ul>
066 *
067 * For a backdoor, the cell location (in horizontal and vertical view coordinates) 
068 * and the originating component is accessible as well. Note that they are not necessarily
069 * valid for the "life" component. It's not recommended to actually use them. If needed,
070 * that's probably a sign the api is lacking :-)
071 * <p>
072 * 
073 * 
074 * <ul>
075 * 
076 * <li>PENDING: still incomplete? how about Font?
077 * <li>PENDING: protected methods? Probably need to open up - derived
078 * properties should be accessible in client code.
079 * </ul>
080 * 
081 * @author Jeanette Winzenburg
082 */
083public class CellContext implements Serializable {
084
085    /** the default border for unfocused cells. */
086    protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
087
088    /** ?? the default border for unfocused cells. ?? */
089    private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1,
090            1);
091
092    /**
093     * Returns the shared border for unfocused cells.
094     * <p>
095     * PENDING: ?? copied from default renderers - why is it done like this?
096     * 
097     * @return the border for unfocused cells.
098     */
099    private static Border getNoFocusBorder() {
100        if (System.getSecurityManager() != null) {
101            return SAFE_NO_FOCUS_BORDER;
102        } else {
103            return noFocusBorder;
104        }
105    }
106
107    /** PENDING JW: maybe make this a WeakReference? Would be a more robust fix for Issue #1040-swingx. */
108    protected transient JComponent component;
109
110    /** PENDING JW: maybe make this a WeakReference? Would be a more robust fix for Issue #1040-swingx. */
111    protected transient Object value;
112
113    protected transient int row;
114
115    protected transient int column;
116
117    protected transient boolean selected;
118
119    protected transient boolean focused;
120
121    protected transient boolean expanded;
122
123    protected transient boolean leaf;
124
125    protected transient boolean dropOn;
126    
127    // --------------------------- install context
128
129
130    /**
131     * Sets the state of the cell's context. Convenience method for subclasses. 
132     * 
133     * @param value the content value of the cell
134     * @param row the cell's row index in view coordinates
135     * @param column the cell's column index in view coordinates
136     * @param selected the cell's selected state
137     * @param focused the cell's focused state
138     * @param expanded the cell's expanded state
139     * @param leaf the cell's leaf state
140     */
141    protected void installState(Object value, int row, int column,
142            boolean selected, boolean focused, boolean expanded, boolean leaf) {
143        this.value = value;
144        this.row = row;
145        this.column = column;
146        this.selected = selected;
147        this.focused = focused;
148        this.expanded = expanded;
149        this.leaf = leaf;
150    }
151
152    /**
153     * Replaces the value of this cell context with the given parameter and returns 
154     * the replaced value.
155     * 
156     * @param value the new value of the cell context
157     * @return the replaced value of the cell context
158     */
159    public Object replaceValue(Object value) {
160        Object old = getValue();
161        this.value = value;
162        return old;
163    }
164    
165    // -------------------- accessors of installed state
166
167    /**
168     * Returns the component the cell resides on, may be null. Subclasses are
169     * expected to override and return the component type they are handling.
170     * 
171     * @return the component the cell resides on, may be null.
172     */
173    public JComponent getComponent() {
174        return component;
175    }
176
177    /**
178     * Returns the value of the cell as set in the install.
179     * 
180     * @return the content value of the cell.
181     */
182    public Object getValue() {
183        return value;
184    }
185
186    /**
187     * Returns the cell's row index in view coordinates as set in the install.
188     * 
189     * @return the cell's row index.
190     */
191    public int getRow() {
192        return row;
193    }
194
195    /**
196     * Returns the cell's column index in view coordinates as set in the
197     * install.
198     * 
199     * @return the cell's column index.
200     */
201    public int getColumn() {
202        return column;
203    }
204
205    /**
206     * Returns the selected state as set in the install.
207     * 
208     * @return the cell's selected state.
209     */
210    public boolean isSelected() {
211        return selected;
212    }
213
214    /**
215     * Returns the focused state as set in the install.
216     * 
217     * @return the cell's focused state.
218     */
219    public boolean isFocused() {
220        return focused;
221    }
222
223    /**
224     * Returns the expanded state as set in the install.
225     * 
226     * @return the cell's expanded state.
227     */
228    public boolean isExpanded() {
229        return expanded;
230    }
231
232    /**
233     * Returns the leaf state as set in the install.
234     * 
235     * @return the cell's leaf state.
236     */
237    public boolean isLeaf() {
238        return leaf;
239    }
240
241    // -------------------- accessors for derived state
242    /**
243     * Returns the cell's editability. Subclasses should override to return a
244     * reasonable cell-related state.
245     * <p>
246     * 
247     * Here: false.
248     * 
249     * @return the cell's editable property.
250     */
251    public boolean isEditable() {
252        return false;
253    }
254
255    /**
256     * Returns the icon. Subclasses should override to return a reasonable
257     * cell-related state.
258     * <p>
259     * 
260     * Here: <code>null</code>.
261     * 
262     * @return the cell's icon.
263     */
264    public Icon getIcon() {
265        return null;
266    }
267
268    /**
269     * Returns a boolean indicating if the cell is a drop location with any of the dropOn
270     * modes. It's up to subclasses to implement.
271     * <p>
272     * 
273     * Here: false.
274     * 
275     * @return true if the current cell is a drop location with any of the dropOn modes,
276     *    false otherwise
277     */
278    protected boolean isDropOn() {
279        return dropOn;
280    }
281    
282    /**
283     * Returns the foreground color of the renderered component or null if the
284     * component is null
285     * <p>
286     * 
287     * PENDING: fallback to UI properties if comp == null?
288     * 
289     * @return the foreground color of the rendered component.
290     */
291    protected Color getForeground() {
292        if (isDropOn()) {
293            return getSelectionForeground();
294        }
295        return getComponent() != null ? getComponent().getForeground() : null;
296    }
297
298    /**
299     * Returns the background color of the renderered component or null if the
300     * component is null
301     * <p>
302     * 
303     * PENDING: fallback to UI properties if comp == null?
304     * 
305     * @return the background color of the rendered component.
306     */
307    protected Color getBackground() {
308        if (isDropOn()) {
309            return getSelectionBackground();
310        }
311        return getComponent() != null ? getComponent().getBackground() : null;
312    }
313
314    /**
315     * Returns the default selection background color of the renderered
316     * component. Typically, the color is LF specific. It's up to subclasses to
317     * look it up. Here: returns null.
318     * <p>
319     * 
320     * PENDING: return UI properties here?
321     * 
322     * @return the selection background color of the rendered component.
323     */
324    protected Color getSelectionBackground() {
325        return null;
326    }
327
328    /**
329     * Returns the default selection foreground color of the renderered
330     * component. Typically, the color is LF specific. It's up to subclasses to
331     * look it up. Here: returns null.
332     * <p>
333     * 
334     * PENDING: return UI properties here?
335     * 
336     * @return the selection foreground color of the rendered component.
337     */
338    protected Color getSelectionForeground() {
339        return null;
340    }
341
342    /**
343     * Returns the default focus border of the renderered component. Typically,
344     * the border is LF specific.
345     * 
346     * @return the focus border of the rendered component.
347     */
348    protected Border getFocusBorder() {
349        Border border = null;
350        if (isSelected()) {
351            border = UIManager
352                    .getBorder(getUIKey("focusSelectedCellHighlightBorder"));
353        }
354        if (border == null) {
355            border = UIManager.getBorder(getUIKey("focusCellHighlightBorder"));
356        }
357        return border;
358    }
359
360    /**
361     * Returns the default border of the renderered component depending on cell
362     * state. Typically, the border is LF specific.
363     * <p>
364     * 
365     * Here: returns the focus border if the cell is focused, the context
366     * defined no focus border otherwise.
367     * 
368     * @return the default border of the rendered component.
369     */
370    protected Border getBorder() {
371        if (isFocused()) {
372            return getFocusBorder();
373        }
374        Border border = UIManager.getBorder(getUIKey("cellNoFocusBorder"));
375        return border != null ? border : getNoFocusBorder();
376    }
377
378    /**
379     * Returns the default focused foreground color of the renderered component.
380     * Typically, the color is LF specific.
381     * 
382     * @return the focused foreground color of the rendered component.
383     */
384    protected Color getFocusForeground() {
385        return UIManager.getColor(getUIKey("focusCellForeground"));
386    }
387
388    /**
389     * Returns the default focused background color of the renderered component.
390     * Typically, the color is LF specific.
391     * 
392     * @return the focused background color of the rendered component.
393     */
394    protected Color getFocusBackground() {
395        return UIManager.getColor(getUIKey("focusCellBackground"));
396    }
397
398    protected Color getDropCellForeground() {
399        return UIManager.getColor(getUIKey("dropCellForeground"));
400    }
401    
402    protected Color getDropCellBackground() {
403        return UIManager.getColor(getUIKey("dropCellBackground"));
404    }
405    // ----------------------- convenience
406
407    /**
408     * Convenience method to build a component type specific lookup key for the
409     * UIManager.
410     * 
411     * @param key the general part of the key
412     * @return a composed key build of a component type prefix and the input.
413     */
414    protected String getUIKey(String key) {
415        return getUIPrefix() + key;
416    }
417
418    /**
419     * Returns the component type specific prefix of keys for lookup in the
420     * UIManager. Subclasses must override, here: returns the empty String.
421     * 
422     * @return the component type specific prefix.
423     */
424    protected String getUIPrefix() {
425        return "";
426    }
427
428    /**
429     * Returns the Font of the target component or null if no component installed.
430     * @return
431     */
432    protected Font getFont() {
433        return getComponent() != null ? getComponent().getFont() : null;
434    }
435
436    public String getCellRendererName() {
437        return getUIPrefix() + "cellRenderer";
438    }
439
440}