001 /*
002 * $Id: HighlighterPipeline.java,v 1.12 2006/02/08 13:05:48 kleopatra Exp $
003 *
004 * Copyright 2004 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
022 package org.jdesktop.swingx.decorator;
023
024 import java.awt.Color;
025 import java.awt.Component;
026 import java.util.ArrayList;
027 import java.util.Iterator;
028 import java.util.List;
029
030 import javax.swing.BoundedRangeModel;
031 import javax.swing.JComponent;
032 import javax.swing.event.ChangeEvent;
033 import javax.swing.event.ChangeListener;
034 import javax.swing.event.EventListenerList;
035 import javax.swing.table.DefaultTableCellRenderer;
036
037 import org.jdesktop.swingx.decorator.Highlighter.UIHighlighter;
038
039 /**
040 * A class which manages the lists of highlighters.
041 *
042 * @see Highlighter
043 *
044 * @author Ramesh Gupta
045 * @author Jeanette Winzenburg
046 *
047 */
048 public class HighlighterPipeline implements UIHighlighter {
049 protected transient ChangeEvent changeEvent = null;
050 protected EventListenerList listenerList = new EventListenerList();
051
052 protected List<Highlighter> highlighters;
053 // JW: this is a hack to make JXTable renderers behave...
054 private final static Highlighter resetDefaultTableCellRendererHighlighter = new Highlighter(null, null, true){
055
056 @Override
057 protected void applyBackground(Component renderer, ComponentAdapter adapter) {
058 if (!adapter.isSelected()) {
059 // renderer.setBackground(null);
060 Object colorMemory = ((JComponent) renderer).getClientProperty("rendererColorMemory.background");
061 if (colorMemory instanceof ColorMemory) {
062 renderer.setBackground(((ColorMemory) colorMemory).color);
063 } else {
064 ((JComponent) renderer).putClientProperty("rendererColorMemory.background", new ColorMemory(renderer.getBackground()));
065 }
066 }
067 }
068
069 @Override
070 protected void applyForeground(Component renderer, ComponentAdapter adapter) {
071 if (!adapter.isSelected()) {
072 // renderer.setForeground(null);
073 Object colorMemory = ((JComponent) renderer).getClientProperty("rendererColorMemory.foreground");
074 if (colorMemory instanceof ColorMemory) {
075 renderer.setForeground(((ColorMemory) colorMemory).color);
076 } else {
077 ((JComponent) renderer).putClientProperty("rendererColorMemory.foreground", new ColorMemory(renderer.getForeground()));
078 }
079 }
080 }
081
082 };
083
084 private static class ColorMemory {
085 public ColorMemory(Color foreground) {
086 color = foreground;
087 }
088
089 Color color;
090 }
091
092 private ChangeListener highlighterChangeListener;
093
094 public HighlighterPipeline() {
095 highlighters = new ArrayList<Highlighter>();
096 }
097
098 /**
099 *
100 * @param inList the array of highlighters to initially add to this.
101 * @throws NullPointerException if array is null of array contains null values.
102 */
103 public HighlighterPipeline(Highlighter[] inList) {
104 this();
105 for (int i = 0; i < inList.length; i++) {
106 addHighlighter(inList[i]);
107 }
108 }
109
110 /**
111 * Appends a highlighter to the pipeline.
112 *
113 * @param hl highlighter to add
114 * @throws NullPointerException if highlighter is null.
115 */
116 public void addHighlighter(Highlighter hl) {
117 addHighlighter(hl, false);
118 }
119
120 /**
121 * Adds a highlighter to the pipeline.
122 *
123 * PENDING: Duplicate inserts?
124 *
125 * @param hl highlighter to add
126 * @param prepend prepend the highlighter if true; false will append
127 * @throws NullPointerException if highlighter is null.
128 */
129 public void addHighlighter(Highlighter hl, boolean prepend) {
130 if (prepend) {
131 highlighters.add(0, hl);
132 } else {
133 highlighters.add(highlighters.size(), hl);
134 }
135 updateUI(hl);
136 hl.addChangeListener(getHighlighterChangeListener());
137 fireStateChanged();
138 }
139
140
141 private ChangeListener getHighlighterChangeListener() {
142 if (highlighterChangeListener == null) {
143 highlighterChangeListener = new ChangeListener() {
144
145 public void stateChanged(ChangeEvent e) {
146 fireStateChanged();
147
148 }
149
150 };
151 }
152 return highlighterChangeListener;
153 }
154
155 /**
156 * Removes a highlighter from the pipeline.
157 *
158 *
159 * @param hl highlighter to remove
160 */
161 public void removeHighlighter(Highlighter hl) {
162 boolean success = highlighters.remove(hl);
163 if (success) {
164 // PENDING: duplicates?
165 hl.removeChangeListener(getHighlighterChangeListener());
166 fireStateChanged();
167 }
168 // should log if this didn't succeed. Maybe
169 }
170
171 public Highlighter[] getHighlighters() {
172 return (Highlighter[])highlighters.toArray(new Highlighter[highlighters.size()]);
173 }
174
175
176 /**
177 * Applies all the highlighters to the components.
178 *
179 * @throws NullPointerException if either stamp or adapter is null.
180 */
181 public Component apply(Component stamp, ComponentAdapter adapter) {
182 stamp = resetDefaultTableCellRenderer(stamp, adapter);
183 for (Iterator<Highlighter> iter = highlighters.iterator(); iter.hasNext();) {
184 stamp = iter.next().highlight(stamp, adapter);
185
186 }
187 return stamp;
188 }
189
190 /**
191 * This is a hack around DefaultTableCellRenderer color "memory".
192 *
193 * The issue is that the default has internal color management
194 * which is different from other types of renderers. The
195 * consequence of the internal color handling is that there's
196 * a color memory which must be reset somehow. The "old" hack around
197 * reset the xxColors of all types of renderers to the adapter's
198 * target XXColors, introducing #178-swingx (Highlighgters must not
199 * change any colors except those for which their color properties are
200 * explicitly set).
201 *
202 * This hack limits the interference to renderers of type
203 * DefaultTableCellRenderer, applying a hacking highlighter which
204 * resets the renderers XXColors to null if unselected. Note that
205 * both hacks loose any colors previously set by clients (in
206 * prepareRenderer before applying the pipeline).
207 *
208 * @param stamp
209 * @param adapter
210 * @return
211 */
212 private Component resetDefaultTableCellRenderer(Component stamp, ComponentAdapter adapter) {
213 //JW
214 // table renderers have different state memory as list/tree renderers
215 // without the null they don't unstamp!
216 // but... null has adversory effect on JXList f.i. - selection
217 // color is changed. This is related to #178-swingx:
218 // highlighter background computation is weird.
219 //
220 if (stamp instanceof DefaultTableCellRenderer) {
221 /** @todo optimize the following bug fix */
222 stamp = resetDefaultTableCellRendererHighlighter.highlight(stamp, adapter);
223 }
224 return stamp;
225 }
226
227 public void updateUI() {
228 for (Highlighter highlighter : highlighters) {
229 updateUI(highlighter);
230 }
231 }
232
233 /**
234 * @param hl
235 */
236 private void updateUI(Highlighter hl) {
237 if (hl instanceof UIHighlighter) {
238 ((UIHighlighter) hl).updateUI();
239 }
240 }
241
242 /**
243 * Adds a <code>ChangeListener</code>. The change listeners are run each
244 * time any one of the Bounded Range model properties changes.
245 *
246 * @param l the ChangeListener to add
247 * @see #removeChangeListener
248 * @see BoundedRangeModel#addChangeListener
249 */
250 public void addChangeListener(ChangeListener l) {
251 listenerList.add(ChangeListener.class, l);
252 }
253
254
255 /**
256 * Removes a <code>ChangeListener</code>.
257 *
258 * @param l the <code>ChangeListener</code> to remove
259 * @see #addChangeListener
260 * @see BoundedRangeModel#removeChangeListener
261 */
262 public void removeChangeListener(ChangeListener l) {
263 listenerList.remove(ChangeListener.class, l);
264 }
265
266
267 /**
268 * Returns an array of all the change listeners
269 * registered on this <code>DefaultBoundedRangeModel</code>.
270 *
271 * @return all of this model's <code>ChangeListener</code>s
272 * or an empty
273 * array if no change listeners are currently registered
274 *
275 * @see #addChangeListener
276 * @see #removeChangeListener
277 *
278 * @since 1.4
279 */
280 public ChangeListener[] getChangeListeners() {
281 return (ChangeListener[])listenerList.getListeners(
282 ChangeListener.class);
283 }
284
285
286 /**
287 * Runs each <code>ChangeListener</code>'s <code>stateChanged</code> method.
288 *
289 * @see #setRangeProperties
290 * @see EventListenerList
291 */
292 protected void fireStateChanged()
293 {
294 Object[] listeners = listenerList.getListenerList();
295 for (int i = listeners.length - 2; i >= 0; i -=2 ) {
296 if (listeners[i] == ChangeListener.class) {
297 if (changeEvent == null) {
298 changeEvent = new ChangeEvent(this);
299 }
300 ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
301 }
302 }
303 }
304
305
306 }