GNU Classpath (0.18) | ||
Frames | No Frames |
1: /* DefaultListSelectionModel.java -- 2: Copyright (C) 2002, 2004, 2005 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; 40: 41: import java.io.Serializable; 42: import java.util.BitSet; 43: import java.util.EventListener; 44: 45: import javax.swing.event.EventListenerList; 46: import javax.swing.event.ListSelectionEvent; 47: import javax.swing.event.ListSelectionListener; 48: 49: /** 50: * The default implementation of {@link ListSelectionModel}, 51: * which is used by {@link javax.swing.JList} and 52: * similar classes to manage the selection status of a number of data 53: * elements. 54: * 55: * <p>The class is organized <em>abstractly</em> as a set of intervals of 56: * integers. Each interval indicates an inclusive range of indices in a 57: * list -- held by some other object and unknown to this class -- which is 58: * considered "selected". There are various accessors for querying and 59: * modifying the set of intervals, with simplified forms accepting a single 60: * index, representing an interval with only one element. </p> 61: */ 62: public class DefaultListSelectionModel implements Cloneable, 63: ListSelectionModel, 64: Serializable 65: { 66: private static final long serialVersionUID = -5718799865110415860L; 67: 68: /** The list of ListSelectionListeners subscribed to this selection model. */ 69: protected EventListenerList listenerList = new EventListenerList(); 70: 71: 72: /** 73: * The current list selection mode. Must be one of the numeric constants 74: * <code>SINGLE_SELECTION</code>, <code>SINGLE_INTERVAL_SELECTION</code> 75: * or <code>MULTIPLE_INTERVAL_SELECTION</code> from {@link 76: * ListSelectionModel}. The default value is 77: * <code>MULTIPLE_INTERVAL_SELECTION</code>. 78: */ 79: int selectionMode = MULTIPLE_INTERVAL_SELECTION; 80: 81: /** 82: * The index of the "lead" of the most recent selection. The lead is the 83: * second argument in any call to {@link #setSelectionInterval}, {@link 84: * #addSelectionInterval} or {@link #removeSelectionInterval}. Generally 85: * the lead refers to the most recent position a user dragged their mouse 86: * over. 87: */ 88: int leadSelectionIndex = -1; 89: 90: /** 91: * The index of the "anchor" of the most recent selection. The anchor is 92: * the first argument in any call to {@link #setSelectionInterval}, 93: * {@link #addSelectionInterval} or {@link 94: * #removeSelectionInterval}. Generally the anchor refers to the first 95: * recent position a user clicks when they begin to drag their mouse over 96: * a list. 97: * 98: * @see #getAnchorSelectionIndex 99: * @see #setAnchorSelectionIndex 100: */ 101: int anchorSelectionIndex = -1; 102: 103: /** 104: * controls the range of indices provided in any {@link 105: * ListSelectionEvent} fired by the selectionModel. Let 106: * <code>[A,L]</code> be the range of indices between {@link 107: * #anchorSelectionIndex} and {@link #leadSelectionIndex} inclusive, and 108: * let <code>[i0,i1]</code> be the range of indices changed in a given 109: * call which generates a {@link ListSelectionEvent}. Then when this 110: * property is <code>true</code>, the {@link ListSelectionEvent} contains 111: * the range <code>[A,L] union [i0,i1]</code>; when <code>false</code> it 112: * will contain only <code>[i0,i1]</code>. The default is 113: * <code>true</code>. 114: * 115: * @see #isLeadAnchorNotificationEnabled 116: * @see #setLeadAnchorNotificationEnabled 117: */ 118: protected boolean leadAnchorNotificationEnabled = true; 119: 120: /** 121: * Whether the selection is currently "adjusting". Any {@link 122: * ListSelectionEvent} events constructed in response to changes in this 123: * list selection model will have their {@link 124: * ListSelectionEvent#isAdjusting} field set to this value. 125: * 126: * @see #getValueIsAdjusting 127: * @see #setValueIsAdjusting 128: */ 129: boolean valueIsAdjusting = false; 130: 131: 132: /** 133: * The current set of "intervals", represented simply by a {@link 134: * java.util.BitSet}. A set bit indicates a selected index, whereas a 135: * cleared bit indicates a non-selected index. 136: */ 137: BitSet sel = new BitSet(); 138: 139: /** 140: * A variable to store the previous value of sel. 141: * Used to make sure we only fireValueChanged when the BitSet 142: * actually does change. 143: */ 144: Object oldSel; 145: 146: /** 147: * Whether this call of setLeadSelectionInterval was called locally 148: * from addSelectionInterval 149: */ 150: boolean setLeadCalledFromAdd = false; 151: 152: /** 153: * Gets the value of the {@link #selectionMode} property. 154: * 155: * @return The current value of the property 156: */ 157: public int getSelectionMode() 158: { 159: return selectionMode; 160: } 161: 162: /** 163: * Sets the value of the {@link #selectionMode} property. 164: * 165: * @param a The new value of the property 166: */ 167: public void setSelectionMode(int a) 168: { 169: selectionMode = a; 170: } 171: 172: /** 173: * Gets the value of the {@link #anchorSelectionIndex} property. 174: * 175: * @return The current property value 176: * 177: * @see #setAnchorSelectionIndex 178: */ 179: public int getAnchorSelectionIndex() 180: { 181: return anchorSelectionIndex; 182: } 183: 184: /** 185: * Sets the value of the {@link #anchorSelectionIndex} property. 186: * 187: * @param anchorIndex The new property value 188: * 189: * @see #getAnchorSelectionIndex 190: */ 191: public void setAnchorSelectionIndex(int anchorIndex) 192: { 193: anchorSelectionIndex = anchorIndex; 194: } 195: 196: /** 197: * Gets the value of the {@link #leadSelectionIndex} property. 198: * 199: * @return The current property value 200: * 201: * @see #setLeadSelectionIndex 202: */ 203: public int getLeadSelectionIndex() 204: { 205: return leadSelectionIndex; 206: } 207: 208: /** 209: * <p>Sets the value of the {@link #anchorSelectionIndex} property. As a 210: * side effect, alters the selection status of two ranges of indices. Let 211: * <code>OL</code> be the old lead selection index, <code>NL</code> be 212: * the new lead selection index, and <code>A</code> be the anchor 213: * selection index. Then if <code>A</code> is a valid selection index, 214: * one of two things happens depending on the seleciton status of 215: * <code>A</code>:</p> 216: * 217: * <ul> 218: * 219: * <li><code>isSelectedIndex(A) == true</code>: set <code>[A,OL]</code> 220: * to <em>deselected</em>, then set <code>[A,NL]</code> to 221: * <em>selected</em>.</li> 222: * 223: * <li><code>isSelectedIndex(A) == false</code>: set <code>[A,OL]</code> 224: * to <em>selected</em>, then set <code>[A,NL]</code> to 225: * <em>deselected</em>.</li> 226: * 227: * </ul> 228: * 229: * <p>This method generates at most a single {@link ListSelectionEvent} 230: * despite changing multiple ranges. The range of values provided to the 231: * {@link ListSelectionEvent} includes only the minimum range of values 232: * which changed selection status between the beginning and end of the 233: * method.</p> 234: * 235: * @param leadIndex The new property value 236: * 237: * @see #getAnchorSelectionIndex 238: */ 239: public void setLeadSelectionIndex(int leadIndex) 240: { 241: int oldLeadIndex = leadSelectionIndex; 242: if (setLeadCalledFromAdd == false) 243: oldSel = sel.clone(); 244: leadSelectionIndex = leadIndex; 245: 246: if (anchorSelectionIndex == -1) 247: return; 248: 249: int R1 = Math.min(anchorSelectionIndex, oldLeadIndex); 250: int R2 = Math.max(anchorSelectionIndex, oldLeadIndex); 251: int S1 = Math.min(anchorSelectionIndex, leadIndex); 252: int S2 = Math.max(anchorSelectionIndex, leadIndex); 253: 254: int lo = Math.min(R1, S1); 255: int hi = Math.max(R2, S2); 256: 257: BitSet oldRange = sel.get(lo, hi+1); 258: 259: if (isSelectedIndex(anchorSelectionIndex)) 260: { 261: sel.clear(R1, R2+1); 262: sel.set(S1, S2+1); 263: } 264: else 265: { 266: sel.set(R1, R2+1); 267: sel.clear(S1, S2+1); 268: } 269: 270: BitSet newRange = sel.get(lo, hi+1); 271: newRange.xor(oldRange); 272: 273: int beg = sel.nextSetBit(0), end = -1; 274: for(int i=beg; i >= 0; i=sel.nextSetBit(i+1)) 275: end = i; 276: if (sel.equals(oldSel) == false) 277: fireValueChanged(beg, end, valueIsAdjusting); 278: } 279: 280: /** 281: * Gets the value of the {@link #leadAnchorNotificationEnabled} property. 282: * 283: * @return The current property value 284: * 285: * @see #setLeadAnchorNotificationEnabled 286: */ 287: public boolean isLeadAnchorNotificationEnabled() 288: { 289: return leadAnchorNotificationEnabled; 290: } 291: 292: /** 293: * Sets the value of the {@link #leadAnchorNotificationEnabled} property. 294: * 295: * @param l The new property value 296: * 297: * @see #isLeadAnchorNotificationEnabled 298: */ 299: public void setLeadAnchorNotificationEnabled(boolean l) 300: { 301: leadAnchorNotificationEnabled = l; 302: } 303: 304: /** 305: * Gets the value of the {@link #valueIsAdjusting} property. 306: * 307: * @return The current property value 308: * 309: * @see #setValueIsAdjusting 310: */ 311: public boolean getValueIsAdjusting() 312: { 313: return valueIsAdjusting; 314: } 315: 316: /** 317: * Sets the value of the {@link #valueIsAdjusting} property. 318: * 319: * @param v The new property value 320: * 321: * @see #getValueIsAdjusting 322: */ 323: public void setValueIsAdjusting(boolean v) 324: { 325: valueIsAdjusting = v; 326: } 327: 328: /** 329: * Determines whether the selection is empty. 330: * 331: * @return <code>true</code> if the selection is empty, otherwise 332: * <code>false</code> 333: */ 334: public boolean isSelectionEmpty() 335: { 336: return sel.isEmpty(); 337: } 338: 339: /** 340: * Gets the smallest index which is currently a member of a selection 341: * interval. 342: * 343: * @return The least integer <code>i</code> such that <code>i >= 344: * 0</code> and <code>i</code> is a member of a selected interval, or 345: * <code>-1</code> if there are no selected intervals 346: * 347: * @see #getMaxSelectionIndex 348: */ 349: public int getMinSelectionIndex() 350: { 351: if (isSelectionEmpty()) 352: return -1; 353: 354: return sel.nextSetBit(0); 355: } 356: 357: /** 358: * Gets the largest index which is currently a member of a selection 359: * interval. 360: * 361: * @return The greatest integer <code>i</code> such that <code>i >= 362: * 0</code> and <code>i</code> is a member of a selected interval, or 363: * <code>-1</code> if there are no selected intervals 364: * 365: * @see #getMinSelectionIndex 366: */ 367: public int getMaxSelectionIndex() 368: { 369: if (isSelectionEmpty()) 370: return -1; 371: 372: int mx = -1; 373: for(int i=sel.nextSetBit(0); i >= 0; i=sel.nextSetBit(i+1)) 374: { 375: mx = i; 376: } 377: return mx; 378: } 379: 380: /** 381: * Determines whether a particular index is a member of a selection 382: * interval. 383: * 384: * @param a The index to search for 385: * 386: * @return <code>true</code> if the index is a member of a selection interval, 387: * otherwise <code>false</code> 388: */ 389: public boolean isSelectedIndex(int a) 390: { 391: return sel.get(a); 392: } 393: 394: /** 395: * If the {@link #selectionMode} property is equal to 396: * <code>SINGLE_SELECTION</code> equivalent to calling 397: * <code>setSelectionInterval(index1, index2)</code>; 398: * If the {@link #selectionMode} property is equal to 399: * <code>SINGLE_INTERVAL_SELECTION</code> and the interval being 400: * added is not adjacent to an already selected interval, 401: * equivalent to <code>setSelectionInterval(index1, index2)</code>. 402: * Otherwise adds the range <code>[index0, index1]</code> 403: * to the selection interval set. 404: * 405: * @param index0 The beginning of the range of indices to select 406: * @param index1 The end of the range of indices to select 407: * 408: * @see #setSelectionInterval 409: * @see #removeSelectionInterval 410: */ 411: public void addSelectionInterval(int index0, int index1) 412: { 413: int lo = Math.min(index0, index1); 414: int hi = Math.max(index0, index1); 415: oldSel = sel.clone(); 416: 417: if (selectionMode == SINGLE_SELECTION) 418: sel.clear(); 419: 420: // COMPAT: Like Sun (but not like IBM), we allow calls to 421: // addSelectionInterval when selectionMode is 422: // SINGLE_SELECTION_INTERVAL iff the interval being added 423: // is adjacent to an already selected interval 424: if (selectionMode == SINGLE_INTERVAL_SELECTION) 425: if (!(isSelectedIndex(index0) || 426: isSelectedIndex(index1) || 427: isSelectedIndex(Math.max(lo-1,0)) || 428: isSelectedIndex(Math.min(hi+1,sel.size())))) 429: sel.clear(); 430: 431: if (selectionMode == SINGLE_SELECTION) 432: index0 = index1; 433: 434: // We have to update the anchorSelectionIndex and leadSelectionIndex 435: // variables 436: 437: // The next if statements breaks down to "if this selection is adjacent 438: // to the previous selection and going in the same direction" 439: if ((isSelectedIndex(leadSelectionIndex)) 440: && ((index0 - 1 == leadSelectionIndex 441: && (index1 >= index0) 442: && (leadSelectionIndex >= anchorSelectionIndex)) 443: || (index0 + 1 == leadSelectionIndex && (index1 <= index0) 444: && (leadSelectionIndex <= anchorSelectionIndex))) 445: && (anchorSelectionIndex != -1 || leadSelectionIndex != -1)) 446: { 447: // setting setLeadCalledFromAdd to true tells setLeadSelectionIndex 448: // not to update oldSel 449: setLeadCalledFromAdd = true; 450: setLeadSelectionIndex(index1); 451: setLeadCalledFromAdd = false; 452: } 453: else 454: { 455: leadSelectionIndex = index1; 456: anchorSelectionIndex = index0; 457: sel.set(lo, hi+1); 458: if (sel.equals(oldSel) == false) 459: fireValueChanged(lo, hi, valueIsAdjusting); 460: } 461: } 462: 463: 464: /** 465: * Deselects all indices in the inclusive range 466: * <code>[index0,index1]</code>. 467: * 468: * @param index0 The beginning of the range of indices to deselect 469: * @param index1 The end of the range of indices to deselect 470: * 471: * @see #addSelectionInterval 472: * @see #setSelectionInterval 473: */ 474: public void removeSelectionInterval(int index0, 475: int index1) 476: { 477: oldSel = sel.clone(); 478: int lo = Math.min(index0, index1); 479: int hi = Math.max(index0, index1); 480: 481: // if selectionMode is SINGLE_INTERVAL_SELECTION and removing the interval 482: // (index0,index1) would leave two disjoint selection intervals, remove all 483: // selected indices from lo to the last selected index 484: if (getMinSelectionIndex() > 0 && getMinSelectionIndex() < lo && 485: selectionMode == SINGLE_INTERVAL_SELECTION) 486: hi = sel.size() - 1; 487: 488: sel.clear(lo, hi+1); 489: //update anchorSelectionIndex and leadSelectionIndex variables 490: //TODO: will probably need MouseDragged to test properly and know if this works 491: setAnchorSelectionIndex(index0); 492: leadSelectionIndex = index1; 493: if (sel.equals(oldSel) == false) 494: fireValueChanged(lo, hi, valueIsAdjusting); 495: } 496: 497: /** 498: * Removes all intervals in the selection set. 499: */ 500: public void clearSelection() 501: { 502: oldSel = sel.clone(); 503: int sz = sel.size(); 504: sel.clear(); 505: if (sel.equals(oldSel) == false) 506: fireValueChanged(0, sz, valueIsAdjusting); 507: } 508: 509: /** 510: * Clears the current selection and marks a given interval as 511: * "selected". If the current selection mode is 512: * <code>SINGLE_SELECTION</code> only the index <code>index2</code> is 513: * selected. 514: * 515: * @param index0 The low end of the new selection 516: * @param index1 The high end of the new selection 517: */ 518: public void setSelectionInterval(int index0, int index1) 519: { 520: oldSel = sel.clone(); 521: sel.clear(); 522: if (selectionMode == SINGLE_SELECTION) 523: index0 = index1; 524: 525: int lo = Math.min(index0, index1); 526: int hi = Math.max(index0, index1); 527: sel.set(lo, hi+1); 528: // update the anchorSelectionIndex and leadSelectionIndex variables 529: setAnchorSelectionIndex(index0); 530: leadSelectionIndex=index1; 531: if (sel.equals(oldSel) == false) 532: fireValueChanged(lo, hi, valueIsAdjusting); 533: } 534: 535: /** 536: * Inserts a number of indices either before or after a particular 537: * position in the set of indices. Renumbers all indices after the 538: * inserted range. The new indices in the inserted range are not 539: * selected. This method is typically called to synchronize the selection 540: * model with an inserted range of elements in a {@link ListModel}. 541: * 542: * @param index The position to insert indices at 543: * @param length The number of indices to insert 544: * @param before Indicates whether to insert the indices before the index 545: * or after it 546: */ 547: public void insertIndexInterval(int index, 548: int length, 549: boolean before) 550: { 551: if (!before) 552: { 553: index++; 554: length--; 555: } 556: BitSet tmp = sel.get(index, sel.size()); 557: sel.clear(index, sel.size()); 558: int n = tmp.size(); 559: for (int i = 0; i < n; ++i) 560: sel.set(index + length + i, tmp.get(i)); 561: } 562: 563: /** 564: * Removes a range from the set of indices. Renumbers all indices after 565: * the removed range. This method is typically called to synchronize the 566: * selection model with a deleted range of elements in a {@link 567: * ListModel}. 568: * 569: * @param index0 The first index to remove (inclusive) 570: * @param index1 The last index to remove (inclusive) 571: */ 572: public void removeIndexInterval(int index0, 573: int index1) 574: { 575: int lo = Math.min(index0, index1); 576: int hi = Math.max(index0, index1); 577: 578: BitSet tmp = sel.get(hi, sel.size()); 579: sel.clear(lo, sel.size()); 580: int n = tmp.size(); 581: for (int i = 0; i < n; ++i) 582: sel.set(lo + i, tmp.get(i)); 583: } 584: 585: /** 586: * Fires a {@link ListSelectionEvent} to all the listeners of type {@link 587: * ListSelectionListener} registered with this selection model to 588: * indicate that a series of adjustment has just ended. 589: * 590: * The values of {@link #getMinSelectionIndex} and 591: * {@link #getMaxSelectionIndex} are used in the {@link ListSelectionEvent} 592: * that gets fired. 593: * 594: * @param isAdjusting <code>true</code> if this is the final change 595: * in a series of adjustments, <code>false/code> otherwise 596: */ 597: protected void fireValueChanged(boolean isAdjusting) 598: { 599: fireValueChanged(getMinSelectionIndex(), getMaxSelectionIndex(), 600: isAdjusting); 601: } 602: 603: /** 604: * Fires a {@link ListSelectionEvent} to all the listeners of type {@link 605: * ListSelectionListener} registered with this selection model. 606: * 607: * @param firstIndex The low index of the changed range 608: * @param lastIndex The high index of the changed range 609: */ 610: protected void fireValueChanged(int firstIndex, int lastIndex) 611: { 612: fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting()); 613: } 614: 615: /** 616: * Fires a {@link ListSelectionEvent} to all the listeners of type {@link 617: * ListSelectionListener} registered with this selection model. 618: * 619: * @param firstIndex The low index of the changed range 620: * @param lastIndex The high index of the changed range 621: * @param isAdjusting Whether this change is part of a seqence of adjustments 622: * made to the selection, such as during interactive scrolling 623: */ 624: protected void fireValueChanged(int firstIndex, int lastIndex, 625: boolean isAdjusting) 626: { 627: ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex, 628: lastIndex, isAdjusting); 629: ListSelectionListener[] listeners = getListSelectionListeners(); 630: for (int i = 0; i < listeners.length; ++i) 631: listeners[i].valueChanged(evt); 632: } 633: 634: /** 635: * Adds a listener. 636: * 637: * @param listener The listener to add 638: * 639: * @see #removeListSelectionListener 640: * @see #getListSelectionListeners 641: */ 642: public void addListSelectionListener(ListSelectionListener listener) 643: { 644: listenerList.add(ListSelectionListener.class, listener); 645: } 646: 647: /** 648: * Removes a registered listener. 649: * 650: * @param listener The listener to remove 651: * 652: * @see #addListSelectionListener 653: * @see #getListSelectionListeners 654: */ 655: public void removeListSelectionListener(ListSelectionListener listener) 656: { 657: listenerList.remove(ListSelectionListener.class, listener); 658: } 659: 660: /** 661: * Returns an array of all registerers listeners. 662: * 663: * @param listenerType The type of listener to retrieve 664: * 665: * @return The array 666: * 667: * @see #getListSelectionListeners 668: * @since 1.3 669: */ 670: public EventListener[] getListeners(Class listenerType) 671: { 672: return listenerList.getListeners(listenerType); 673: } 674: 675: /** 676: * Returns an array of all registerd list selection listeners. 677: * 678: * @return the array 679: * 680: * @see #addListSelectionListener 681: * @see #removeListSelectionListener 682: * @see #getListeners 683: * @since 1.4 684: */ 685: public ListSelectionListener[] getListSelectionListeners() 686: { 687: return (ListSelectionListener[]) getListeners(ListSelectionListener.class); 688: } 689: 690: /** 691: * Returns a clone of this object. 692: * <code>listenerList</code> don't gets duplicated. 693: * 694: * @return the cloned object 695: * 696: * @throws CloneNotSupportedException if an error occurs 697: */ 698: public Object clone() 699: throws CloneNotSupportedException 700: { 701: DefaultListSelectionModel model = 702: (DefaultListSelectionModel) super.clone(); 703: model.sel = (BitSet) sel.clone(); 704: return model; 705: } 706: }
GNU Classpath (0.18) |