001 /* 002 * $Id: SelectionMapper.java,v 1.4 2006/05/14 15:55:53 dmouse 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 package org.jdesktop.swingx.decorator; 022 023 import javax.swing.DefaultListSelectionModel; 024 import javax.swing.ListSelectionModel; 025 import javax.swing.event.ListSelectionEvent; 026 import javax.swing.event.ListSelectionListener; 027 028 /** 029 * Responsible for keeping track of selection in model coordinates.<p> 030 * 031 * updates view selection on pipeline change. 032 * updates model selection on view selection change. 033 * 034 * @author Jeanette Winzenburg 035 */ 036 public class SelectionMapper { 037 038 /** selection in view coordinates. */ 039 private ListSelectionModel viewSelection; 040 041 /** selection in model coordinates. */ 042 protected DefaultListSelectionModel modelSelection; 043 044 /** mapping pipeline. */ 045 private FilterPipeline pipeline; 046 047 /** listener to view selection. */ 048 private ListSelectionListener viewSelectionListener; 049 /** state flag for locking non-listening phases. */ 050 private boolean isListening; 051 052 /** listener to mapping pipeline. */ 053 private PipelineListener pipelineListener; 054 055 056 /** 057 * PRE: selection != null; 058 * 059 * @param pipeline 060 * @param selection 061 */ 062 public SelectionMapper(FilterPipeline pipeline, ListSelectionModel selection) { 063 modelSelection = new DefaultListSelectionModel(); 064 setViewSelectionModel(selection); 065 setFilters(pipeline); 066 } 067 068 /** 069 * sets the view selection model. Must not be null. 070 * 071 * @param selection holding selected indices in view coordinates 072 */ 073 public void setViewSelectionModel(ListSelectionModel selection) { 074 ListSelectionModel old = this.viewSelection; 075 if (old != null) { 076 old.removeListSelectionListener(viewSelectionListener); 077 // modelSelection.clearSelection(); 078 clearModelSelection(); 079 } 080 this.viewSelection = selection; 081 mapTowardsModel(); 082 viewSelection.addListSelectionListener(getViewSelectionListener()); 083 isListening = true; 084 } 085 086 /** 087 * TODO: temporarily added for testing... 088 * @return view selection model 089 */ 090 public ListSelectionModel getViewSelectionModel() { 091 return viewSelection; 092 } 093 094 public void setFilters(FilterPipeline pipeline) { 095 FilterPipeline old = this.pipeline; 096 if (old != null) { 097 old.removePipelineListener(pipelineListener); 098 } 099 this.pipeline = pipeline; 100 if (pipeline != null) { 101 pipeline.addPipelineListener(getPipelineListener()); 102 } 103 restoreSelection(); 104 } 105 106 107 public void restoreSelection() { 108 lock(); 109 clearViewSelection(); 110 111 int[] selected = getSelectedRows(modelSelection); 112 for (int i = 0; i < selected.length; i++) { 113 int index = convertToView(selected[i]); 114 // index might be -1, but then addSelectionInterval ignores it. 115 viewSelection.addSelectionInterval(index, index); 116 } 117 int lead = modelSelection.getLeadSelectionIndex(); 118 // TODO: PENDING: JW - this is a quick hack for spurious AIOB - need to enquire why 119 // they happen in the first place 120 if (lead >= 0) { 121 lead = convertToView(lead); 122 } 123 if (viewSelection instanceof DefaultListSelectionModel) { 124 ((DefaultListSelectionModel) viewSelection).moveLeadSelectionIndex(lead); 125 } else { 126 // PENDING: not tested, don't have a non-DefaultXX handy 127 viewSelection.removeSelectionInterval(lead, lead); 128 viewSelection.addSelectionInterval(lead, lead); 129 } 130 unlock(); 131 } 132 133 134 public void unlock() { 135 if (!isListening) { 136 viewSelection.setValueIsAdjusting(false); 137 viewSelection.addListSelectionListener(getViewSelectionListener()); 138 isListening = true; 139 } 140 } 141 142 public void lock() { 143 if (isListening) { 144 viewSelection.removeListSelectionListener(viewSelectionListener); 145 viewSelection.setValueIsAdjusting(true); 146 isListening = false; 147 } 148 } 149 150 public void clearModelSelection() { 151 // TODO: JW: need to reset anchor/lead? 152 modelSelection.clearSelection(); 153 modelSelection.setAnchorSelectionIndex(-1); 154 modelSelection.setLeadSelectionIndex(-1); 155 } 156 157 /** 158 * 159 */ 160 private void clearViewSelection() { 161 // TODO: JW - hmm... clearSelection doesn't reset the lead/anchor. Why not? 162 viewSelection.clearSelection(); 163 viewSelection.setAnchorSelectionIndex(-1); 164 viewSelection.setLeadSelectionIndex(-1); 165 } 166 167 public void insertIndexInterval(int start, int length, boolean before) { 168 modelSelection.insertIndexInterval(start, length, before); 169 } 170 171 public void removeIndexInterval(int start, int end) { 172 modelSelection.removeIndexInterval(start, end); 173 } 174 175 private void mapTowardsModel() { 176 // modelSelection.clearSelection(); 177 clearModelSelection(); 178 int[] selected = getSelectedRows(viewSelection); 179 for (int i = 0; i < selected.length; i++) { 180 int modelIndex = convertToModel(selected[i]); 181 modelSelection.addSelectionInterval(modelIndex, modelIndex); 182 } 183 if (selected.length > 0) { 184 // convert lead selection index to model coordinates 185 modelSelection.moveLeadSelectionIndex(convertToModel(viewSelection.getLeadSelectionIndex())); 186 } 187 } 188 189 private int convertToModel(int index) { 190 // TODO: JW: check for valid index? must be < pipeline.getOutputSize() 191 return (pipeline != null) && pipeline.isAssigned() ? pipeline.convertRowIndexToModel(index) : index; 192 } 193 194 private int convertToView(int index) { 195 // TODO: JW: check for valid index? must be < pipeline.getInputSize() 196 return (pipeline != null) && pipeline.isAssigned() ? pipeline.convertRowIndexToView(index) : index; 197 } 198 199 protected void updateFromViewSelectionChanged(int firstIndex, int lastIndex) { 200 for (int i = firstIndex; i <= lastIndex; i++) { 201 int modelIndex = convertToModel(i); 202 if (viewSelection.isSelectedIndex(i)) { 203 modelSelection.addSelectionInterval(modelIndex, modelIndex); 204 } else { 205 modelSelection.removeSelectionInterval(modelIndex, modelIndex); 206 } 207 } 208 int lead = viewSelection.getLeadSelectionIndex(); 209 if (lead >= 0) { 210 modelSelection.moveLeadSelectionIndex(convertToModel(lead)); 211 } 212 213 } 214 215 protected void updateFromPipelineChanged() { 216 restoreSelection(); 217 } 218 219 private int[] getSelectedRows(ListSelectionModel selection) { 220 int iMin = selection.getMinSelectionIndex(); 221 int iMax = selection.getMaxSelectionIndex(); 222 223 if ((iMin == -1) || (iMax == -1)) { 224 return new int[0]; 225 } 226 227 int[] rvTmp = new int[1 + (iMax - iMin)]; 228 int n = 0; 229 for (int i = iMin; i <= iMax; i++) { 230 if (selection.isSelectedIndex(i)) { 231 rvTmp[n++] = i; 232 } 233 } 234 int[] rv = new int[n]; 235 System.arraycopy(rvTmp, 0, rv, 0, n); 236 return rv; 237 } 238 239 private PipelineListener getPipelineListener() { 240 if (pipelineListener == null) { 241 pipelineListener = new PipelineListener() { 242 243 public void contentsChanged(PipelineEvent e) { 244 updateFromPipelineChanged(); 245 } 246 247 }; 248 } 249 return pipelineListener; 250 } 251 252 private ListSelectionListener getViewSelectionListener() { 253 if (viewSelectionListener == null) { 254 viewSelectionListener = new ListSelectionListener() { 255 256 public void valueChanged(ListSelectionEvent e) { 257 if (e.getValueIsAdjusting()) return; 258 updateFromViewSelectionChanged(e.getFirstIndex(), e.getLastIndex()); 259 } 260 261 }; 262 } 263 return viewSelectionListener; 264 } 265 266 }