svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.fmap.control / src / main / java / org / gvsig / fmap / mapcontrol / dal / feature / swing / FeatureSelectionModel.java @ 43358
History | View | Annotate | Download (13.6 KB)
1 | 40559 | jjdelcerro | /**
|
---|---|---|---|
2 | * gvSIG. Desktop Geographic Information System.
|
||
3 | 40435 | jjdelcerro | *
|
4 | 40559 | jjdelcerro | * Copyright (C) 2007-2013 gvSIG Association.
|
5 | *
|
||
6 | 40435 | jjdelcerro | * This program is free software; you can redistribute it and/or
|
7 | * modify it under the terms of the GNU General Public License
|
||
8 | 40559 | jjdelcerro | * as published by the Free Software Foundation; either version 3
|
9 | 40435 | jjdelcerro | * of the License, or (at your option) any later version.
|
10 | 40559 | jjdelcerro | *
|
11 | 40435 | jjdelcerro | * This program is distributed in the hope that it will be useful,
|
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
14 | * GNU General Public License for more details.
|
||
15 | 40559 | jjdelcerro | *
|
16 | 40435 | jjdelcerro | * You should have received a copy of the GNU General Public License
|
17 | * along with this program; if not, write to the Free Software
|
||
18 | 40559 | jjdelcerro | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
19 | 40435 | jjdelcerro | * MA 02110-1301, USA.
|
20 | 40559 | jjdelcerro | *
|
21 | * For any additional information, do not hesitate to contact us
|
||
22 | * at info AT gvsig.com, or visit our website www.gvsig.com.
|
||
23 | 40435 | jjdelcerro | */
|
24 | /*
|
||
25 | * AUTHORS (In addition to CIT):
|
||
26 | * 2010 {} {{Task}}
|
||
27 | */
|
||
28 | package org.gvsig.fmap.mapcontrol.dal.feature.swing; |
||
29 | |||
30 | import javax.swing.ListSelectionModel; |
||
31 | import javax.swing.event.EventListenerList; |
||
32 | import javax.swing.event.ListSelectionEvent; |
||
33 | import javax.swing.event.ListSelectionListener; |
||
34 | |||
35 | import org.gvsig.fmap.dal.exception.DataException; |
||
36 | import org.gvsig.fmap.dal.feature.Feature; |
||
37 | 43358 | jjdelcerro | import org.gvsig.fmap.dal.feature.FeatureQuery; |
38 | 40435 | jjdelcerro | import org.gvsig.fmap.dal.feature.FeatureSelection; |
39 | import org.gvsig.fmap.dal.feature.FeatureSet; |
||
40 | import org.gvsig.fmap.dal.feature.FeatureStore; |
||
41 | import org.gvsig.fmap.dal.feature.FeatureStoreNotification; |
||
42 | import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException; |
||
43 | import org.gvsig.fmap.mapcontrol.dal.feature.swing.table.FeatureTableModel; |
||
44 | import org.gvsig.tools.dispose.DisposableIterator; |
||
45 | import org.gvsig.tools.observer.Observable; |
||
46 | import org.gvsig.tools.observer.Observer; |
||
47 | import org.slf4j.Logger; |
||
48 | import org.slf4j.LoggerFactory; |
||
49 | |||
50 | /**
|
||
51 | * @author 2010- C?sar Ordi?ana - gvSIG team
|
||
52 | */
|
||
53 | public class FeatureSelectionModel implements ListSelectionModel, Observer { |
||
54 | 42639 | dmartinezizquierdo | private static Logger LOG = |
55 | 40435 | jjdelcerro | LoggerFactory.getLogger(FeatureSelectionModel.class); |
56 | 42639 | dmartinezizquierdo | |
57 | 40435 | jjdelcerro | protected EventListenerList listenerList = new EventListenerList(); |
58 | |||
59 | private final FeatureTableModel featureTableModel; |
||
60 | |||
61 | private int selectionMode = SINGLE_INTERVAL_SELECTION; |
||
62 | |||
63 | private boolean isAdjusting = false; |
||
64 | |||
65 | private int anchor = -1; |
||
66 | |||
67 | private int lead = -1; |
||
68 | 42639 | dmartinezizquierdo | |
69 | 40435 | jjdelcerro | private int currentFirst = -1; |
70 | private int currentLast = -1; |
||
71 | |||
72 | /**
|
||
73 | * Creates a new {@link FeatureSelectionModel} with a
|
||
74 | * {@link FeatureTableModel} used to get the {@link Feature}s by position in
|
||
75 | * the table.
|
||
76 | 42639 | dmartinezizquierdo | *
|
77 | 40435 | jjdelcerro | * @param featureTableModel
|
78 | * to get Features from
|
||
79 | * @throws DataException
|
||
80 | * if there is an error getting the store selection
|
||
81 | */
|
||
82 | public FeatureSelectionModel(FeatureTableModel featureTableModel)
|
||
83 | throws DataException {
|
||
84 | this.featureTableModel = featureTableModel;
|
||
85 | this.featureTableModel.getFeatureStore().addObserver(this); |
||
86 | } |
||
87 | |||
88 | public int getAnchorSelectionIndex() { |
||
89 | return anchor;
|
||
90 | } |
||
91 | |||
92 | public int getLeadSelectionIndex() { |
||
93 | return lead;
|
||
94 | } |
||
95 | |||
96 | public int getMaxSelectionIndex() { |
||
97 | 42639 | dmartinezizquierdo | |
98 | 40435 | jjdelcerro | int resp = this.getSelectionIndex(true); |
99 | return resp;
|
||
100 | /*
|
||
101 | 42639 | dmartinezizquierdo | *
|
102 | 40435 | jjdelcerro | * The call to "featureTableModel.getFeatureAt(i)"
|
103 | * causes a lot of reloadPage all over
|
||
104 | 42639 | dmartinezizquierdo | *
|
105 | 40435 | jjdelcerro | FeatureSelection selection = getFeatureSelection();
|
106 | try {
|
||
107 | if (!selection.isEmpty()) {
|
||
108 | for (int i = featureTableModel.getRowCount() - 1; i >= 0; i--) {
|
||
109 | if (selection.isSelected(featureTableModel.getFeatureAt(i))) {
|
||
110 | return i;
|
||
111 | }
|
||
112 | }
|
||
113 | }
|
||
114 | } catch (DataException e) {
|
||
115 | throw new SelectionChangeException(e);
|
||
116 | }
|
||
117 | return -1;
|
||
118 | */
|
||
119 | } |
||
120 | |||
121 | public int getMinSelectionIndex() { |
||
122 | |||
123 | int resp = this.getSelectionIndex(false); |
||
124 | return resp;
|
||
125 | 42639 | dmartinezizquierdo | |
126 | 40435 | jjdelcerro | /*
|
127 | 42639 | dmartinezizquierdo | *
|
128 | 40435 | jjdelcerro | * The call to "featureTableModel.getFeatureAt(i)"
|
129 | * causes a lot of reloadPage all over
|
||
130 | 42639 | dmartinezizquierdo | *
|
131 | 40435 | jjdelcerro | try {
|
132 | |||
133 | int ii = 0;
|
||
134 | |||
135 | FeatureSelection selection = this.getFeatureSelection();
|
||
136 | for (int i = 0; i < featureTableModel.getRowCount(); i++) {
|
||
137 | if (selection.isSelected(featureTableModel.getFeatureAt(i))) {
|
||
138 | ii = i;
|
||
139 | }
|
||
140 | }
|
||
141 | } catch (Exception e) {
|
||
142 | throw new SelectionChangeException(e);
|
||
143 | }
|
||
144 | */
|
||
145 | 42639 | dmartinezizquierdo | |
146 | 40435 | jjdelcerro | } |
147 | |||
148 | public void insertIndexInterval(int index, int length, boolean before) { |
||
149 | // Nothing to do
|
||
150 | } |
||
151 | |||
152 | public void removeIndexInterval(int index0, int index1) { |
||
153 | // Nothing to do
|
||
154 | } |
||
155 | |||
156 | public void setAnchorSelectionIndex(int index) { |
||
157 | this.anchor = index;
|
||
158 | } |
||
159 | |||
160 | public void setLeadSelectionIndex(int index) { |
||
161 | this.lead = index;
|
||
162 | } |
||
163 | |||
164 | public void addSelectionInterval(int index0, int index1) { |
||
165 | 42639 | dmartinezizquierdo | if (!featureTableModel.isSelectionLocked()){
|
166 | doWithSelection(new FeatureSelectionOperation() {
|
||
167 | 40435 | jjdelcerro | |
168 | 42639 | dmartinezizquierdo | public void doWithSelection(FeatureSelection selection, int first, |
169 | int last) throws DataException { |
||
170 | for (int i = first; i <= last; i++) { |
||
171 | Feature feature = getFeature(i); |
||
172 | if (!selection.isSelected(feature)) {
|
||
173 | selection.select(feature); |
||
174 | } |
||
175 | } |
||
176 | } |
||
177 | 40435 | jjdelcerro | |
178 | 42639 | dmartinezizquierdo | }, index0, index1, true);
|
179 | } |
||
180 | 40435 | jjdelcerro | } |
181 | |||
182 | public void setSelectionInterval(int index0, int index1) { |
||
183 | 42639 | dmartinezizquierdo | if (!featureTableModel.isSelectionLocked()){
|
184 | doWithSelection(new FeatureSelectionOperation() {
|
||
185 | 40435 | jjdelcerro | |
186 | 42639 | dmartinezizquierdo | public void doWithSelection(FeatureSelection selection, int first, |
187 | int last) throws DataException { |
||
188 | selection.deselectAll(); |
||
189 | for (int i = first; i <= last; i++) { |
||
190 | Feature feature = getFeature(i); |
||
191 | selection.select(feature); |
||
192 | } |
||
193 | } |
||
194 | 40435 | jjdelcerro | |
195 | 42639 | dmartinezizquierdo | }, index0, index1, true);
|
196 | } |
||
197 | 40435 | jjdelcerro | } |
198 | |||
199 | public void removeSelectionInterval(int index0, int index1) { |
||
200 | 42639 | dmartinezizquierdo | if (!featureTableModel.isSelectionLocked()){
|
201 | doWithSelection(new FeatureSelectionOperation() {
|
||
202 | 40435 | jjdelcerro | |
203 | 42639 | dmartinezizquierdo | public void doWithSelection(FeatureSelection selection, int first, |
204 | int last) throws DataException { |
||
205 | for (int i = first; i <= last; i++) { |
||
206 | Feature feature = getFeature(i); |
||
207 | if (selection.isSelected(feature)) {
|
||
208 | selection.deselect(feature); |
||
209 | } |
||
210 | } |
||
211 | } |
||
212 | 40435 | jjdelcerro | |
213 | 42639 | dmartinezizquierdo | }, index0, index1, false);
|
214 | } |
||
215 | 40435 | jjdelcerro | } |
216 | |||
217 | public void clearSelection() { |
||
218 | 42639 | dmartinezizquierdo | if (!featureTableModel.isSelectionLocked()){
|
219 | try {
|
||
220 | getFeatureSelection().deselectAll(); |
||
221 | } catch (DataException e) {
|
||
222 | throw new SelectionChangeException(e); |
||
223 | } |
||
224 | } |
||
225 | 40435 | jjdelcerro | } |
226 | |||
227 | public boolean isSelectedIndex(int index) { |
||
228 | if (index == -1) { |
||
229 | return false; |
||
230 | } |
||
231 | Feature feature = featureTableModel.getFeatureAt(index); |
||
232 | return getFeatureSelection().isSelected(feature);
|
||
233 | } |
||
234 | |||
235 | public boolean isSelectionEmpty() { |
||
236 | try {
|
||
237 | return getFeatureSelection().isEmpty();
|
||
238 | } catch (DataException ex) {
|
||
239 | throw new SelectionChangeException(ex); |
||
240 | } |
||
241 | } |
||
242 | |||
243 | public boolean getValueIsAdjusting() { |
||
244 | return isAdjusting;
|
||
245 | } |
||
246 | |||
247 | public void setValueIsAdjusting(boolean valueIsAdjusting) { |
||
248 | if (this.isAdjusting != valueIsAdjusting) { |
||
249 | this.isAdjusting = valueIsAdjusting;
|
||
250 | if (this.isAdjusting) { |
||
251 | getFeatureSelection().beginComplexNotification(); |
||
252 | } else {
|
||
253 | getFeatureSelection().endComplexNotification(); |
||
254 | } |
||
255 | } |
||
256 | } |
||
257 | |||
258 | public int getSelectionMode() { |
||
259 | return selectionMode;
|
||
260 | } |
||
261 | |||
262 | public void setSelectionMode(int selectionMode) { |
||
263 | this.selectionMode = selectionMode;
|
||
264 | } |
||
265 | |||
266 | public void addListSelectionListener(ListSelectionListener listener) { |
||
267 | listenerList.add(ListSelectionListener.class, listener);
|
||
268 | } |
||
269 | |||
270 | public void removeListSelectionListener(ListSelectionListener listener) { |
||
271 | listenerList.remove(ListSelectionListener.class, listener);
|
||
272 | } |
||
273 | |||
274 | public void update(Observable observable, Object notification) { |
||
275 | if (notification instanceof FeatureStoreNotification) { |
||
276 | FeatureStoreNotification fnotification = |
||
277 | (FeatureStoreNotification) notification; |
||
278 | if (!fnotification.getSource().equals(getFeatureStore())) {
|
||
279 | return;
|
||
280 | } |
||
281 | if (FeatureStoreNotification.SELECTION_CHANGE.equals(fnotification.getType())) {
|
||
282 | try{
|
||
283 | fireValueChanged(-1, -1, false); |
||
284 | }catch(ConcurrentDataModificationException e){
|
||
285 | LOG.warn("The store has been updated and the selection can not be refreshed", e);
|
||
286 | } |
||
287 | } |
||
288 | } |
||
289 | } |
||
290 | |||
291 | private FeatureSelection getFeatureSelection() {
|
||
292 | try {
|
||
293 | return (FeatureSelection) getFeatureStore().getSelection();
|
||
294 | } catch (DataException ex) {
|
||
295 | throw new SelectionChangeException(ex); |
||
296 | } |
||
297 | } |
||
298 | |||
299 | /**
|
||
300 | * @param operation
|
||
301 | * @param index0
|
||
302 | * @param index1
|
||
303 | */
|
||
304 | private void doWithSelection(FeatureSelectionOperation operation, |
||
305 | int index0, int index1, boolean select) { |
||
306 | // Set the anchor and lead
|
||
307 | anchor = index0; |
||
308 | lead = index1; |
||
309 | |||
310 | // As index0 <= index1 is no guaranteed, calculate the first and second
|
||
311 | // values
|
||
312 | int first = (index0 <= index1) ? index0 : index1;
|
||
313 | int last = (index0 <= index1) ? index1 : index0;
|
||
314 | 42639 | dmartinezizquierdo | |
315 | 40435 | jjdelcerro | //If the new selection is not updated don't continue
|
316 | if ((currentFirst == first) && (currentLast == last)){
|
||
317 | return;
|
||
318 | } |
||
319 | 43269 | fdiaz | |
320 | int oldFirst = currentFirst;
|
||
321 | int oldLast = currentLast;
|
||
322 | 40435 | jjdelcerro | currentFirst = first; |
323 | currentLast = last; |
||
324 | |||
325 | FeatureSelection selection = getFeatureSelection(); |
||
326 | |||
327 | // Perform the selection operation into a complex notification
|
||
328 | selection.beginComplexNotification(); |
||
329 | try {
|
||
330 | // Is a full select or deselect
|
||
331 | if (first == 00 && last == featureTableModel.getRowCount() - 1) { |
||
332 | if (select) {
|
||
333 | selection.selectAll(); |
||
334 | } else {
|
||
335 | selection.deselectAll(); |
||
336 | } |
||
337 | } else {
|
||
338 | operation.doWithSelection(selection, first, last); |
||
339 | } |
||
340 | } catch (DataException e) {
|
||
341 | throw new SelectionChangeException(e); |
||
342 | } finally {
|
||
343 | selection.endComplexNotification(); |
||
344 | } |
||
345 | |||
346 | 43269 | fdiaz | // Para no lanzar dos veces el fireValueChanged sobre las mismas filas de la selecci?n
|
347 | if(oldFirst<=first){
|
||
348 | if(oldLast<first){ //No intersectan los rangos |
||
349 | fireValueChanged(oldFirst, oldLast, isAdjusting); |
||
350 | fireValueChanged(first, last, isAdjusting); |
||
351 | } else { // Intersectan los rangos oldFirst - first - ... |
||
352 | if (last > oldLast) { // Intersectan los rangos oldFirst - first - oldLast - last |
||
353 | fireValueChanged(oldFirst, last, isAdjusting); |
||
354 | } else { // Intersectan los rangos oldFirst - first - last - oldLast |
||
355 | fireValueChanged(oldFirst, oldLast, isAdjusting); |
||
356 | } |
||
357 | } |
||
358 | } |
||
359 | if(first<=oldFirst){
|
||
360 | if(last<oldFirst){ //No intersectan los rangos |
||
361 | fireValueChanged(oldFirst, oldLast, isAdjusting); |
||
362 | fireValueChanged(first, last, isAdjusting); |
||
363 | } else { // Intersectan los rangos first - oldFirst - ... |
||
364 | if (last > oldLast) { // Intersectan los rangos first - oldFirst - oldLast - last |
||
365 | fireValueChanged(first, last, isAdjusting); |
||
366 | } else { // Intersectan los rangos first - oldFirst - last - oldLast |
||
367 | fireValueChanged(first, oldLast, isAdjusting); |
||
368 | } |
||
369 | } |
||
370 | } |
||
371 | 40435 | jjdelcerro | } |
372 | |||
373 | /**
|
||
374 | * Returns a Feature by table row position.
|
||
375 | */
|
||
376 | private Feature getFeature(int index) { |
||
377 | return featureTableModel.getFeatureAt(index);
|
||
378 | } |
||
379 | |||
380 | /**
|
||
381 | * Returns the FeatureStore.
|
||
382 | */
|
||
383 | private FeatureStore getFeatureStore() {
|
||
384 | return featureTableModel.getFeatureStore();
|
||
385 | } |
||
386 | |||
387 | /**
|
||
388 | * @param firstIndex
|
||
389 | * the first index in the interval
|
||
390 | * @param lastIndex
|
||
391 | * the last index in the interval
|
||
392 | * @param isAdjusting
|
||
393 | * true if this is the final change in a series of adjustments
|
||
394 | * @see EventListenerList
|
||
395 | */
|
||
396 | protected void fireValueChanged(int firstIndex, int lastIndex, |
||
397 | boolean isAdjusting) {
|
||
398 | Object[] listeners = listenerList.getListenerList(); |
||
399 | ListSelectionEvent e = null; |
||
400 | |||
401 | for (int i = listeners.length - 2; i >= 0; i -= 2) { |
||
402 | if (listeners[i] == ListSelectionListener.class) { |
||
403 | if (e == null) { |
||
404 | e = |
||
405 | new ListSelectionEvent(this, firstIndex, lastIndex, |
||
406 | isAdjusting); |
||
407 | } |
||
408 | ((ListSelectionListener) listeners[i + 1]).valueChanged(e); |
||
409 | } |
||
410 | } |
||
411 | } |
||
412 | |||
413 | private interface FeatureSelectionOperation { |
||
414 | void doWithSelection(FeatureSelection selection, int first, int last) |
||
415 | throws DataException;
|
||
416 | } |
||
417 | 42639 | dmartinezizquierdo | |
418 | 40435 | jjdelcerro | /**
|
419 | 42639 | dmartinezizquierdo | *
|
420 | * Return the index of the the first (last) selected feature
|
||
421 | *
|
||
422 | * @param last whether to return the index of the last selected feature
|
||
423 | 40435 | jjdelcerro | * @return
|
424 | */
|
||
425 | private int getSelectionIndex(boolean last) { |
||
426 | 42639 | dmartinezizquierdo | |
427 | 40435 | jjdelcerro | int ind = -1; |
428 | int resp = -1; |
||
429 | |||
430 | FeatureSet fs = null;
|
||
431 | DisposableIterator diter = null;
|
||
432 | |||
433 | try {
|
||
434 | FeatureSelection selection = getFeatureSelection(); |
||
435 | if (!selection.isEmpty()) {
|
||
436 | 43358 | jjdelcerro | FeatureStore store = getFeatureStore(); |
437 | FeatureQuery query = store.createFeatureQuery(); |
||
438 | query.addEssentialAttributeNames(store); |
||
439 | fs = store.getFeatureSet(query); |
||
440 | 40435 | jjdelcerro | diter = fs.fastIterator(); |
441 | Feature feat = null;
|
||
442 | while (diter.hasNext()) {
|
||
443 | ind++; |
||
444 | feat = (Feature) diter.next(); |
||
445 | if (selection.isSelected(feat)) {
|
||
446 | resp = ind; |
||
447 | if (!last) {
|
||
448 | break;
|
||
449 | } |
||
450 | } |
||
451 | } |
||
452 | 42639 | dmartinezizquierdo | |
453 | 40435 | jjdelcerro | } |
454 | } catch (DataException e) {
|
||
455 | throw new SelectionChangeException(e); |
||
456 | } finally {
|
||
457 | if (diter != null) { |
||
458 | diter.dispose(); |
||
459 | } |
||
460 | |||
461 | if (fs != null) { |
||
462 | fs.dispose(); |
||
463 | } |
||
464 | } |
||
465 | 42639 | dmartinezizquierdo | return resp;
|
466 | 40435 | jjdelcerro | } |
467 | 42639 | dmartinezizquierdo | |
468 | 40435 | jjdelcerro | } |