001/*
002 * $Id$
003 *
004 * Copyright 2009 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 */
022package org.jdesktop.swingx.plaf.synth;
023
024import java.awt.Color;
025import java.awt.Graphics;
026import java.beans.PropertyChangeEvent;
027import java.beans.PropertyChangeListener;
028
029import javax.swing.JComponent;
030import javax.swing.plaf.ComponentUI;
031import javax.swing.plaf.UIResource;
032import javax.swing.plaf.synth.ColorType;
033import javax.swing.plaf.synth.Region;
034import javax.swing.plaf.synth.SynthConstants;
035import javax.swing.plaf.synth.SynthContext;
036import javax.swing.plaf.synth.SynthLookAndFeel;
037import javax.swing.plaf.synth.SynthStyle;
038
039import org.jdesktop.swingx.SwingXUtilities;
040import org.jdesktop.swingx.plaf.basic.core.BasicXListUI;
041
042/**
043 * TODO add type doc
044 * 
045 * @author Jeanette Winzenburg
046 */
047public class SynthXListUI extends BasicXListUI 
048    // PENDING JW: SynthUI is sun package (here: used by c&p'ed SynthBorder) - replace?
049    // maybe not: SynthLookUp looks up styles from delegates of type SynthUI only
050    implements SynthConstants,  SynthUI  /*, PropertyChangeListener */{
051
052    private SynthStyle style;
053    @SuppressWarnings("unused")
054    private boolean useListColors;
055    @SuppressWarnings("unused")
056    private boolean useUIBorder;
057
058    /**
059     * Returns a new instance of SynthXListUI.  SynthXListUI delegates are
060     * allocated one per JList.
061     *
062     * @return A new ListUI implementation for the Synth look and feel.
063     */
064    public static ComponentUI createUI(JComponent list) {
065        return new SynthXListUI();
066    }
067
068    /**
069     * {@inheritDoc} <p>
070     * Overridden to fill background, Synth-style.
071     */
072    @Override
073    public void update(Graphics g, JComponent c) {
074        SynthContext context = getContext(c);
075        SynthUtils.update(context, g);
076        paintBorder(context, g, 0, 0, c.getWidth(), c.getHeight());
077        paint(g, c);
078    }
079
080
081    /**
082     * {@inheritDoc} <p>
083     * Overridden to update style if appropriate.
084     */
085    @Override
086    protected PropertyChangeListener createPropertyChangeListener() {
087        PropertyChangeListener l = new PropertyChangeHandler() {
088
089            @Override
090            public void propertyChange(PropertyChangeEvent e) {
091                if (SynthUtils.shouldUpdateStyle(e)) {
092                    updateStyle();
093                }
094                super.propertyChange(e);
095            }
096            
097        };
098        return l;
099    }
100
101    /**
102     * {@inheritDoc} <p>
103     * Overridden to install properties, Synth-style.
104     */
105    @Override
106    protected void installDefaults() {
107        // never happens - the delegate renderer is always not-null and
108        // not a ui-resource
109//        if (list.getCellRenderer() == null ||
110//                 (list.getCellRenderer() instanceof UIResource)) {
111//            list.setCellRenderer(new SynthListCellRenderer());
112//        }
113        updateStyle();
114    }
115
116    private void updateStyle() {
117        // compare local reference to style from factory
118        // nothing to do if same
119        if (style == getStyle()) return;
120        // check if this is called from init or from later update
121        // if from later updates, need to cleanup old
122        boolean refresh = style != null;
123        if (refresh) {
124            style.uninstallDefaults(getContext(ENABLED));
125        }
126        // update local reference
127        style = getStyle();
128        // special case border
129        installSynthBorder();
130        // install defaults 
131        style.installDefaults(getContext(ENABLED));
132        // install selected properties
133        SynthContext selectedContext = getContext(SELECTED);
134        Color sbg = list.getSelectionBackground();
135        if (sbg == null || sbg instanceof UIResource) {
136            list.setSelectionBackground(style.getColor(
137                    selectedContext, ColorType.TEXT_BACKGROUND));
138        }
139        
140        Color sfg = list.getSelectionForeground();
141        if (sfg == null || sfg instanceof UIResource) {
142            list.setSelectionForeground(style.getColor(
143                    selectedContext, ColorType.TEXT_FOREGROUND));
144        }
145        // install cell height
146        int height = style.getInt(selectedContext, "List.cellHeight", -1);
147        if (height != -1) {
148            list.setFixedCellHeight(height);
149        }
150        // we do this because ... ??
151        if (refresh) {
152            uninstallKeyboardActions();
153            installKeyboardActions();
154        }
155        // install currently unused properties of this delegate
156        useListColors = style.getBoolean(selectedContext,
157                "List.rendererUseListColors", true);
158        useUIBorder = style.getBoolean(selectedContext,
159                "List.rendererUseUIBorder", true);
160        
161    }
162
163    /**
164     * Installs a SynthBorder from the current style, if ui-installable.
165     * 
166     * @param context the context 
167     */
168    protected void installSynthBorder() {
169        if (SwingXUtilities.isUIInstallable(list.getBorder())) {
170            list.setBorder(new SynthBorder(this, style.getInsets(getContext(ENABLED), null)));
171        }
172    }
173
174    /**
175     * {@inheritDoc} <p>
176     * Overridden to uninstall properties, Synth-style, after calling super.
177     */
178    @Override
179    protected void uninstallDefaults() {
180        super.uninstallDefaults();
181        style.uninstallDefaults(getContext(ENABLED));
182        style = null;
183    }
184
185    
186    /**
187     * Paints border with the context's style's painter.
188     * Implemented for SynthUI interface.
189     */
190    @Override
191    public void paintBorder(SynthContext context, Graphics g, int x, int y,
192            int w, int h) {
193        SynthUtils.getPainter(context).paintListBorder(context, g, x, y, w, h);
194    }
195
196    /**
197     * {@inheritDoc} <p>
198     * 
199     * Returns a context for the component's current state.
200     * Implemented for SynthUI interface. <p>
201     * 
202     * PENDING JW: not entirely sure if allowed ... but need to replace SynthUI anyway?.
203     * 
204     * @throws IllegalArgumentException if the component is not controlled by this 
205     *    delegate
206     */
207    @Override
208    public SynthContext getContext(JComponent c) {
209        if (c != list) throw new IllegalArgumentException("must be ui-delegate for component");
210        return getContext();
211    }
212
213    /**
214     * Returns the context based on current state.
215     * @return
216     */
217    private SynthContext getContext() {
218        return getContext(getComponentState());
219    }
220    
221    /**
222     * Returns the current component state for the controlled list.
223     * @return
224     */
225    private int getComponentState() {
226        return SynthUtils.getComponentState(list);
227    }
228
229    /**
230     * Returns a Context with the given component state.
231     * 
232     * @param state
233     * @return
234     */
235    private SynthContext getContext(int state) {
236        return SynthUtils.getContext(list, getRegion(), style, state);
237    }
238    
239    private Region getRegion() {
240        return XRegion.getXRegion(list, true);
241    }
242
243
244    /**
245     * Returns the style for this component from the style factory.
246     * @return
247     */
248    private SynthStyle getStyle() {
249        return SynthLookAndFeel.getStyleFactory().getStyle(list, getRegion());
250    }
251
252
253//    private class SynthListCellRenderer extends DefaultListCellRenderer.UIResource {
254//        public String getName() {
255//            return "List.cellRenderer";
256//        }
257//
258//        public void setBorder(Border b) {
259//            if (useUIBorder || b instanceof SynthBorder) {
260//                super.setBorder(b);
261//            }
262//        }
263//
264//        public Component getListCellRendererComponent(JList list, Object value,
265//                  int index, boolean isSelected, boolean cellHasFocus) {
266//            if (!useListColors && (isSelected || cellHasFocus)) {
267//                SynthLookAndFeel.setSelectedUI((SynthLabelUI)SynthLookAndFeel.
268//                             getUIOfType(getUI(), SynthLabelUI.class),
269//                                   isSelected, cellHasFocus, list.isEnabled(), false);
270//            }
271//            else {
272//                SynthLookAndFeel.resetSelectedUI();
273//            }
274//            
275//            super.getListCellRendererComponent(list, value, index,
276//                                               isSelected, cellHasFocus);
277//            return this;
278//        }
279//
280//        public void paint(Graphics g) {
281//            super.paint(g);
282//            SynthLookAndFeel.resetSelectedUI();
283//        }
284//    }
285
286
287}