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 }