Statistics
| Revision:

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
}