svn-gvsig-desktop / trunk / applications / appgvSIG / src / com / iver / cit / gvsig / gui / DefaultListSelectionModel.java @ 312
History | View | Annotate | Download (26.5 KB)
1 |
package com.iver.cit.gvsig.gui; |
---|---|
2 |
|
3 |
import java.io.Serializable; |
4 |
import java.util.EventListener; |
5 |
|
6 |
import javax.swing.ListSelectionModel; |
7 |
import javax.swing.event.EventListenerList; |
8 |
import javax.swing.event.ListSelectionEvent; |
9 |
import javax.swing.event.ListSelectionListener; |
10 |
|
11 |
import org.apache.log4j.Logger; |
12 |
|
13 |
import com.iver.cit.gvsig.fmap.FRecordset; |
14 |
import com.iver.cit.gvsig.fmap.SelectionEvent; |
15 |
import com.iver.cit.gvsig.fmap.SelectionListener; |
16 |
import com.iver.mdiApp.Utilities; |
17 |
|
18 |
|
19 |
/**
|
20 |
* Default data model for list selections.
|
21 |
*
|
22 |
* <p>
|
23 |
* <strong>Warning:</strong> Serialized objects of this class will not be
|
24 |
* compatible with future Swing releases. The current serialization support is
|
25 |
* appropriate for short term storage or RMI between applications running the
|
26 |
* same version of Swing. As of 1.4, support for long term storage of all
|
27 |
* JavaBeans<sup><font size="-2">TM</font></sup> has been added to the
|
28 |
* <code>java.beans</code> package. Please see {@link java.beans.XMLEncoder}.
|
29 |
* </p>
|
30 |
*
|
31 |
* @author Philip Milne
|
32 |
* @author Hans Muller
|
33 |
* @version 1.67 01/23/03
|
34 |
*
|
35 |
* @see ListSelectionModel
|
36 |
*/
|
37 |
public class DefaultListSelectionModel implements ListSelectionModel, Cloneable, |
38 |
Serializable, SelectionListener {
|
39 |
private static Logger logger = Logger.getLogger(DefaultListSelectionModel.class.getName()); |
40 |
private static final int MIN = -1; |
41 |
private static final int MAX = Integer.MAX_VALUE; |
42 |
private int selectionMode = MULTIPLE_INTERVAL_SELECTION; |
43 |
private int minIndex = MAX; |
44 |
private int maxIndex = MIN; |
45 |
private int anchorIndex = -1; |
46 |
private int leadIndex = -1; |
47 |
private int firstAdjustedIndex = MAX; |
48 |
private int lastAdjustedIndex = MIN; |
49 |
private boolean isAdjusting = false; |
50 |
private int firstChangedIndex = MAX; |
51 |
private int lastChangedIndex = MIN; |
52 |
|
53 |
private FRecordset recordset;
|
54 |
private TableSorter sorterModel;
|
55 |
|
56 |
protected EventListenerList listenerList = new EventListenerList(); |
57 |
protected boolean leadAnchorNotificationEnabled = true; |
58 |
|
59 |
/**
|
60 |
* Creates a new DefaultListSelectionModel object.
|
61 |
*
|
62 |
* @param recordset DOCUMENT ME!
|
63 |
*/
|
64 |
public DefaultListSelectionModel() { |
65 |
} |
66 |
|
67 |
public void setRecordset(FRecordset recordset){ |
68 |
this.recordset = recordset;
|
69 |
recordset.addListSelectionListener(this);
|
70 |
} |
71 |
|
72 |
// implements javax.swing.ListSelectionModel
|
73 |
public int getMinSelectionIndex() { |
74 |
return isSelectionEmpty() ? (-1) : minIndex; |
75 |
} |
76 |
|
77 |
// implements javax.swing.ListSelectionModel
|
78 |
public int getMaxSelectionIndex() { |
79 |
return maxIndex;
|
80 |
} |
81 |
|
82 |
// implements javax.swing.ListSelectionModel
|
83 |
public boolean getValueIsAdjusting() { |
84 |
return isAdjusting;
|
85 |
} |
86 |
|
87 |
// implements javax.swing.ListSelectionModel
|
88 |
|
89 |
/**
|
90 |
* Returns the selection mode.
|
91 |
*
|
92 |
* @return one of the these values: <ul><li>SINGLE_SELECTION
|
93 |
* <li>SINGLE_INTERVAL_SELECTION <li>MULTIPLE_INTERVAL_SELECTION
|
94 |
* </ul>
|
95 |
*
|
96 |
* @see #getSelectionMode
|
97 |
*/
|
98 |
public int getSelectionMode() { |
99 |
return selectionMode;
|
100 |
} |
101 |
|
102 |
// implements javax.swing.ListSelectionModel
|
103 |
|
104 |
/**
|
105 |
* Sets the selection mode. The default is MULTIPLE_INTERVAL_SELECTION.
|
106 |
*
|
107 |
* @param selectionMode one of three values: <ul><li>SINGLE_SELECTION
|
108 |
* <li>SINGLE_INTERVAL_SELECTION <li>MULTIPLE_INTERVAL_SELECTION
|
109 |
* </ul>
|
110 |
*
|
111 |
* @exception IllegalArgumentException if <code>selectionMode</code> is not
|
112 |
* one of the legal values shown above
|
113 |
*
|
114 |
* @see #setSelectionMode
|
115 |
*/
|
116 |
public void setSelectionMode(int selectionMode) { |
117 |
switch (selectionMode) {
|
118 |
case SINGLE_SELECTION:
|
119 |
case SINGLE_INTERVAL_SELECTION:
|
120 |
case MULTIPLE_INTERVAL_SELECTION:
|
121 |
this.selectionMode = selectionMode;
|
122 |
|
123 |
break;
|
124 |
|
125 |
default:
|
126 |
throw new IllegalArgumentException("invalid selectionMode"); |
127 |
} |
128 |
} |
129 |
|
130 |
// implements javax.swing.ListSelectionModel
|
131 |
public boolean isSelectedIndex(int index) { |
132 |
// return ((index < minIndex) || (index > maxIndex)) ? false
|
133 |
// : recordset.isSelected(index);
|
134 |
// if (sorted)
|
135 |
if (recordset != null){ |
136 |
return recordset.isSelected(sorterModel.modelIndex(index));
|
137 |
}else{
|
138 |
return false; |
139 |
} |
140 |
} |
141 |
|
142 |
// implements javax.swing.ListSelectionModel
|
143 |
public boolean isSelectionEmpty() { |
144 |
return (minIndex > maxIndex);
|
145 |
} |
146 |
|
147 |
// implements javax.swing.ListSelectionModel
|
148 |
public void addListSelectionListener(ListSelectionListener l) { |
149 |
listenerList.add(ListSelectionListener.class, l);
|
150 |
} |
151 |
|
152 |
// implements javax.swing.ListSelectionModel
|
153 |
public void removeListSelectionListener(ListSelectionListener l) { |
154 |
listenerList.remove(ListSelectionListener.class, l);
|
155 |
} |
156 |
|
157 |
/**
|
158 |
* Returns an array of all the list selection listeners registered on this
|
159 |
* <code>DefaultListSelectionModel</code>.
|
160 |
*
|
161 |
* @return all of this model's <code>ListSelectionListener</code>s or an
|
162 |
* empty array if no list selection listeners are currently
|
163 |
* registered
|
164 |
*
|
165 |
* @see #addListSelectionListener
|
166 |
* @see #removeListSelectionListener
|
167 |
* @since 1.4
|
168 |
*/
|
169 |
public ListSelectionListener[] getListSelectionListeners() { |
170 |
return (ListSelectionListener[]) listenerList.getListeners(ListSelectionListener.class); |
171 |
} |
172 |
|
173 |
/**
|
174 |
* Notifies listeners that we have ended a series of adjustments.
|
175 |
*
|
176 |
* @param isAdjusting DOCUMENT ME!
|
177 |
*/
|
178 |
protected void fireValueChanged(boolean isAdjusting) { |
179 |
if (lastChangedIndex == MIN) {
|
180 |
return;
|
181 |
} |
182 |
|
183 |
/* Change the values before sending the event to the
|
184 |
* listeners in case the event causes a listener to make
|
185 |
* another change to the selection.
|
186 |
*/
|
187 |
int oldFirstChangedIndex = firstChangedIndex;
|
188 |
int oldLastChangedIndex = lastChangedIndex;
|
189 |
firstChangedIndex = MAX; |
190 |
lastChangedIndex = MIN; |
191 |
fireValueChanged(oldFirstChangedIndex, oldLastChangedIndex, isAdjusting); |
192 |
} |
193 |
|
194 |
/**
|
195 |
* Notifies <code>ListSelectionListeners</code> that the value of the
|
196 |
* selection, in the closed interval <code>firstIndex</code>,
|
197 |
* <code>lastIndex</code>, has changed.
|
198 |
*
|
199 |
* @param firstIndex DOCUMENT ME!
|
200 |
* @param lastIndex DOCUMENT ME!
|
201 |
*/
|
202 |
protected void fireValueChanged(int firstIndex, int lastIndex) { |
203 |
fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting()); |
204 |
} |
205 |
|
206 |
/**
|
207 |
* DOCUMENT ME!
|
208 |
*
|
209 |
* @param firstIndex the first index in the interval
|
210 |
* @param lastIndex the last index in the interval
|
211 |
* @param isAdjusting true if this is the final change in a series of
|
212 |
* adjustments
|
213 |
*
|
214 |
* @see EventListenerList
|
215 |
*/
|
216 |
protected void fireValueChanged(int firstIndex, int lastIndex, |
217 |
boolean isAdjusting) {
|
218 |
Object[] listeners = listenerList.getListenerList(); |
219 |
ListSelectionEvent e = null; |
220 |
|
221 |
for (int i = listeners.length - 2; i >= 0; i -= 2) { |
222 |
if (listeners[i] == ListSelectionListener.class) { |
223 |
if (e == null) { |
224 |
e = new ListSelectionEvent(this, firstIndex, lastIndex, |
225 |
isAdjusting); |
226 |
} |
227 |
|
228 |
((ListSelectionListener) listeners[i + 1]).valueChanged(e); |
229 |
} |
230 |
} |
231 |
} |
232 |
|
233 |
/**
|
234 |
* DOCUMENT ME!
|
235 |
*/
|
236 |
private void fireValueChanged() { |
237 |
if (lastAdjustedIndex == MIN) {
|
238 |
return;
|
239 |
} |
240 |
|
241 |
/* If getValueAdjusting() is true, (eg. during a drag opereration)
|
242 |
* record the bounds of the changes so that, when the drag finishes (and
|
243 |
* setValueAdjusting(false) is called) we can post a single event
|
244 |
* with bounds covering all of these individual adjustments.
|
245 |
*/
|
246 |
if (getValueIsAdjusting()) {
|
247 |
firstChangedIndex = Math.min(firstChangedIndex, firstAdjustedIndex);
|
248 |
lastChangedIndex = Math.max(lastChangedIndex, lastAdjustedIndex);
|
249 |
} |
250 |
|
251 |
/* Change the values before sending the event to the
|
252 |
* listeners in case the event causes a listener to make
|
253 |
* another change to the selection.
|
254 |
*/
|
255 |
int oldFirstAdjustedIndex = firstAdjustedIndex;
|
256 |
int oldLastAdjustedIndex = lastAdjustedIndex;
|
257 |
firstAdjustedIndex = MAX; |
258 |
lastAdjustedIndex = MIN; |
259 |
|
260 |
fireValueChanged(oldFirstAdjustedIndex, oldLastAdjustedIndex); |
261 |
} |
262 |
|
263 |
/**
|
264 |
* Returns an array of all the objects currently registered as
|
265 |
* <code><em>Foo</em>Listener</code>s upon this model.
|
266 |
* <code><em>Foo</em>Listener</code>s are registered using the
|
267 |
* <code>add<em>Foo</em>Listener</code> method.
|
268 |
*
|
269 |
* <p>
|
270 |
* You can specify the <code>listenerType</code> argument with a class
|
271 |
* literal, such as <code><em>Foo</em>Listener.class</code>. For example,
|
272 |
* you can query a <code>DefaultListSelectionModel</code> instance
|
273 |
* <code>m</code> for its list selection listeners with the following
|
274 |
* code:
|
275 |
* <pre>ListSelectionListener[] lsls = (ListSelectionListener[])(m.getListeners(ListSelectionListener.class));</pre>
|
276 |
* If no such listeners exist, this method returns an empty array.
|
277 |
* </p>
|
278 |
*
|
279 |
* @param listenerType the type of listeners requested; this parameter
|
280 |
* should specify an interface that descends from
|
281 |
* <code>java.util.EventListener</code>
|
282 |
*
|
283 |
* @return an array of all objects registered as
|
284 |
* <code><em>Foo</em>Listener</code>s on this model, or an empty
|
285 |
* array if no such listeners have been added
|
286 |
*
|
287 |
* @see #getListSelectionListeners
|
288 |
* @since 1.3
|
289 |
*/
|
290 |
public EventListener[] getListeners(Class listenerType) { |
291 |
return listenerList.getListeners(listenerType);
|
292 |
} |
293 |
|
294 |
// Updates first and last change indices
|
295 |
private void markAsDirty(int r) { |
296 |
firstAdjustedIndex = Math.min(firstAdjustedIndex, r);
|
297 |
lastAdjustedIndex = Math.max(lastAdjustedIndex, r);
|
298 |
} |
299 |
|
300 |
// Sets the state at this index and update all relevant state.
|
301 |
private void set(int r) { |
302 |
r = sorterModel.modelIndex(r); |
303 |
/*
|
304 |
if (recordset.isSelected(r)) {
|
305 |
return;
|
306 |
}
|
307 |
*/
|
308 |
|
309 |
if (recordset == null) return; |
310 |
recordset.setSelected(r, true, true); |
311 |
markAsDirty(r); |
312 |
|
313 |
// Update minimum and maximum indices
|
314 |
minIndex = Math.min(minIndex, r);
|
315 |
maxIndex = Math.max(maxIndex, r);
|
316 |
|
317 |
} |
318 |
|
319 |
// Clears the state at this index and update all relevant state.
|
320 |
private void clear(int r) { |
321 |
r = sorterModel.modelIndex(r); |
322 |
if (recordset == null) return; |
323 |
recordset.setSelected(r, false, true); |
324 |
markAsDirty(r); |
325 |
|
326 |
// Update minimum and maximum indices
|
327 |
|
328 |
/*
|
329 |
If (r > minIndex) the minimum has not changed.
|
330 |
The case (r < minIndex) is not possible because r'th value was set.
|
331 |
We only need to check for the case when lowest entry has been cleared,
|
332 |
and in this case we need to search for the first value set above it.
|
333 |
*/
|
334 |
if (r == minIndex) {
|
335 |
for (minIndex = minIndex + 1; minIndex <= maxIndex; minIndex++) { |
336 |
if (recordset.isSelected(minIndex)) {
|
337 |
break;
|
338 |
} |
339 |
} |
340 |
} |
341 |
|
342 |
/*
|
343 |
If (r < maxIndex) the maximum has not changed.
|
344 |
The case (r > maxIndex) is not possible because r'th value was set.
|
345 |
We only need to check for the case when highest entry has been cleared,
|
346 |
and in this case we need to search for the first value set below it.
|
347 |
*/
|
348 |
if (r == maxIndex) {
|
349 |
for (maxIndex = maxIndex - 1; minIndex <= maxIndex; maxIndex--) { |
350 |
if (recordset.isSelected(maxIndex)) {
|
351 |
break;
|
352 |
} |
353 |
} |
354 |
} |
355 |
|
356 |
/* Performance note: This method is called from inside a loop in
|
357 |
changeSelection() but we will only iterate in the loops
|
358 |
above on the basis of one iteration per deselected cell - in total.
|
359 |
Ie. the next time this method is called the work of the previous
|
360 |
deselection will not be repeated.
|
361 |
|
362 |
We also don't need to worry about the case when the min and max
|
363 |
values are in their unassigned states. This cannot happen because
|
364 |
this method's initial check ensures that the selection was not empty
|
365 |
and therefore that the minIndex and maxIndex had 'real' values.
|
366 |
|
367 |
If we have cleared the whole selection, set the minIndex and maxIndex
|
368 |
to their cannonical values so that the next set command always works
|
369 |
just by using Math.min and Math.max.
|
370 |
*/
|
371 |
if (isSelectionEmpty()) {
|
372 |
minIndex = MAX; |
373 |
maxIndex = MIN; |
374 |
} |
375 |
} |
376 |
|
377 |
/**
|
378 |
* Sets the value of the leadAnchorNotificationEnabled flag.
|
379 |
*
|
380 |
* @see #isLeadAnchorNotificationEnabled()
|
381 |
*/
|
382 |
public void setLeadAnchorNotificationEnabled(boolean flag) { |
383 |
leadAnchorNotificationEnabled = flag; |
384 |
} |
385 |
|
386 |
/**
|
387 |
* Returns the value of the <code>leadAnchorNotificationEnabled</code>
|
388 |
* flag. When <code>leadAnchorNotificationEnabled</code> is true the model
|
389 |
* generates notification events with bounds that cover all the changes to
|
390 |
* the selection plus the changes to the lead and anchor indices. Setting
|
391 |
* the flag to false causes a narrowing of the event's bounds to include
|
392 |
* only the elements that have been selected or deselected since the last
|
393 |
* change. Either way, the model continues to maintain the lead and anchor
|
394 |
* variables internally. The default is true.
|
395 |
*
|
396 |
* @return the value of the <code>leadAnchorNotificationEnabled</code> flag
|
397 |
*
|
398 |
* @see #setLeadAnchorNotificationEnabled(boolean)
|
399 |
*/
|
400 |
public boolean isLeadAnchorNotificationEnabled() { |
401 |
return leadAnchorNotificationEnabled;
|
402 |
} |
403 |
|
404 |
/**
|
405 |
* DOCUMENT ME!
|
406 |
*
|
407 |
* @param anchorIndex DOCUMENT ME!
|
408 |
* @param leadIndex DOCUMENT ME!
|
409 |
*/
|
410 |
private void updateLeadAnchorIndices(int anchorIndex, int leadIndex) { |
411 |
if (leadAnchorNotificationEnabled) {
|
412 |
if (this.anchorIndex != anchorIndex) { |
413 |
if (this.anchorIndex != -1) { // The unassigned state. |
414 |
markAsDirty(this.anchorIndex);
|
415 |
} |
416 |
|
417 |
markAsDirty(anchorIndex); |
418 |
} |
419 |
|
420 |
if (this.leadIndex != leadIndex) { |
421 |
if (this.leadIndex != -1) { // The unassigned state. |
422 |
markAsDirty(this.leadIndex);
|
423 |
} |
424 |
|
425 |
markAsDirty(leadIndex); |
426 |
} |
427 |
} |
428 |
|
429 |
this.anchorIndex = anchorIndex;
|
430 |
this.leadIndex = leadIndex;
|
431 |
} |
432 |
|
433 |
/**
|
434 |
* DOCUMENT ME!
|
435 |
*
|
436 |
* @param a DOCUMENT ME!
|
437 |
* @param b DOCUMENT ME!
|
438 |
* @param i DOCUMENT ME!
|
439 |
*
|
440 |
* @return DOCUMENT ME!
|
441 |
*/
|
442 |
private boolean contains(int a, int b, int i) { |
443 |
return (i >= a) && (i <= b);
|
444 |
} |
445 |
|
446 |
/**
|
447 |
* DOCUMENT ME!
|
448 |
*
|
449 |
* @param clearMin DOCUMENT ME!
|
450 |
* @param clearMax DOCUMENT ME!
|
451 |
* @param setMin DOCUMENT ME!
|
452 |
* @param setMax DOCUMENT ME!
|
453 |
* @param clearFirst DOCUMENT ME!
|
454 |
*/
|
455 |
private void changeSelection(int clearMin, int clearMax, int setMin, |
456 |
int setMax, boolean clearFirst) { |
457 |
for (int i = Math.min(setMin, clearMin); |
458 |
i <= Math.max(setMax, clearMax); i++) {
|
459 |
boolean shouldClear = contains(clearMin, clearMax, i);
|
460 |
boolean shouldSet = contains(setMin, setMax, i);
|
461 |
|
462 |
if (shouldSet && shouldClear) {
|
463 |
if (clearFirst) {
|
464 |
shouldClear = false;
|
465 |
} else {
|
466 |
shouldSet = false;
|
467 |
} |
468 |
} |
469 |
|
470 |
if (shouldSet) {
|
471 |
set(i); |
472 |
} |
473 |
|
474 |
if (shouldClear) {
|
475 |
clear(i); |
476 |
} |
477 |
} |
478 |
|
479 |
fireValueChanged(); |
480 |
} |
481 |
|
482 |
/**
|
483 |
* Change the selection with the effect of first clearing the values in the
|
484 |
* inclusive range [clearMin, clearMax] then setting the values in the
|
485 |
* inclusive range [setMin, setMax]. Do this in one pass so that no values
|
486 |
* are cleared if they would later be set.
|
487 |
*
|
488 |
* @param clearMin DOCUMENT ME!
|
489 |
* @param clearMax DOCUMENT ME!
|
490 |
* @param setMin DOCUMENT ME!
|
491 |
* @param setMax DOCUMENT ME!
|
492 |
*/
|
493 |
private void changeSelection(int clearMin, int clearMax, int setMin, |
494 |
int setMax) {
|
495 |
changeSelection(clearMin, clearMax, setMin, setMax, true);
|
496 |
} |
497 |
|
498 |
// implements javax.swing.ListSelectionModel
|
499 |
public void clearSelection() { |
500 |
removeSelectionInterval(minIndex, maxIndex); |
501 |
} |
502 |
|
503 |
// implements javax.swing.ListSelectionModel
|
504 |
public void setSelectionInterval(int index0, int index1) { |
505 |
if ((index0 == -1) || (index1 == -1)) { |
506 |
return;
|
507 |
} |
508 |
|
509 |
if (getSelectionMode() == SINGLE_SELECTION) {
|
510 |
index0 = index1; |
511 |
} |
512 |
|
513 |
updateLeadAnchorIndices(index0, index1); |
514 |
|
515 |
int clearMin = 0; //minIndex; |
516 |
int clearMax = recordset.getRecordsCount() - 1; //maxIndex; |
517 |
logger.debug(Utilities.getMessage(this,"Se_va_a_tener_en_cuenta_para_borrar_los_registros_desde")+" " + minIndex + " "+Utilities.getMessage(this,"hasta")+" " + maxIndex); |
518 |
int setMin = Math.min(index0, index1); |
519 |
int setMax = Math.max(index0, index1); |
520 |
changeSelection(clearMin, clearMax, setMin, setMax); |
521 |
} |
522 |
|
523 |
// implements javax.swing.ListSelectionModel
|
524 |
public void addSelectionInterval(int index0, int index1) { |
525 |
if ((index0 == -1) || (index1 == -1)) { |
526 |
return;
|
527 |
} |
528 |
|
529 |
if (getSelectionMode() != MULTIPLE_INTERVAL_SELECTION) {
|
530 |
setSelectionInterval(index0, index1); |
531 |
|
532 |
return;
|
533 |
} |
534 |
|
535 |
updateLeadAnchorIndices(index0, index1); |
536 |
|
537 |
int clearMin = MAX;
|
538 |
int clearMax = MIN;
|
539 |
int setMin = Math.min(index0, index1); |
540 |
int setMax = Math.max(index0, index1); |
541 |
changeSelection(clearMin, clearMax, setMin, setMax); |
542 |
} |
543 |
|
544 |
// implements javax.swing.ListSelectionModel
|
545 |
public void removeSelectionInterval(int index0, int index1) { |
546 |
if ((index0 == -1) || (index1 == -1)) { |
547 |
return;
|
548 |
} |
549 |
|
550 |
updateLeadAnchorIndices(index0, index1); |
551 |
|
552 |
int clearMin = Math.min(index0, index1); |
553 |
int clearMax = Math.max(index0, index1); |
554 |
int setMin = MAX;
|
555 |
int setMax = MIN;
|
556 |
|
557 |
// If the removal would produce to two disjoint selections in a mode
|
558 |
// that only allows one, extend the removal to the end of the selection.
|
559 |
if ((getSelectionMode() != MULTIPLE_INTERVAL_SELECTION) &&
|
560 |
(clearMin > minIndex) && (clearMax < maxIndex)) { |
561 |
clearMax = maxIndex; |
562 |
} |
563 |
|
564 |
changeSelection(clearMin, clearMax, setMin, setMax); |
565 |
} |
566 |
|
567 |
/**
|
568 |
* DOCUMENT ME!
|
569 |
*
|
570 |
* @param index DOCUMENT ME!
|
571 |
* @param state DOCUMENT ME!
|
572 |
*/
|
573 |
private void setState(int index, boolean state) { |
574 |
if (state) {
|
575 |
set(index); |
576 |
} else {
|
577 |
clear(index); |
578 |
} |
579 |
} |
580 |
|
581 |
/**
|
582 |
* Insert length indices beginning before/after index. If the value at
|
583 |
* index is itself selected and the selection mode is not
|
584 |
* SINGLE_SELECTION, set all of the newly inserted items as selected.
|
585 |
* Otherwise leave them unselected. This method is typically called to
|
586 |
* sync the selection model with a corresponding change in the data model.
|
587 |
*
|
588 |
* @param index DOCUMENT ME!
|
589 |
* @param length DOCUMENT ME!
|
590 |
* @param before DOCUMENT ME!
|
591 |
*/
|
592 |
public void insertIndexInterval(int index, int length, boolean before) { |
593 |
if (recordset == null) return; |
594 |
|
595 |
/* The first new index will appear at insMinIndex and the last
|
596 |
* one will appear at insMaxIndex
|
597 |
*/
|
598 |
int insMinIndex = (before) ? index : (index + 1); |
599 |
int insMaxIndex = (insMinIndex + length) - 1; |
600 |
|
601 |
/* Right shift the entire bitset by length, beginning with
|
602 |
* index-1 if before is true, index+1 if it's false (i.e. with
|
603 |
* insMinIndex).
|
604 |
*/
|
605 |
for (int i = maxIndex; i >= insMinIndex; i--) { |
606 |
setState(i + length, recordset.isSelected(sorterModel.modelIndex(i))); |
607 |
} |
608 |
|
609 |
/* Initialize the newly inserted indices.
|
610 |
*/
|
611 |
boolean setInsertedValues = ((getSelectionMode() == SINGLE_SELECTION)
|
612 |
? false : recordset.isSelected(index));
|
613 |
|
614 |
for (int i = insMinIndex; i <= insMaxIndex; i++) { |
615 |
setState(i, setInsertedValues); |
616 |
} |
617 |
|
618 |
fireValueChanged(); |
619 |
} |
620 |
|
621 |
/**
|
622 |
* Remove the indices in the interval index0,index1 (inclusive) from the
|
623 |
* selection model. This is typically called to sync the selection model
|
624 |
* width a corresponding change in the data model. Note that (as always)
|
625 |
* index0 need not be
|
626 |
*
|
627 |
* @param index0 DOCUMENT ME!
|
628 |
* @param index1 DOCUMENT ME!
|
629 |
*/
|
630 |
public void removeIndexInterval(int index0, int index1) { |
631 |
if (recordset == null) return; |
632 |
|
633 |
int rmMinIndex = Math.min(index0, index1); |
634 |
int rmMaxIndex = Math.max(index0, index1); |
635 |
int gapLength = (rmMaxIndex - rmMinIndex) + 1; |
636 |
|
637 |
/* Shift the entire bitset to the left to close the index0, index1
|
638 |
* gap.
|
639 |
*/
|
640 |
for (int i = rmMinIndex; i <= maxIndex; i++) { |
641 |
setState(i, recordset.isSelected(i + gapLength)); |
642 |
} |
643 |
|
644 |
fireValueChanged(); |
645 |
} |
646 |
|
647 |
// implements javax.swing.ListSelectionModel
|
648 |
public void setValueIsAdjusting(boolean isAdjusting) { |
649 |
if (isAdjusting != this.isAdjusting) { |
650 |
this.isAdjusting = isAdjusting;
|
651 |
this.fireValueChanged(isAdjusting);
|
652 |
if (!isAdjusting){
|
653 |
recordset.setSelected(0, recordset.isSelected(0), false); |
654 |
} |
655 |
} |
656 |
} |
657 |
|
658 |
/**
|
659 |
* Returns a string that displays and identifies this object's properties.
|
660 |
*
|
661 |
* @return a <code>String</code> representation of this object
|
662 |
*/
|
663 |
public String toString() { |
664 |
|
665 |
if (recordset == null) return "No hay recordset todav?a"; |
666 |
|
667 |
String s = ((getValueIsAdjusting()) ? "~" : "=") + |
668 |
recordset.toString(); |
669 |
|
670 |
return getClass().getName() + " " + Integer.toString(hashCode()) + " " + |
671 |
s; |
672 |
} |
673 |
|
674 |
/**
|
675 |
* Returns a clone of this selection model with the same selection.
|
676 |
* <code>listenerLists</code> are not duplicated.
|
677 |
*
|
678 |
* @return DOCUMENT ME!
|
679 |
*
|
680 |
* @exception CloneNotSupportedException if the selection model does not
|
681 |
* both (a) implement the Cloneable interface and (b) define a
|
682 |
* <code>clone</code> method.
|
683 |
*/
|
684 |
public Object clone() throws CloneNotSupportedException { |
685 |
throw new CloneNotSupportedException(); |
686 |
} |
687 |
|
688 |
// implements javax.swing.ListSelectionModel
|
689 |
public int getAnchorSelectionIndex() { |
690 |
return anchorIndex;
|
691 |
} |
692 |
|
693 |
// implements javax.swing.ListSelectionModel
|
694 |
public int getLeadSelectionIndex() { |
695 |
return leadIndex;
|
696 |
} |
697 |
|
698 |
/**
|
699 |
* Set the anchor selection index, leaving all selection values unchanged.
|
700 |
* If leadAnchorNotificationEnabled is true, send a notification covering
|
701 |
* the old and new anchor cells.
|
702 |
*
|
703 |
* @param anchorIndex DOCUMENT ME!
|
704 |
*
|
705 |
* @see #getAnchorSelectionIndex
|
706 |
* @see #setLeadSelectionIndex
|
707 |
*/
|
708 |
public void setAnchorSelectionIndex(int anchorIndex) { |
709 |
anchorIndex = sorterModel.modelIndex(anchorIndex); |
710 |
updateLeadAnchorIndices(anchorIndex, this.leadIndex);
|
711 |
this.anchorIndex = anchorIndex;
|
712 |
fireValueChanged(); |
713 |
} |
714 |
|
715 |
/**
|
716 |
* Sets the lead selection index, ensuring that values between the anchor
|
717 |
* and the new lead are either all selected or all deselected. If the
|
718 |
* value at the anchor index is selected, first clear all the values in
|
719 |
* the range [anchor, oldLeadIndex], then select all the values values in
|
720 |
* the range [anchor, newLeadIndex], where oldLeadIndex is the old
|
721 |
* leadIndex and newLeadIndex is the new one.
|
722 |
*
|
723 |
* <p>
|
724 |
* If the value at the anchor index is not selected, do the same thing in
|
725 |
* reverse selecting values in the old range and deslecting values in the
|
726 |
* new one.
|
727 |
* </p>
|
728 |
*
|
729 |
* <p>
|
730 |
* Generate a single event for this change and notify all listeners. For
|
731 |
* the purposes of generating minimal bounds in this event, do the
|
732 |
* operation in a single pass; that way the first and last index inside
|
733 |
* the ListSelectionEvent that is broadcast will refer to cells that
|
734 |
* actually changed value because of this method. If, instead, this
|
735 |
* operation were done in two steps the effect on the selection state
|
736 |
* would be the same but two events would be generated and the bounds
|
737 |
* around the changed values would be wider, including cells that had
|
738 |
* been first cleared only to later be set.
|
739 |
* </p>
|
740 |
*
|
741 |
* <p>
|
742 |
* This method can be used in the <code>mouseDragged</code> method of a UI
|
743 |
* class to extend a selection.
|
744 |
* </p>
|
745 |
*
|
746 |
* @param leadIndex DOCUMENT ME!
|
747 |
*
|
748 |
* @see #getLeadSelectionIndex
|
749 |
* @see #setAnchorSelectionIndex
|
750 |
*/
|
751 |
public void setLeadSelectionIndex(int leadIndex) { |
752 |
leadIndex = sorterModel.modelIndex(leadIndex); |
753 |
if (recordset == null) return; |
754 |
int anchorIndex = this.anchorIndex; |
755 |
|
756 |
if ((anchorIndex == -1) || (leadIndex == -1)) { |
757 |
return;
|
758 |
} |
759 |
|
760 |
if (this.leadIndex == -1) { |
761 |
this.leadIndex = leadIndex;
|
762 |
} |
763 |
|
764 |
boolean shouldSelect = recordset.isSelected(this.anchorIndex); |
765 |
|
766 |
if (getSelectionMode() == SINGLE_SELECTION) {
|
767 |
anchorIndex = leadIndex; |
768 |
shouldSelect = true;
|
769 |
} |
770 |
|
771 |
int oldMin = Math.min(this.anchorIndex, this.leadIndex); |
772 |
int oldMax = Math.max(this.anchorIndex, this.leadIndex); |
773 |
int newMin = Math.min(anchorIndex, leadIndex); |
774 |
int newMax = Math.max(anchorIndex, leadIndex); |
775 |
|
776 |
updateLeadAnchorIndices(anchorIndex, leadIndex); |
777 |
|
778 |
if (shouldSelect) {
|
779 |
changeSelection(oldMin, oldMax, newMin, newMax); |
780 |
} else {
|
781 |
changeSelection(newMin, newMax, oldMin, oldMax, false);
|
782 |
} |
783 |
} |
784 |
|
785 |
/**
|
786 |
* @see com.iver.cit.gvsig.fmap.SelectionListener#selectionChanged(com.iver.cit.gvsig.fmap.SelectionEvent)
|
787 |
*/
|
788 |
public void selectionChanged(SelectionEvent e) { |
789 |
int min = sorterModel.modelIndex(e.getFromIndex());
|
790 |
if (min < minIndex) minIndex = min;
|
791 |
int max = sorterModel.modelIndex(e.getToIndex());
|
792 |
if (max < maxIndex) maxIndex = max;
|
793 |
fireValueChanged(minIndex, maxIndex); |
794 |
} |
795 |
/**
|
796 |
* @param sorter
|
797 |
*/
|
798 |
public void setSorterModel(TableSorter sorter) { |
799 |
sorterModel = sorter; |
800 |
} |
801 |
|
802 |
} |