001 /*
002 * $Id: DefaultTableColumnModelExt.java,v 1.10 2005/10/24 15:01:34 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.table;
023
024 import java.beans.PropertyChangeEvent;
025 import java.beans.PropertyChangeListener;
026 import java.util.ArrayList;
027 import java.util.Collections;
028 import java.util.HashMap;
029 import java.util.HashSet;
030 import java.util.Iterator;
031 import java.util.List;
032 import java.util.Map;
033 import java.util.Set;
034
035 import javax.swing.table.DefaultTableColumnModel;
036 import javax.swing.table.TableColumn;
037
038
039 /**
040 * A default implementation supporting invisible columns.
041 *
042 * Note (JW): hot fixed issues #156, #157. To really do it
043 * need enhanced TableColumnModelEvent and -Listeners that are
044 * aware of the event.
045 *
046 *
047 * @author Richard Bair
048 */
049 public class DefaultTableColumnModelExt extends DefaultTableColumnModel implements TableColumnModelExt {
050 private static final String IGNORE_EVENT = "TableColumnModelExt.ignoreEvent";
051 /**
052 * contains a list of all of the columns, in the order in which they would
053 * appear if all of the columns were visible.
054 */
055 private List allColumns = new ArrayList();
056
057 /**
058 * Set of invisible columns. These must be of type TableColumnExt.
059 */
060 private Set invisibleColumns = new HashSet();
061
062 private Map oldIndexes = new HashMap();
063
064 /**
065 * Listener attached to TableColumnExt instances to listen for changes
066 * to their visibility status, and to hide/show the column as oppropriate
067 */
068 private VisibilityListener visibilityListener = new VisibilityListener();
069
070 /**
071 * Creates a new instance of DefaultTableColumnModelExt
072 */
073 public DefaultTableColumnModelExt() {
074 super();
075 }
076
077
078 /**
079 *
080 */
081 public List getColumns(boolean includeHidden) {
082 if (includeHidden) {
083 return new ArrayList(allColumns);
084 }
085 return Collections.list(getColumns());
086 }
087
088 public int getColumnCount(boolean includeHidden) {
089 if (includeHidden) {
090 return allColumns.size();
091 }
092 return getColumnCount();
093 }
094 /**
095 * {@inheritDoc}
096 */
097 public TableColumnExt getColumnExt(Object identifier) {
098 for (Iterator iter = allColumns.iterator(); iter.hasNext();) {
099 TableColumn column = (TableColumn) iter.next();
100 if ((column instanceof TableColumnExt) && (identifier.equals(column.getIdentifier()))) {
101 return (TableColumnExt) column;
102 }
103 }
104 return null;
105 }
106 public Set getInvisibleColumns() {
107 return new HashSet(invisibleColumns);
108 }
109
110 /**
111 * hot fix for #157: listeners that are aware of
112 * the possible existence of invisible columns
113 * should check if the received columnRemoved originated
114 * from moving a column from visible to invisible.
115 *
116 * @param oldIndex the fromIndex of the columnEvent
117 * @return true if the column was moved to invisible
118 */
119 public boolean isRemovedToInvisibleEvent(int oldIndex) {
120 Integer index = new Integer(oldIndex);
121 return oldIndexes.containsValue(index);
122 }
123
124 /**
125 * hot fix for #157: listeners that are aware of
126 * the possible existence of invisible columns
127 * should check if the received columnAdded originated
128 * from moving a column from invisible to visible.
129 *
130 * @param newIndex the toIndex of the columnEvent
131 * @return true if the column was moved to visible
132 */
133 public boolean isAddedFromInvisibleEvent(int newIndex) {
134 if (!(getColumn(newIndex) instanceof TableColumnExt)) return false;
135 return Boolean.TRUE.equals(((TableColumnExt) getColumn(newIndex)).getClientProperty(IGNORE_EVENT));
136 }
137
138
139 public void removeColumn(TableColumn column) {
140 //remove the visibility listener if appropriate
141 if (column instanceof TableColumnExt) {
142 ((TableColumnExt)column).removePropertyChangeListener(visibilityListener);
143 }
144 //remove from the invisible columns set and the allColumns list first
145 invisibleColumns.remove(column);
146 allColumns.remove(column);
147 //let the superclass handle notification etc
148 super.removeColumn(column);
149 }
150
151 public void addColumn(TableColumn aColumn) {
152 // hacking to guarantee correct events
153 // two step: add as visible, setVisible
154 boolean oldVisible = true;
155 //add the visibility listener if appropriate
156 if (aColumn instanceof TableColumnExt) {
157 TableColumnExt xColumn = (TableColumnExt) aColumn;
158 oldVisible = xColumn.isVisible();
159 xColumn.setVisible(true);
160 xColumn.addPropertyChangeListener(visibilityListener);
161 }
162 //append the column to the end of "allColumns". If the column is
163 //visible, let super add it to the list of columns. If not, then
164 //add it to the set of invisible columns and return.
165 //In the case of an invisible column being added to the model,
166 //I still do event notification for the model having
167 //been changed so that the ColumnControlButton or other similar
168 //code interacting with invisible columns knows that a new invisible
169 //column has been added
170 allColumns.add(aColumn);
171 super.addColumn(aColumn);
172 if (aColumn instanceof TableColumnExt) {
173 ((TableColumnExt) aColumn).setVisible(oldVisible);
174 }
175
176 }
177
178
179 protected void moveToInvisible(TableColumnExt col) {
180 //make invisible
181 int oldIndex = tableColumns.indexOf(col);
182 invisibleColumns.add(col);
183 oldIndexes.put(col, new Integer(oldIndex));
184 super.removeColumn(col);
185 }
186
187 protected void moveToVisible(TableColumnExt col) {
188 //make visible
189 invisibleColumns.remove(col);
190 Integer oldIndexInteger = (Integer)oldIndexes.get(col);
191 int oldIndex = oldIndexInteger == null ? getColumnCount() : oldIndexInteger.intValue();
192 oldIndexes.remove(col);
193 col.putClientProperty(IGNORE_EVENT, Boolean.TRUE);
194 super.addColumn(col);
195 moveColumn(getColumnCount() - 1, Math.min(getColumnCount() - 1, oldIndex));
196 col.putClientProperty(IGNORE_EVENT, null);
197 }
198
199 private final class VisibilityListener implements PropertyChangeListener {
200 public void propertyChange(PropertyChangeEvent evt) {
201 if (evt.getPropertyName().equals("visible")) {
202 boolean oldValue = ((Boolean)evt.getOldValue()).booleanValue();
203 boolean newValue = ((Boolean)evt.getNewValue()).booleanValue();
204 TableColumnExt col = (TableColumnExt)evt.getSource();
205
206 if (oldValue && !newValue) {
207 moveToInvisible(col);
208 } else if (!oldValue && newValue) {
209 moveToVisible(col);
210 }
211 }
212 }
213 }
214
215 }