001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.gui.component.renderer;
020
021import java.awt.Color;
022import java.awt.Component;
023import java.awt.Insets;
024import java.util.Map;
025
026import javax.swing.Icon;
027import javax.swing.JComboBox;
028import javax.swing.JComponent;
029import javax.swing.JLabel;
030import javax.swing.JList;
031import javax.swing.border.EmptyBorder;
032import javax.swing.plaf.ColorUIResource;
033import javax.swing.plaf.ComboBoxUI;
034import javax.swing.plaf.ListUI;
035
036import org.pushingpixels.substance.api.ColorSchemeAssociationKind;
037import org.pushingpixels.substance.api.ComponentState;
038import org.pushingpixels.substance.api.ComponentStateFacet;
039import org.pushingpixels.substance.api.SubstanceColorScheme;
040import org.pushingpixels.substance.api.SubstanceLookAndFeel;
041import org.pushingpixels.substance.api.renderers.SubstanceDefaultComboBoxRenderer;
042import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
043import org.pushingpixels.substance.internal.animation.StateTransitionTracker.ModelStateInfo;
044import org.pushingpixels.substance.internal.animation.StateTransitionTracker.StateContributionInfo;
045import org.pushingpixels.substance.internal.ui.SubstanceComboBoxUI;
046import org.pushingpixels.substance.internal.ui.SubstanceListUI;
047import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities;
048import org.pushingpixels.substance.internal.utils.SubstanceSizeUtils;
049import org.pushingpixels.substance.internal.utils.SubstanceStripingUtils;
050import org.pushingpixels.substance.internal.utils.SubstanceTextUtilities;
051import org.pushingpixels.substance.internal.utils.UpdateOptimizationInfo;
052
053/**
054 * CustomComboBox renderer, based on Substance look and feel code.<br>
055 * Override the getListCellRendererComponent() or updateItem() methods to do your own rendering.
056 * 
057 * @author Stephane
058 */
059public class CustomComboBoxRenderer extends SubstanceDefaultComboBoxRenderer
060{
061    /**
062     * 
063     */
064    private static final long serialVersionUID = -3392789033779535472L;
065
066    private final JComboBox combo;
067
068    public CustomComboBoxRenderer(JComboBox combo)
069    {
070        super(combo);
071
072        this.combo = combo;
073    }
074
075    public JComboBox getComboBox()
076    {
077        return combo;
078    }
079
080    @Override
081    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
082            boolean cellHasFocus)
083    {
084        JComponent result = (JComponent) getRendererComponent(list, value, index, isSelected);
085
086        ListUI baseListUI = list.getUI();
087        ComboBoxUI baseComboUI = combo.getUI();
088
089        if ((baseListUI instanceof SubstanceListUI) && (baseComboUI instanceof SubstanceComboBoxUI))
090        {
091            SubstanceListUI listUI = (SubstanceListUI) baseListUI;
092            SubstanceComboBoxUI comboUI = (SubstanceComboBoxUI) baseComboUI;
093
094            // special case for the combobox. The selected value is
095            // painted using the renderer of the list, and the index
096            // is -1.
097            if (index == -1)
098            {
099                StateTransitionTracker stateTransitionTracker = comboUI.getTransitionTracker();
100                ModelStateInfo modelStateInfo = stateTransitionTracker.getModelStateInfo();
101                ComponentState currState = modelStateInfo.getCurrModelState();
102                float comboAlpha = SubstanceColorSchemeUtilities.getAlpha(combo, currState);
103                Color fg = SubstanceTextUtilities.getForegroundColor(combo, ((JLabel) result).getText(),
104                        modelStateInfo, comboAlpha);
105                result.setForeground(fg);
106            }
107            else
108            {
109                // use highlight color scheme for selected and rollover
110                // elements in the drop down list
111                StateTransitionTracker.ModelStateInfo modelStateInfo = listUI.getModelStateInfo(index, result);
112                ComponentState currState = listUI.getCellState(index, result);
113
114                if (modelStateInfo == null)
115                {
116                    SubstanceColorScheme scheme = getColorSchemeForState(list, index, listUI, currState);
117                    result.setForeground(new ColorUIResource(scheme.getForegroundColor()));
118                }
119                else
120                {
121                    Map<ComponentState, StateContributionInfo> activeStates = modelStateInfo.getStateContributionMap();
122                    SubstanceColorScheme colorScheme = getColorSchemeForState(list, index, listUI, currState);
123
124                    if (currState.isDisabled() || (activeStates == null) || (activeStates.size() == 1))
125                    {
126                        super.setForeground(new ColorUIResource(colorScheme.getForegroundColor()));
127                    }
128                    else
129                    {
130                        float aggrRed = 0.0f;
131                        float aggrGreen = 0.0f;
132                        float aggrBlue = 0.0f;
133
134                        for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : modelStateInfo
135                                .getStateContributionMap().entrySet())
136                        {
137                            ComponentState activeState = activeEntry.getKey();
138                            float activeContribution = activeEntry.getValue().getContribution();
139                            if (activeContribution == 0.0f)
140                                continue;
141
142                            SubstanceColorScheme scheme = getColorSchemeForState(list, index, listUI, activeState);
143                            Color schemeFg = scheme.getForegroundColor();
144                            aggrRed += schemeFg.getRed() * activeContribution;
145                            aggrGreen += schemeFg.getGreen() * activeContribution;
146                            aggrBlue += schemeFg.getBlue() * activeContribution;
147                        }
148
149                        result.setForeground(new ColorUIResource(new Color((int) aggrRed, (int) aggrGreen,
150                                (int) aggrBlue)));
151                    }
152                }
153            }
154
155            SubstanceStripingUtils.applyStripedBackground(list, index, this);
156        }
157
158        result.setEnabled(combo.isEnabled() & isEnabled());
159
160        return result;
161    }
162
163    protected Component getRendererComponent(JList list, Object value, int index, boolean isSelected)
164    {
165        setComponentOrientation(list.getComponentOrientation());
166
167        ListUI listUI = list.getUI();
168
169        if (listUI instanceof SubstanceListUI)
170        {
171            SubstanceListUI ui = (SubstanceListUI) listUI;
172
173            StateTransitionTracker.ModelStateInfo modelStateInfo = ui.getModelStateInfo(index, this);
174            ComponentState currState = ui.getCellState(index, this);
175
176            // special case for drop location
177            JList.DropLocation dropLocation = list.getDropLocation();
178            boolean isDropLocation = (dropLocation != null && !dropLocation.isInsert() && dropLocation.getIndex() == index);
179
180            if (!isDropLocation && (modelStateInfo != null))
181            {
182                Map<ComponentState, StateContributionInfo> activeStates = modelStateInfo.getStateContributionMap();
183                SubstanceColorScheme colorScheme = getColorSchemeForState(list, ui, currState);
184
185                if (currState.isDisabled() || (activeStates == null) || (activeStates.size() == 1))
186                    super.setForeground(new ColorUIResource(colorScheme.getForegroundColor()));
187                else
188                {
189                    float aggrRed = 0;
190                    float aggrGreen = 0;
191                    float aggrBlue = 0;
192
193                    for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : modelStateInfo
194                            .getStateContributionMap().entrySet())
195                    {
196                        ComponentState activeState = activeEntry.getKey();
197                        SubstanceColorScheme scheme = getColorSchemeForState(list, ui, activeState);
198                        Color schemeFg = scheme.getForegroundColor();
199                        float contribution = activeEntry.getValue().getContribution();
200
201                        aggrRed += schemeFg.getRed() * contribution;
202                        aggrGreen += schemeFg.getGreen() * contribution;
203                        aggrBlue += schemeFg.getBlue() * contribution;
204                    }
205
206                    super.setForeground(new ColorUIResource(new Color((int) aggrRed, (int) aggrGreen, (int) aggrBlue)));
207                }
208            }
209            else
210            {
211                SubstanceColorScheme scheme = getColorSchemeForState(list, ui, currState);
212
213                if (isDropLocation)
214                {
215                    scheme = SubstanceColorSchemeUtilities.getColorScheme(list,
216                            ColorSchemeAssociationKind.TEXT_HIGHLIGHT, currState);
217                }
218
219                super.setForeground(new ColorUIResource(scheme.getForegroundColor()));
220            }
221        }
222        else
223        {
224            if (isSelected)
225                setForeground(list.getSelectionForeground());
226            else
227                setForeground(list.getForeground());
228        }
229
230        if (SubstanceLookAndFeel.isCurrentLookAndFeel() && (list.getLayoutOrientation() == JList.VERTICAL))
231            SubstanceStripingUtils.applyStripedBackground(list, index, this);
232
233        updateItem(list, value);
234
235        Insets ins = SubstanceSizeUtils.getListCellRendererInsets(SubstanceSizeUtils.getComponentFontSize(list));
236        setBorder(new EmptyBorder(ins.top, ins.left, ins.bottom, ins.right));
237        setOpaque(false);
238
239        return this;
240    }
241
242    protected void updateItem(JList list, Object value)
243    {
244        if (value instanceof Icon)
245        {
246            setIcon((Icon) value);
247            setText("");
248        }
249        else
250        {
251            setIcon(null);
252            setText((value == null) ? "" : value.toString());
253            setToolTipText((value == null) ? "" : value.toString());
254        }
255
256        setEnabled(list.isEnabled());
257        setFont(list.getFont());
258    }
259
260    private SubstanceColorScheme getColorSchemeForState(JList list, SubstanceListUI ui, ComponentState state)
261    {
262        final UpdateOptimizationInfo updateOptimizationInfo = ui.getUpdateOptimizationInfo();
263
264        if (state == ComponentState.ENABLED)
265        {
266            if (updateOptimizationInfo == null)
267                return SubstanceColorSchemeUtilities.getColorScheme(list, state);
268
269            return updateOptimizationInfo.getDefaultScheme();
270        }
271
272        if (updateOptimizationInfo == null)
273            return SubstanceColorSchemeUtilities.getColorScheme(list, ColorSchemeAssociationKind.HIGHLIGHT, state);
274
275        return updateOptimizationInfo.getHighlightColorScheme(state);
276    }
277
278    private SubstanceColorScheme getColorSchemeForState(JList list, int index, SubstanceListUI listUI,
279            ComponentState state)
280    {
281        final boolean toUseHighlightKindForCurrState = (index >= 0)
282                && (state.isFacetActive(ComponentStateFacet.ROLLOVER) || state
283                        .isFacetActive(ComponentStateFacet.SELECTION));
284        final UpdateOptimizationInfo updateOptimizationInfo = listUI.getUpdateOptimizationInfo();
285
286        if (toUseHighlightKindForCurrState)
287        {
288            if (updateOptimizationInfo == null)
289                return SubstanceColorSchemeUtilities.getColorScheme(list, ColorSchemeAssociationKind.HIGHLIGHT, state);
290
291            return updateOptimizationInfo.getHighlightColorScheme(state);
292        }
293
294        if (updateOptimizationInfo == null)
295            return SubstanceColorSchemeUtilities.getColorScheme(list, state);
296
297        return updateOptimizationInfo.getDefaultScheme();
298    }
299}