OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [javax/] [swing/] [tree/] [DefaultTreeSelectionModel.java] - Blame information for rev 772

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 772 jeremybenn
/* DefaultTreeSelectionModel.java
2
   Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package javax.swing.tree;
40
 
41
import gnu.java.lang.CPStringBuilder;
42
 
43
import java.beans.PropertyChangeListener;
44
import java.io.IOException;
45
import java.io.ObjectInputStream;
46
import java.io.ObjectOutputStream;
47
import java.io.Serializable;
48
import java.util.Arrays;
49
import java.util.BitSet;
50
import java.util.EventListener;
51
import java.util.HashSet;
52
import java.util.Iterator;
53
import java.util.Vector;
54
 
55
import javax.swing.DefaultListSelectionModel;
56
import javax.swing.event.EventListenerList;
57
import javax.swing.event.SwingPropertyChangeSupport;
58
import javax.swing.event.TreeSelectionEvent;
59
import javax.swing.event.TreeSelectionListener;
60
 
61
/**
62
 * The implementation of the default tree selection model. The installed
63
 * listeners are notified about the path and not the row changes. If you
64
 * specifically need to track the row changes, register the listener for the
65
 * expansion events.
66
 *
67
 * @author Andrew Selkirk
68
 * @author Audrius Meskauskas
69
 */
70
public class DefaultTreeSelectionModel
71
    implements Cloneable, Serializable, TreeSelectionModel
72
{
73
 
74
  /**
75
   * According to the API docs, the method
76
   * {@link DefaultTreeSelectionModel#notifyPathChange} should
77
   * expect instances of a class PathPlaceHolder in the Vector parameter.
78
   * This seems to be a non-public class, so I can only make guesses about the
79
   * use of it.
80
   */
81
  private static class PathPlaceHolder
82
  {
83
    /**
84
     * The path that we wrap.
85
     */
86
    TreePath path;
87
 
88
    /**
89
     * Indicates if the path is new or already in the selection.
90
     */
91
    boolean isNew;
92
 
93
    /**
94
     * Creates a new instance.
95
     *
96
     * @param p the path to wrap
97
     * @param n if the path is new or already in the selection
98
     */
99
    PathPlaceHolder(TreePath p, boolean n)
100
    {
101
      path = p;
102
      isNew = n;
103
    }
104
  }
105
 
106
  /**
107
   * Use serialVersionUID for interoperability.
108
   */
109
  static final long serialVersionUID = 3288129636638950196L;
110
 
111
  /**
112
   * The name of the selection mode property.
113
   */
114
  public static final String SELECTION_MODE_PROPERTY = "selectionMode";
115
 
116
  /**
117
   * Our Swing property change support.
118
   */
119
  protected SwingPropertyChangeSupport changeSupport;
120
 
121
  /**
122
   * The current selection.
123
   */
124
  protected TreePath[] selection;
125
 
126
  /**
127
   * Our TreeSelectionListeners.
128
   */
129
  protected EventListenerList listenerList;
130
 
131
  /**
132
   * The current RowMapper.
133
   */
134
  protected transient RowMapper rowMapper;
135
 
136
  /**
137
   * The current listSelectionModel.
138
   */
139
  protected DefaultListSelectionModel listSelectionModel;
140
 
141
  /**
142
   * The current selection mode.
143
   */
144
  protected int selectionMode;
145
 
146
  /**
147
   * The path that has been added last.
148
   */
149
  protected TreePath leadPath;
150
 
151
  /**
152
   * The index of the last added path.
153
   */
154
  protected int leadIndex;
155
 
156
  /**
157
   * The row of the last added path according to the RowMapper.
158
   */
159
  protected int leadRow = -1;
160
 
161
  /**
162
   * A supporting datastructure that is used in addSelectionPaths() and
163
   * removeSelectionPaths(). It contains currently selected paths.
164
   *
165
   * @see #addSelectionPaths(TreePath[])
166
   * @see #removeSelectionPaths(TreePath[])
167
   * @see #setSelectionPaths(TreePath[])
168
   */
169
  private transient HashSet<TreePath> selectedPaths;
170
 
171
  /**
172
   * A supporting datastructure that is used in addSelectionPaths() and
173
   * removeSelectionPaths(). It contains the paths that are added or removed.
174
   *
175
   * @see #addSelectionPaths(TreePath[])
176
   * @see #removeSelectionPaths(TreePath[])
177
   * @see #setSelectionPaths(TreePath[])
178
   */
179
  private transient HashSet<TreePath> tmpPaths;
180
 
181
  /**
182
   * Constructs a new DefaultTreeSelectionModel.
183
   */
184
  public DefaultTreeSelectionModel()
185
  {
186
    setSelectionMode(DISCONTIGUOUS_TREE_SELECTION);
187
    listSelectionModel = new DefaultListSelectionModel();
188
    listenerList = new EventListenerList();
189
    leadIndex = -1;
190
    tmpPaths = new HashSet<TreePath>();
191
    selectedPaths = new HashSet<TreePath>();
192
  }
193
 
194
  /**
195
   * Creates a clone of this DefaultTreeSelectionModel with the same selection.
196
   * The cloned instance will have the same registered listeners, the listeners
197
   * themselves will not be cloned. The selection will be cloned.
198
   *
199
   * @exception CloneNotSupportedException should not be thrown here
200
   * @return a copy of this DefaultTreeSelectionModel
201
   */
202
  public Object clone() throws CloneNotSupportedException
203
  {
204
    DefaultTreeSelectionModel cloned =
205
      (DefaultTreeSelectionModel) super.clone();
206
    cloned.changeSupport = null;
207
    cloned.selection = (TreePath[]) selection.clone();
208
    cloned.listenerList = new EventListenerList();
209
    cloned.listSelectionModel =
210
      (DefaultListSelectionModel) listSelectionModel.clone();
211
    cloned.selectedPaths = new HashSet<TreePath>();
212
    cloned.tmpPaths = new HashSet<TreePath>();
213
 
214
    return cloned;
215
  }
216
 
217
  /**
218
   * Returns a string that shows this object's properties.
219
   * The returned string lists the selected tree rows, if any.
220
   *
221
   * @return a string that shows this object's properties
222
   */
223
  public String toString()
224
  {
225
    if (isSelectionEmpty())
226
      return "[selection empty]";
227
    else
228
      {
229
        CPStringBuilder b = new CPStringBuilder("selected rows: [");
230
        for (int i = 0; i < selection.length; i++)
231
          {
232
            b.append(getRow(selection[i]));
233
            b.append(' ');
234
          }
235
        b.append(", lead " + getLeadSelectionRow());
236
        return b.toString();
237
      }
238
  }
239
 
240
  /**
241
   * writeObject
242
   *
243
   * @param value0 TODO
244
   * @exception IOException TODO
245
   */
246
  private void writeObject(ObjectOutputStream value0) throws IOException
247
  {
248
    // TODO
249
  }
250
 
251
  /**
252
   * readObject
253
   *
254
   * @param value0 TODO
255
   * @exception IOException TODO
256
   * @exception ClassNotFoundException TODO
257
   */
258
  private void readObject(ObjectInputStream value0) throws IOException,
259
      ClassNotFoundException
260
  {
261
    // TODO
262
  }
263
 
264
  /**
265
   * Sets the RowMapper that should be used to map between paths and their rows.
266
   *
267
   * @param mapper the RowMapper to set
268
   * @see RowMapper
269
   */
270
  public void setRowMapper(RowMapper mapper)
271
  {
272
    rowMapper = mapper;
273
    resetRowSelection();
274
  }
275
 
276
  /**
277
   * Returns the RowMapper that is currently used to map between paths and their
278
   * rows.
279
   *
280
   * @return the current RowMapper
281
   * @see RowMapper
282
   */
283
  public RowMapper getRowMapper()
284
  {
285
    return rowMapper;
286
  }
287
 
288
  /**
289
   * Sets the current selection mode. Possible values are
290
   * {@link #SINGLE_TREE_SELECTION}, {@link #CONTIGUOUS_TREE_SELECTION} and
291
   * {@link #DISCONTIGUOUS_TREE_SELECTION}.
292
   *
293
   * @param mode the selection mode to be set
294
   * @see #getSelectionMode
295
   * @see #SINGLE_TREE_SELECTION
296
   * @see #CONTIGUOUS_TREE_SELECTION
297
   * @see #DISCONTIGUOUS_TREE_SELECTION
298
   */
299
  public void setSelectionMode(int mode)
300
  {
301
    int oldMode = selectionMode;
302
    selectionMode = mode;
303
    // Make sure we have a valid selection mode.
304
    if (selectionMode != SINGLE_TREE_SELECTION
305
        && selectionMode != CONTIGUOUS_TREE_SELECTION
306
        && selectionMode != DISCONTIGUOUS_TREE_SELECTION)
307
      selectionMode = DISCONTIGUOUS_TREE_SELECTION;
308
 
309
    // Fire property change event.
310
    if (oldMode != selectionMode && changeSupport != null)
311
      changeSupport.firePropertyChange(SELECTION_MODE_PROPERTY, oldMode,
312
                                       selectionMode);
313
  }
314
 
315
  /**
316
   * Returns the current selection mode.
317
   *
318
   * @return the current selection mode
319
   * @see #setSelectionMode
320
   * @see #SINGLE_TREE_SELECTION
321
   * @see #CONTIGUOUS_TREE_SELECTION
322
   * @see #DISCONTIGUOUS_TREE_SELECTION
323
   */
324
  public int getSelectionMode()
325
  {
326
    return selectionMode;
327
  }
328
 
329
  /**
330
   * Sets this path as the only selection. If this changes the selection the
331
   * registered TreeSelectionListeners are notified.
332
   *
333
   * @param path the path to set as selection
334
   */
335
  public void setSelectionPath(TreePath path)
336
  {
337
    TreePath[] paths = null;
338
    if (path != null)
339
      paths = new TreePath[]{ path };
340
    setSelectionPaths(paths);
341
  }
342
 
343
  /**
344
   * Get the number of the tree row for the given path.
345
   *
346
   * @param path the tree path
347
   * @return the tree row for this path or -1 if the path is not visible.
348
   */
349
  int getRow(TreePath path)
350
  {
351
    RowMapper mapper = getRowMapper();
352
 
353
    if (mapper instanceof AbstractLayoutCache)
354
      {
355
        // The absolute majority of cases, unless the TreeUI is very
356
        // seriously rewritten
357
        AbstractLayoutCache ama = (AbstractLayoutCache) mapper;
358
        return ama.getRowForPath(path);
359
      }
360
    else if (mapper != null)
361
      {
362
        // Generic non optimized implementation.
363
        int[] rows = mapper.getRowsForPaths(new TreePath[] { path });
364
        if (rows.length == 0)
365
          return - 1;
366
        else
367
          return rows[0];
368
      }
369
    return -1;
370
  }
371
 
372
  /**
373
   * Sets the paths as selection. This method checks for duplicates and removes
374
   * them. If this changes the selection the registered TreeSelectionListeners
375
   * are notified.
376
   *
377
   * @param paths the paths to set as selection
378
   */
379
  public void setSelectionPaths(TreePath[] paths)
380
  {
381
    int oldLength = 0;
382
    if (selection != null)
383
      oldLength = selection.length;
384
    int newLength = 0;
385
    if (paths != null)
386
      newLength = paths.length;
387
    if (newLength > 0 || oldLength > 0)
388
      {
389
        // For SINGLE_TREE_SELECTION and for CONTIGUOUS_TREE_SELECTION with
390
        // a non-contiguous path, we only allow the first path element.
391
        if ((selectionMode == SINGLE_TREE_SELECTION && newLength > 1)
392
            || (selectionMode == CONTIGUOUS_TREE_SELECTION && newLength > 0
393
                && ! arePathsContiguous(paths)))
394
          {
395
            paths = new TreePath[] { paths[0] };
396
            newLength = 1;
397
          }
398
        // Find new paths.
399
        Vector<PathPlaceHolder> changedPaths = null;
400
        tmpPaths.clear();
401
        int validPaths = 0;
402
        TreePath oldLeadPath = leadPath;
403
        for (int i = 0; i < newLength; i++)
404
          {
405
            if (paths[i] != null && ! tmpPaths.contains(paths[i]))
406
              {
407
                validPaths++;
408
                tmpPaths.add(paths[i]);
409
                if (! selectedPaths.contains(paths[i]))
410
                  {
411
                    if (changedPaths == null)
412
                      changedPaths = new Vector<PathPlaceHolder>();
413
                    changedPaths.add(new PathPlaceHolder(paths[i], true));
414
                  }
415
                leadPath = paths[i];
416
              }
417
          }
418
        // Put together the new selection.
419
        TreePath[] newSelection = null;
420
        if (validPaths != 0)
421
          {
422
            if (validPaths != newLength)
423
              {
424
                // Some of the paths are already selected, put together
425
                // the new selection carefully.
426
                newSelection = new TreePath[validPaths];
427
                Iterator<TreePath> newPaths = tmpPaths.iterator();
428
                validPaths = 0;
429
                for (int i = 0; newPaths.hasNext(); i++)
430
                  newSelection[i] = newPaths.next();
431
              }
432
            else
433
              {
434
                newSelection = new TreePath[paths.length];
435
                System.arraycopy(paths, 0, newSelection, 0, paths.length);
436
              }
437
          }
438
 
439
        // Find paths that have been selected, but are no more.
440
        for (int i = 0; i < oldLength; i++)
441
          {
442
            if (selection[i] != null && ! tmpPaths.contains(selection[i]))
443
              {
444
                if (changedPaths == null)
445
                  changedPaths = new Vector<PathPlaceHolder>();
446
                changedPaths.add(new PathPlaceHolder(selection[i], false));
447
              }
448
          }
449
 
450
        // Perform changes and notification.
451
        selection = newSelection;
452
        HashSet<TreePath> tmp = selectedPaths;
453
        selectedPaths = tmpPaths;
454
        tmpPaths = tmp;
455
        tmpPaths.clear();
456
 
457
        // Not necessary, but required according to the specs and to tests.
458
        if (selection != null)
459
          insureUniqueness();
460
        updateLeadIndex();
461
        resetRowSelection();
462
        if (changedPaths != null && changedPaths.size() > 0)
463
          notifyPathChange(changedPaths, oldLeadPath);
464
      }
465
  }
466
 
467
  /**
468
   * Adds a path to the list of selected paths. This method checks if the path
469
   * is already selected and doesn't add the same path twice. If this changes
470
   * the selection the registered TreeSelectionListeners are notified.
471
   *
472
   * The lead path is changed to the added path. This also happen if the
473
   * passed path was already selected before.
474
   *
475
   * @param path the path to add to the selection
476
   */
477
  public void addSelectionPath(TreePath path)
478
  {
479
    if (path != null)
480
      {
481
        TreePath[] add = new TreePath[]{ path };
482
        addSelectionPaths(add);
483
      }
484
  }
485
 
486
  /**
487
   * Adds the paths to the list of selected paths. This method checks if the
488
   * paths are already selected and doesn't add the same path twice. If this
489
   * changes the selection the registered TreeSelectionListeners are notified.
490
   *
491
   * @param paths the paths to add to the selection
492
   */
493
  public void addSelectionPaths(TreePath[] paths)
494
  {
495
    int length = paths != null ? paths.length : 0;
496
    if (length > 0)
497
      {
498
        if (selectionMode == SINGLE_TREE_SELECTION)
499
          setSelectionPaths(paths);
500
        else if (selectionMode == CONTIGUOUS_TREE_SELECTION
501
                 &&  ! canPathsBeAdded(paths))
502
          {
503
            if (arePathsContiguous(paths))
504
              setSelectionPaths(paths);
505
            else
506
              setSelectionPaths(new TreePath[] { paths[0] });
507
          }
508
        else
509
          {
510
            Vector<PathPlaceHolder> changedPaths = null;
511
            tmpPaths.clear();
512
            int validPaths = 0;
513
            TreePath oldLeadPath = leadPath;
514
            int oldPaths = 0;
515
            if (selection != null)
516
              oldPaths = selection.length;
517
            int i;
518
            for (i = 0; i < length; i++)
519
              {
520
                if (paths[i] != null)
521
                  {
522
                    if (! selectedPaths.contains(paths[i]))
523
                      {
524
                        validPaths++;
525
                        if (changedPaths == null)
526
                          changedPaths = new Vector<PathPlaceHolder>();
527
                        changedPaths.add(new PathPlaceHolder(paths[i], true));
528
                        selectedPaths.add(paths[i]);
529
                        tmpPaths.add(paths[i]);
530
                      }
531
                    leadPath = paths[i];
532
                  }
533
              }
534
            if (validPaths > 0)
535
              {
536
                TreePath[] newSelection = new TreePath[oldPaths + validPaths];
537
                if (oldPaths > 0)
538
                  System.arraycopy(selection, 0, newSelection, 0, oldPaths);
539
                if (validPaths != paths.length)
540
                  {
541
                    // Some of the paths are already selected, put together
542
                    // the new selection carefully.
543
                    Iterator<TreePath> newPaths = tmpPaths.iterator();
544
                    i = oldPaths;
545
                    while (newPaths.hasNext())
546
                      {
547
                        newSelection[i] = newPaths.next();
548
                        i++;
549
                      }
550
                  }
551
                else
552
                  System.arraycopy(paths, 0, newSelection, oldPaths,
553
                                   validPaths);
554
                selection = newSelection;
555
                insureUniqueness();
556
                updateLeadIndex();
557
                resetRowSelection();
558
                if (changedPaths != null && changedPaths.size() > 0)
559
                  notifyPathChange(changedPaths, oldLeadPath);
560
              }
561
            else
562
              leadPath = oldLeadPath;
563
            tmpPaths.clear();
564
          }
565
      }
566
  }
567
 
568
  /**
569
   * Removes the path from the selection. If this changes the selection the
570
   * registered TreeSelectionListeners are notified.
571
   *
572
   * @param path the path to remove
573
   */
574
  public void removeSelectionPath(TreePath path)
575
  {
576
    if (path != null)
577
      removeSelectionPaths(new TreePath[]{ path });
578
  }
579
 
580
  /**
581
   * Removes the paths from the selection. If this changes the selection the
582
   * registered TreeSelectionListeners are notified.
583
   *
584
   * @param paths the paths to remove
585
   */
586
  public void removeSelectionPaths(TreePath[] paths)
587
  {
588
    if (paths != null && selection != null && paths.length > 0)
589
      {
590
        if (! canPathsBeRemoved(paths))
591
          clearSelection();
592
        else
593
          {
594
            Vector<PathPlaceHolder> pathsToRemove = null;
595
            for (int i = paths.length - 1; i >= 0; i--)
596
              {
597
                if (paths[i] != null && selectedPaths.contains(paths[i]))
598
                  {
599
                    if (pathsToRemove == null)
600
                      pathsToRemove = new Vector<PathPlaceHolder>();
601
                    selectedPaths.remove(paths[i]);
602
                    pathsToRemove.add(new PathPlaceHolder(paths[i],
603
                                                          false));
604
                  }
605
              }
606
            if (pathsToRemove != null)
607
              {
608
                int numRemove = pathsToRemove.size();
609
                TreePath oldLead = leadPath;
610
                if (numRemove == selection.length)
611
                  selection = null;
612
                else
613
                  {
614
                    selection = new TreePath[selection.length - numRemove];
615
                    Iterator<TreePath> keep = selectedPaths.iterator();
616
                    for (int valid = 0; keep.hasNext(); valid++)
617
                      selection[valid] = keep.next();
618
                  }
619
                // Update lead path.
620
                if (leadPath != null && ! selectedPaths.contains(leadPath))
621
                  {
622
                    if (selection != null)
623
                      leadPath = selection[selection.length - 1];
624
                    else
625
                      leadPath = null;
626
                  }
627
                else if (selection != null)
628
                  leadPath = selection[selection.length - 1];
629
                else
630
                  leadPath = null;
631
                updateLeadIndex();
632
                resetRowSelection();
633
                notifyPathChange(pathsToRemove, oldLead);
634
              }
635
          }
636
      }
637
  }
638
 
639
  /**
640
   * Returns the first path in the selection. This is especially useful when the
641
   * selectionMode is {@link #SINGLE_TREE_SELECTION}.
642
   *
643
   * @return the first path in the selection
644
   */
645
  public TreePath getSelectionPath()
646
  {
647
    if ((selection == null) || (selection.length == 0))
648
      return null;
649
    else
650
      return selection[0];
651
  }
652
 
653
  /**
654
   * Returns the complete selection.
655
   *
656
   * @return the complete selection
657
   */
658
  public TreePath[] getSelectionPaths()
659
  {
660
    return selection;
661
  }
662
 
663
  /**
664
   * Returns the number of paths in the selection.
665
   *
666
   * @return the number of paths in the selection
667
   */
668
  public int getSelectionCount()
669
  {
670
    if (selection == null)
671
      return 0;
672
    else
673
      return selection.length;
674
  }
675
 
676
  /**
677
   * Checks if a given path is in the selection.
678
   *
679
   * @param path the path to check
680
   * @return <code>true</code> if the path is in the selection,
681
   *         <code>false</code> otherwise
682
   */
683
  public boolean isPathSelected(TreePath path)
684
  {
685
    if (selection == null)
686
      return false;
687
 
688
    for (int i = 0; i < selection.length; i++)
689
      {
690
        if (selection[i].equals(path))
691
          return true;
692
      }
693
    return false;
694
  }
695
 
696
  /**
697
   * Checks if the selection is empty.
698
   *
699
   * @return <code>true</code> if the selection is empty, <code>false</code>
700
   *         otherwise
701
   */
702
  public boolean isSelectionEmpty()
703
  {
704
    return (selection == null) || (selection.length == 0);
705
  }
706
 
707
  /**
708
   * Removes all paths from the selection. Fire the unselection event.
709
   */
710
  public void clearSelection()
711
  {
712
    if (selection != null)
713
      {
714
        int selectionLength = selection.length;
715
        boolean[] news = new boolean[selectionLength];
716
        Arrays.fill(news, false);
717
        TreeSelectionEvent event = new TreeSelectionEvent(this, selection,
718
                                                          news, leadPath,
719
                                                          null);
720
        leadPath = null;
721
        leadIndex = 0;
722
        leadRow = 0;
723
        selectedPaths.clear();
724
        selection = null;
725
        resetRowSelection();
726
        fireValueChanged(event);
727
      }
728
  }
729
 
730
  /**
731
   * Adds a <code>TreeSelectionListener</code> object to this model.
732
   *
733
   * @param listener the listener to add
734
   */
735
  public void addTreeSelectionListener(TreeSelectionListener listener)
736
  {
737
    listenerList.add(TreeSelectionListener.class, listener);
738
  }
739
 
740
  /**
741
   * Removes a <code>TreeSelectionListener</code> object from this model.
742
   *
743
   * @param listener the listener to remove
744
   */
745
  public void removeTreeSelectionListener(TreeSelectionListener listener)
746
  {
747
    listenerList.remove(TreeSelectionListener.class, listener);
748
  }
749
 
750
  /**
751
   * Returns all <code>TreeSelectionListener</code> added to this model.
752
   *
753
   * @return an array of listeners
754
   * @since 1.4
755
   */
756
  public TreeSelectionListener[] getTreeSelectionListeners()
757
  {
758
    return (TreeSelectionListener[]) getListeners(TreeSelectionListener.class);
759
  }
760
 
761
  /**
762
   * fireValueChanged
763
   *
764
   * @param event the event to fire.
765
   */
766
  protected void fireValueChanged(TreeSelectionEvent event)
767
  {
768
    TreeSelectionListener[] listeners = getTreeSelectionListeners();
769
 
770
    for (int i = 0; i < listeners.length; ++i)
771
      listeners[i].valueChanged(event);
772
  }
773
 
774
  /**
775
   * Returns all added listeners of a special type.
776
   *
777
   * @param listenerType the listener type
778
   * @return an array of listeners
779
   * @since 1.3
780
   */
781
  public <T extends EventListener> T[] getListeners(Class<T> listenerType)
782
  {
783
    return listenerList.getListeners(listenerType);
784
  }
785
 
786
  /**
787
   * Returns the currently selected rows.
788
   *
789
   * @return the currently selected rows
790
   */
791
  public int[] getSelectionRows()
792
  {
793
    int[] rows = null;
794
    if (rowMapper != null && selection != null)
795
      {
796
        rows = rowMapper.getRowsForPaths(selection);
797
        if (rows != null)
798
          {
799
            // Find invisible rows.
800
            int invisible = 0;
801
            for (int i = rows.length - 1; i >= 0; i--)
802
              {
803
                if (rows[i] == -1)
804
                  invisible++;
805
 
806
              }
807
            // Clean up invisible rows.
808
            if (invisible > 0)
809
              {
810
                if (invisible == rows.length)
811
                  rows = null;
812
                else
813
                  {
814
                    int[] newRows = new int[rows.length - invisible];
815
                    int visCount = 0;
816
                    for (int i = rows.length - 1; i >= 0; i--)
817
                      {
818
                        if (rows[i] != -1)
819
                          {
820
                            newRows[visCount] = rows[i];
821
                            visCount++;
822
                          }
823
                      }
824
                    rows = newRows;
825
                  }
826
              }
827
          }
828
      }
829
    return rows;
830
  }
831
 
832
  /**
833
   * Returns the smallest row index from the selection.
834
   *
835
   * @return the smallest row index from the selection
836
   */
837
  public int getMinSelectionRow()
838
  {
839
    return listSelectionModel.getMinSelectionIndex();
840
  }
841
 
842
  /**
843
   * Returns the largest row index from the selection.
844
   *
845
   * @return the largest row index from the selection
846
   */
847
  public int getMaxSelectionRow()
848
  {
849
    return listSelectionModel.getMaxSelectionIndex();
850
  }
851
 
852
  /**
853
   * Checks if a particular row is selected.
854
   *
855
   * @param row the index of the row to check
856
   * @return <code>true</code> if the row is in this selection,
857
   *         <code>false</code> otherwise
858
   * @throws NullPointerException if the row mapper is not set (can only happen
859
   *           if the user has plugged in the custom incorrect TreeUI
860
   *           implementation.
861
   */
862
  public boolean isRowSelected(int row)
863
  {
864
    return listSelectionModel.isSelectedIndex(row);
865
  }
866
 
867
  /**
868
   * Updates the mappings from TreePaths to row indices.
869
   */
870
  public void resetRowSelection()
871
  {
872
    listSelectionModel.clearSelection();
873
    if (selection != null && rowMapper != null)
874
      {
875
        int[] rows = rowMapper.getRowsForPaths(selection);
876
        // Update list selection model.
877
        for (int i = 0; i < rows.length; i++)
878
          {
879
            int row = rows[i];
880
            if (row != -1)
881
              listSelectionModel.addSelectionInterval(row, row);
882
          }
883
        // Update lead selection.
884
        if (leadIndex != -1 && rows != null)
885
          leadRow = rows[leadIndex];
886
        else if (leadPath != null)
887
          {
888
            TreePath[] tmp = new TreePath[]{ leadPath };
889
            rows = rowMapper.getRowsForPaths(tmp);
890
            leadRow = rows != null ? rows[0] : -1;
891
          }
892
        else
893
          leadRow = -1;
894
        insureRowContinuity();
895
      }
896
    else
897
      leadRow = -1;
898
  }
899
 
900
  /**
901
   * getLeadSelectionRow
902
   *
903
   * @return int
904
   */
905
  public int getLeadSelectionRow()
906
  {
907
    return leadRow;
908
  }
909
 
910
  /**
911
   * getLeadSelectionPath
912
   *
913
   * @return TreePath
914
   */
915
  public TreePath getLeadSelectionPath()
916
  {
917
    return leadPath;
918
  }
919
 
920
  /**
921
   * Adds a <code>PropertyChangeListener</code> object to this model.
922
   *
923
   * @param listener the listener to add.
924
   */
925
  public void addPropertyChangeListener(PropertyChangeListener listener)
926
  {
927
    if (changeSupport == null)
928
      changeSupport = new SwingPropertyChangeSupport(this);
929
    changeSupport.addPropertyChangeListener(listener);
930
  }
931
 
932
  /**
933
   * Removes a <code>PropertyChangeListener</code> object from this model.
934
   *
935
   * @param listener the listener to remove.
936
   */
937
  public void removePropertyChangeListener(PropertyChangeListener listener)
938
  {
939
    if (changeSupport != null)
940
      changeSupport.removePropertyChangeListener(listener);
941
  }
942
 
943
  /**
944
   * Returns all added <code>PropertyChangeListener</code> objects.
945
   *
946
   * @return an array of listeners.
947
   * @since 1.4
948
   */
949
  public PropertyChangeListener[] getPropertyChangeListeners()
950
  {
951
    PropertyChangeListener[] listeners = null;
952
    if (changeSupport != null)
953
      listeners = changeSupport.getPropertyChangeListeners();
954
    else
955
      listeners = new PropertyChangeListener[0];
956
    return listeners;
957
  }
958
 
959
  /**
960
   * Makes sure the currently selected paths are valid according to the current
961
   * selectionMode. If the selectionMode is set to
962
   * {@link #CONTIGUOUS_TREE_SELECTION} and the selection isn't contiguous then
963
   * the selection is reset to the first set of contguous paths. If the
964
   * selectionMode is set to {@link #SINGLE_TREE_SELECTION} and the selection
965
   * has more than one path, the selection is reset to the contain only the
966
   * first path.
967
   */
968
  protected void insureRowContinuity()
969
  {
970
    if (selectionMode == CONTIGUOUS_TREE_SELECTION && selection != null
971
        && rowMapper != null)
972
      {
973
        int min = listSelectionModel.getMinSelectionIndex();
974
        if (min != -1)
975
          {
976
            int max = listSelectionModel.getMaxSelectionIndex();
977
            for (int i = min; i <= max; i++)
978
              {
979
                if (! listSelectionModel.isSelectedIndex(i))
980
                  {
981
                    if (i == min)
982
                      clearSelection();
983
                    else
984
                      {
985
                        TreePath[] newSelection = new TreePath[i - min];
986
                        int[] rows = rowMapper.getRowsForPaths(selection);
987
                        for (int j = 0; j < rows.length; j++)
988
                          {
989
                            if (rows[j] < i)
990
                              newSelection[rows[j] - min] = selection[j];
991
                          }
992
                        setSelectionPaths(newSelection);
993
                        break;
994
                      }
995
                  }
996
              }
997
          }
998
      }
999
    else if (selectionMode == SINGLE_TREE_SELECTION && selection != null
1000
        && selection.length > 1)
1001
      setSelectionPath(selection[0]);
1002
  }
1003
 
1004
  /**
1005
   * Returns <code>true</code> if the paths are contiguous (take subsequent
1006
   * rows in the diplayed tree view. The method returns <code>true</code> if
1007
   * we have no RowMapper assigned.
1008
   *
1009
   * @param paths the paths to check for continuity
1010
   * @return <code>true</code> if the paths are contiguous or we have no
1011
   *         RowMapper assigned
1012
   */
1013
  protected boolean arePathsContiguous(TreePath[] paths)
1014
  {
1015
    if (rowMapper == null || paths.length < 2)
1016
      return true;
1017
 
1018
    int length = paths.length;
1019
    TreePath[] tmp = new TreePath[1];
1020
    tmp[0] = paths[0];
1021
    int min = rowMapper.getRowsForPaths(tmp)[0];
1022
    BitSet selected = new BitSet();
1023
    int valid = 0;
1024
    for (int i = 0; i < length; i++)
1025
      {
1026
        if (paths[i] != null)
1027
          {
1028
            tmp[0] = paths[i];
1029
            int[] rows = rowMapper.getRowsForPaths(tmp);
1030
            if (rows == null)
1031
              return false; // No row mapping yet, can't be selected.
1032
            int row = rows[0];
1033
            if (row == -1 || row < (min - length) || row > (min + length))
1034
              return false; // Not contiguous.
1035
            min = Math.min(min, row);
1036
            if (! selected.get(row))
1037
              {
1038
                selected.set(row);
1039
                valid++;
1040
              }
1041
 
1042
          }
1043
      }
1044
    int max = valid + min;
1045
    for (int i = min; i < max; i++)
1046
      if (! selected.get(i))
1047
        return false; // Not contiguous.
1048
    return true;
1049
  }
1050
 
1051
  /**
1052
   * Checks if the paths can be added. This returns <code>true</code> if:
1053
   * <ul>
1054
   * <li><code>paths</code> is <code>null</code> or empty</li>
1055
   * <li>we have no RowMapper assigned</li>
1056
   * <li>nothing is currently selected</li>
1057
   * <li>selectionMode is {@link #DISCONTIGUOUS_TREE_SELECTION}</li>
1058
   * <li>adding the paths to the selection still results in a contiguous set of
1059
   * paths</li>
1060
   *
1061
   * @param paths the paths to check
1062
   * @return <code>true</code> if the paths can be added with respect to the
1063
   *         selectionMode
1064
   */
1065
  protected boolean canPathsBeAdded(TreePath[] paths)
1066
  {
1067
    if (paths == null || paths.length == 0 || rowMapper == null
1068
        || selection == null || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
1069
      return true;
1070
 
1071
    BitSet selected = new BitSet();
1072
    int min = listSelectionModel.getMinSelectionIndex();
1073
    int max = listSelectionModel.getMaxSelectionIndex();
1074
    TreePath[] tmp = new TreePath[1];
1075
    if (min != -1)
1076
      {
1077
        // Set the bitmask of selected elements.
1078
        for (int i = min; i <= max; i++)
1079
          selected.set(i);
1080
      }
1081
    else
1082
      {
1083
        tmp[0] = paths[0];
1084
        min = rowMapper.getRowsForPaths(tmp)[0];
1085
        max = min;
1086
      }
1087
    // Mark new paths as selected.
1088
    for (int i = paths.length - 1; i >= 0; i--)
1089
      {
1090
        if (paths[i] != null)
1091
          {
1092
            tmp[0] = paths[i];
1093
            int[] rows = rowMapper.getRowsForPaths(tmp);
1094
            if (rows == null)
1095
              return false; // Now row mapping yet, can't be selected.
1096
            int row = rows[0];
1097
            if (row == -1)
1098
              return false; // Now row mapping yet, can't be selected.
1099
            min = Math.min(min, row);
1100
            max = Math.max(max, row);
1101
            selected.set(row);
1102
          }
1103
      }
1104
    // Now look if the new selection would be contiguous.
1105
    for (int i = min; i <= max; i++)
1106
      if (! selected.get(i))
1107
        return false;
1108
    return true;
1109
  }
1110
 
1111
  /**
1112
   * Checks if the paths can be removed without breaking the continuity of the
1113
   * selection according to selectionMode.
1114
   *
1115
   * @param paths the paths to check
1116
   * @return <code>true</code> if the paths can be removed with respect to the
1117
   *         selectionMode
1118
   */
1119
  protected boolean canPathsBeRemoved(TreePath[] paths)
1120
  {
1121
    if (rowMapper == null || isSelectionEmpty()
1122
        || selectionMode == DISCONTIGUOUS_TREE_SELECTION)
1123
      return true;
1124
 
1125
    HashSet<TreePath> set = new HashSet<TreePath>();
1126
    for (int i = 0; i < selection.length; i++)
1127
      set.add(selection[i]);
1128
 
1129
    for (int i = 0; i < paths.length; i++)
1130
      set.remove(paths[i]);
1131
 
1132
    TreePath[] remaining = new TreePath[set.size()];
1133
    Iterator<TreePath> iter = set.iterator();
1134
 
1135
    for (int i = 0; i < remaining.length; i++)
1136
      remaining[i] = iter.next();
1137
 
1138
    return arePathsContiguous(remaining);
1139
  }
1140
 
1141
  /**
1142
   * Notify the installed listeners that the given patches have changed. This
1143
   * method will call listeners if invoked, but it is not called from the
1144
   * implementation of this class.
1145
   *
1146
   * @param vPaths the vector of the changed patches
1147
   * @param oldLeadSelection the old selection index
1148
   */
1149
  protected void notifyPathChange(Vector<PathPlaceHolder> vPaths,
1150
                                  TreePath oldLeadSelection)
1151
  {
1152
 
1153
    int numChangedPaths = vPaths.size();
1154
    boolean[] news = new boolean[numChangedPaths];
1155
    TreePath[] paths = new TreePath[numChangedPaths];
1156
    for (int i = 0; i < numChangedPaths; i++)
1157
      {
1158
        PathPlaceHolder p = vPaths.get(i);
1159
        news[i] = p.isNew;
1160
        paths[i] = p.path;
1161
      }
1162
 
1163
    TreeSelectionEvent event = new TreeSelectionEvent(this, paths, news,
1164
                                                      oldLeadSelection,
1165
                                                      leadPath);
1166
    fireValueChanged(event);
1167
  }
1168
 
1169
  /**
1170
   * Updates the lead selection row number after changing the lead selection
1171
   * path.
1172
   */
1173
  protected void updateLeadIndex()
1174
  {
1175
    leadIndex = -1;
1176
    if (leadPath != null)
1177
      {
1178
        leadRow = -1;
1179
        if (selection == null)
1180
          leadPath = null;
1181
        else
1182
          {
1183
            for (int i = selection.length - 1; i >= 0 && leadIndex == -1; i--)
1184
              {
1185
                if (selection[i] == leadPath)
1186
                  leadIndex = i;
1187
              }
1188
          }
1189
      }
1190
  }
1191
 
1192
  /**
1193
   * This method exists due historical reasons and returns without action
1194
   * (unless overridden). For compatibility with the applications that override
1195
   * it, it is still called from the {@link #setSelectionPaths(TreePath[])} and
1196
   * {@link #addSelectionPaths(TreePath[])}.
1197
   */
1198
  protected void insureUniqueness()
1199
  {
1200
    // Following the API 1.4, the method should return without action.
1201
  }
1202
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.