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