Home » openjdk-7 » javax » swing » [javadoc | source]

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

Home » openjdk-7 » javax » swing » [javadoc | source]