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 }