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 }