| /******************************************************************************* |
| * Copyright (c) 2002, 2012 Innoopract Informationssysteme GmbH and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Innoopract Informationssysteme GmbH - initial API and implementation |
| * EclipseSource - ongoing development |
| ******************************************************************************/ |
| package org.eclipse.swt.widgets; |
| |
| import org.eclipse.rap.rwt.RWT; |
| import org.eclipse.rap.rwt.graphics.Graphics; |
| import org.eclipse.rap.rwt.internal.theme.IThemeAdapter; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.SWTException; |
| import org.eclipse.swt.events.*; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.internal.widgets.*; |
| import org.eclipse.swt.internal.widgets.listkit.ListThemeAdapter; |
| |
| |
| /** |
| * Instances of this class represent a selectable user interface |
| * object that displays a list of strings and issues notification |
| * when a string is selected. A list may be single or multi select. |
| * <p> |
| * <dl> |
| * <dt><b>Styles:</b></dt> |
| * <dd>SINGLE, MULTI</dd> |
| * <dt><b>Events:</b></dt> |
| * <dd>Selection, DefaultSelection</dd> |
| * </dl> |
| * <p> |
| * Note: Only one of SINGLE and MULTI may be specified. |
| * </p><p> |
| * IMPORTANT: This class is <em>not</em> intended to be subclassed. |
| * </p> |
| * @since 1.0 |
| */ |
| public class List extends Scrollable { |
| |
| private final class ResizeListener extends ControlAdapter { |
| @Override |
| public void controlResized( ControlEvent event ) { |
| List.this.updateScrollBars(); |
| } |
| } |
| |
| private final ListModel model; |
| private int focusIndex; |
| private transient IListAdapter listAdapter; |
| private final ResizeListener resizeListener; |
| private int topIndex; |
| private boolean hasVScrollBar; |
| private boolean hasHScrollBar; |
| private Rectangle bufferedItemPadding; |
| private int customItemHeight; |
| boolean markupEnabled; |
| private boolean markupValidationDisabled; |
| |
| /** |
| * Constructs a new instance of this class given its parent |
| * and a style value describing its behavior and appearance. |
| * <p> |
| * The style value is either one of the style constants defined in |
| * class <code>SWT</code> which is applicable to instances of this |
| * class, or must be built by <em>bitwise OR</em>'ing together |
| * (that is, using the <code>int</code> "|" operator) two or more |
| * of those <code>SWT</code> style constants. The class description |
| * lists the style constants that are applicable to the class. |
| * Style bits are also inherited from superclasses. |
| * </p> |
| * |
| * @param parent a composite control which will be the parent of the new instance (cannot be null) |
| * @param style the style of control to construct |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> |
| * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> |
| * </ul> |
| * |
| * @see SWT#SINGLE |
| * @see SWT#MULTI |
| * @see Widget#checkSubclass |
| * @see Widget#getStyle |
| */ |
| public List( Composite parent, int style ) { |
| super( parent, checkStyle( style ) ); |
| model = new ListModel( ( style & SWT.SINGLE ) != 0 ); |
| focusIndex = -1; |
| customItemHeight = -1; |
| resizeListener = new ResizeListener(); |
| addControlListener( resizeListener ); |
| } |
| |
| ///////////////////// |
| // Adaptable override |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter( Class<T> adapter ) { |
| T result; |
| if( adapter == IListAdapter.class ) { |
| if( listAdapter == null ) { |
| listAdapter = new IListAdapter() { |
| public void setFocusIndex( int focusIndex ) { |
| List.this.setFocusIndex( focusIndex ); |
| } |
| |
| public Point getItemDimensions() { |
| return List.this.getItemDimensions(); |
| } |
| }; |
| } |
| result = ( T )listAdapter; |
| } else { |
| result = super.getAdapter( adapter ); |
| } |
| return result; |
| } |
| |
| /////////////////////////////// |
| // Methods to get/set selection |
| |
| /** |
| * Returns an array of <code>String</code>s that are currently |
| * selected in the receiver. The order of the items is unspecified. |
| * An empty array indicates that no items are selected. |
| * <p> |
| * Note: This is not the actual structure used by the receiver |
| * to maintain its selection, so modifying the array will |
| * not affect the receiver. |
| * </p> |
| * @return an array representing the selection |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public String[] getSelection() { |
| checkWidget(); |
| int[] selectionIndices = model.getSelectionIndices(); |
| String[] result = new String[ selectionIndices.length ]; |
| for( int i = 0; i < result.length; i++ ) { |
| result[ i ] = model.getItem( selectionIndices[ i ] ); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the zero-relative index of the item which is currently |
| * selected in the receiver, or -1 if no item is selected. |
| * |
| * @return the index of the selected item or -1 |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getSelectionIndex() { |
| checkWidget(); |
| return model.getSelectionIndex(); |
| } |
| |
| /** |
| * Returns the zero-relative indices of the items which are currently |
| * selected in the receiver. The order of the indices is unspecified. |
| * The array is empty if no items are selected. |
| * <p> |
| * Note: This is not the actual structure used by the receiver |
| * to maintain its selection, so modifying the array will |
| * not affect the receiver. |
| * </p> |
| * @return the array of indices of the selected items |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int[] getSelectionIndices() { |
| checkWidget(); |
| return model.getSelectionIndices(); |
| } |
| |
| /** |
| * Returns the number of selected items contained in the receiver. |
| * |
| * @return the number of selected items |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getSelectionCount() { |
| checkWidget(); |
| return model.getSelectionCount(); |
| } |
| |
| /** |
| * Selects the item at the given zero-relative index in the receiver. |
| * If the item at the index was already selected, it remains selected. |
| * The current selection is first cleared, then the new item is selected. |
| * Indices that are out of range are ignored. |
| * |
| * @param selection the index of the item to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * @see List#deselectAll() |
| * @see List#select(int) |
| */ |
| // TODO [rh] selection is not scrolled into view (see List.js) |
| public void setSelection( int selection ) { |
| checkWidget(); |
| model.setSelection( selection ); |
| updateFocusIndexAfterSelectionChange(); |
| } |
| |
| /** |
| * Selects the items at the given zero-relative indices in the receiver. |
| * The current selection is cleared before the new items are selected. |
| * <p> |
| * Indices that are out of range and duplicate indices are ignored. |
| * If the receiver is single-select and multiple indices are specified, |
| * then all indices are ignored. |
| * |
| * @param selection the indices of the items to select |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see List#deselectAll() |
| * @see List#select(int[]) |
| */ |
| public void setSelection( int[] selection ) { |
| checkWidget(); |
| model.setSelection( selection ); |
| updateFocusIndexAfterSelectionChange(); |
| } |
| |
| /** |
| * Selects the items in the range specified by the given zero-relative |
| * indices in the receiver. The range of indices is inclusive. |
| * The current selection is cleared before the new items are selected. |
| * <p> |
| * Indices that are out of range are ignored and no items will be selected |
| * if start is greater than end. |
| * If the receiver is single-select and there is more than one item in the |
| * given range, then all indices are ignored. |
| * |
| * @param start the start index of the items to select |
| * @param end the end index of the items to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see List#deselectAll() |
| * @see List#select(int,int) |
| */ |
| public void setSelection( int start, int end ) { |
| checkWidget(); |
| model.setSelection( start, end ); |
| updateFocusIndexAfterSelectionChange(); |
| } |
| |
| /** |
| * Sets the receiver's selection to be the given array of items. |
| * The current selection is cleared before the new items are selected. |
| * <p> |
| * Items that are not in the receiver are ignored. |
| * If the receiver is single-select and multiple items are specified, |
| * then all items are ignored. |
| * |
| * @param selection the array of items |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the array of items is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see List#deselectAll() |
| * @see List#select(int[]) |
| * @see List#setSelection(int[]) |
| */ |
| public void setSelection( String[] selection ) { |
| checkWidget(); |
| model.setSelection( selection ); |
| updateFocusIndexAfterSelectionChange(); |
| } |
| |
| /** |
| * Selects the item at the given zero-relative index in the receiver's |
| * list. If the item at the index was already selected, it remains |
| * selected. Indices that are out of range are ignored. |
| * |
| * @param index the index of the item to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void select( int index ) { |
| checkWidget(); |
| if( ( style & SWT.SINGLE ) != 0 ) { |
| if( index >= 0 && index < model.getItemCount() ) { |
| model.setSelection( index ); |
| } |
| } else { |
| model.addSelection( index ); |
| } |
| } |
| |
| /** |
| * Selects the items at the given zero-relative indices in the receiver. |
| * The current selection is not cleared before the new items are selected. |
| * <p> |
| * If the item at a given index is not selected, it is selected. |
| * If the item at a given index was already selected, it remains selected. |
| * Indices that are out of range and duplicate indices are ignored. |
| * If the receiver is single-select and multiple indices are specified, |
| * then all indices are ignored. |
| * |
| * @param indices the array of indices for the items to select |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the array of indices is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see List#setSelection(int[]) |
| */ |
| public void select( int[] indices ) { |
| checkWidget(); |
| if( indices == null ) { |
| error( SWT.ERROR_NULL_ARGUMENT ); |
| } |
| int length = indices.length; |
| if( length != 0 && ( ( style & SWT.SINGLE ) == 0 || length <= 1 ) ) { |
| int i = 0; |
| while( i < length ) { |
| int index = indices[ i ]; |
| model.addSelection( index ); |
| i++; |
| } |
| } |
| } |
| |
| /** |
| * Selects the items in the range specified by the given zero-relative |
| * indices in the receiver. The range of indices is inclusive. |
| * The current selection is not cleared before the new items are selected. |
| * <p> |
| * If an item in the given range is not selected, it is selected. |
| * If an item in the given range was already selected, it remains selected. |
| * Indices that are out of range are ignored and no items will be selected |
| * if start is greater than end. |
| * If the receiver is single-select and there is more than one item in the |
| * given range, then all indices are ignored. |
| * |
| * @param start the start of the range |
| * @param end the end of the range |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see List#setSelection(int,int) |
| */ |
| public void select( int start, int end ) { |
| checkWidget(); |
| if( end >= 0 && start <= end && ( ( style & SWT.SINGLE ) == 0 || start == end ) ) { |
| int count = model.getItemCount(); |
| if( count != 0 && start < count ) { |
| int startIndex = Math.max( 0, start ); |
| int endIndex = Math.min( end, count - 1 ); |
| if( ( style & SWT.SINGLE ) != 0 ) { |
| model.setSelection( startIndex ); |
| } else { |
| for( int i = startIndex; i <= endIndex; i++ ) { |
| model.addSelection( i ); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Selects all of the items in the receiver. |
| * <p> |
| * If the receiver is single-select, do nothing. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void selectAll() { |
| checkWidget(); |
| model.selectAll(); |
| updateFocusIndexAfterSelectionChange(); |
| } |
| |
| /** |
| * Deselects all selected items in the receiver. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void deselectAll() { |
| checkWidget(); |
| model.deselectAll(); |
| updateFocusIndexAfterSelectionChange(); |
| } |
| |
| /** |
| * Deselects the item at the given zero-relative index in the receiver. |
| * If the item at the index was already deselected, it remains |
| * deselected. Indices that are out of range are ignored. |
| * |
| * @param index the index of the item to deselect |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 1.3 |
| */ |
| public void deselect( int index ) { |
| checkWidget(); |
| removeFromSelection( index ); |
| } |
| |
| /** |
| * Deselects the items at the given zero-relative indices in the receiver. |
| * If the item at the given zero-relative index in the receiver |
| * is selected, it is deselected. If the item at the index |
| * was not selected, it remains deselected. The range of the |
| * indices is inclusive. Indices that are out of range are ignored. |
| * |
| * @param start the start index of the items to deselect |
| * @param end the end index of the items to deselect |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 1.3 |
| */ |
| public void deselect( int start, int end ) { |
| checkWidget(); |
| if( start == 0 && end == model.getItemCount() - 1 ) { |
| deselectAll(); |
| } else { |
| int actualStart = Math.max( 0, start ); |
| for( int i = actualStart; i <= end; i++ ) { |
| removeFromSelection( i ); |
| } |
| } |
| } |
| |
| /** |
| * Deselects the items at the given zero-relative indices in the receiver. |
| * If the item at the given zero-relative index in the receiver |
| * is selected, it is deselected. If the item at the index |
| * was not selected, it remains deselected. Indices that are out |
| * of range and duplicate indices are ignored. |
| * |
| * @param indices the array of indices for the items to deselect |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 1.3 |
| */ |
| public void deselect( int [] indices ) { |
| checkWidget(); |
| if( indices == null ) { |
| error( SWT.ERROR_NULL_ARGUMENT ); |
| } |
| for( int i = 0; i < indices.length; i++ ) { |
| removeFromSelection( indices[ i ] ); |
| } |
| } |
| |
| private void removeFromSelection( int index ) { |
| if( index >= 0 && index < model.getItemCount() ) { |
| boolean found = false; |
| int selection[] = model.getSelectionIndices(); |
| for( int i = 0; !found && i < selection.length; i++ ) { |
| if( index == selection[ i ] ) { |
| int length = selection.length; |
| int[] newSel = new int[ length - 1 ]; |
| System.arraycopy( selection, 0, newSel, 0, i ); |
| if( i < length - 1 ) { |
| System.arraycopy( selection, i + 1, newSel, i, length - i - 1 ); |
| } |
| selection = newSel; |
| model.setSelection( selection ); |
| found = true; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns <code>true</code> if the item is selected, |
| * and <code>false</code> otherwise. Indices out of |
| * range are ignored. |
| * |
| * @param index the index of the item |
| * @return the visibility state of the item at the index |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public boolean isSelected( int index ) { |
| checkWidget(); |
| boolean result; |
| if( ( style & SWT.SINGLE ) != 0 ) { |
| result = index == getSelectionIndex(); |
| } else { |
| int[] selectionIndices = getSelectionIndices(); |
| result = false; |
| for( int i = 0; !result && i < selectionIndices.length; i++ ) { |
| if( index == selectionIndices[ i ] ) { |
| result = true; |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Sets the zero-relative index of the item which is currently |
| * at the top of the receiver. This index can change when items |
| * are scrolled or new items are added and removed. |
| * |
| * @param topIndex the index of the top item |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 1.3 |
| */ |
| public void setTopIndex( int topIndex ) { |
| checkWidget(); |
| int count = model.getItemCount(); |
| if( this.topIndex != topIndex && topIndex >= 0 && topIndex < count ) { |
| this.topIndex = topIndex; |
| } |
| } |
| |
| /** |
| * Returns the zero-relative index of the item which is currently |
| * at the top of the receiver. This index can change when items are |
| * scrolled or new items are added or removed. |
| * |
| * @return the index of the top item |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 1.3 |
| */ |
| public int getTopIndex() { |
| checkWidget(); |
| return topIndex; |
| } |
| |
| /** |
| * Shows the selection. If the selection is already showing in the receiver, |
| * this method simply returns. Otherwise, the items are scrolled until |
| * the selection is visible. |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @since 1.3 |
| */ |
| public void showSelection() { |
| checkWidget(); |
| int index = getSelectionIndex(); |
| if( index != -1 ) { |
| int itemCount = getVisibleItemCount(); |
| if( index < topIndex ) { |
| // Show item as top item |
| setTopIndex( index ); |
| } else if( itemCount > 0 && index >= topIndex + itemCount ) { |
| // Show item as last item |
| setTopIndex( index - itemCount + 1 ); |
| } |
| } |
| } |
| |
| /** |
| * Returns the zero-relative index of the item which currently |
| * has the focus in the receiver, or -1 if no item has focus. |
| * |
| * @return the index of the selected item |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getFocusIndex() { |
| checkWidget(); |
| return focusIndex; |
| } |
| |
| //////////////////////////////// |
| // Methods to maintain the items |
| |
| /** |
| * Adds the argument to the end of the receiver's list. |
| * |
| * @param string the new item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the string is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see #add(String,int) |
| */ |
| public void add( String string ) { |
| checkWidget(); |
| model.add( string ); |
| updateFocusIndexAfterItemChange(); |
| updateScrollBars(); |
| } |
| |
| /** |
| * Adds the argument to the receiver's list at the given |
| * zero-relative index. |
| * <p> |
| * Note: To add an item at the end of the list, use the |
| * result of calling <code>getItemCount()</code> as the |
| * index or use <code>add(String)</code>. |
| * </p> |
| * |
| * @param string the new item |
| * @param index the index for the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the string is null</li> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see #add(String) |
| */ |
| public void add( String string, int index ) { |
| checkWidget(); |
| model.add( string, index ); |
| updateFocusIndexAfterItemChange(); |
| updateScrollBars(); |
| } |
| |
| /** |
| * Removes the item from the receiver at the given |
| * zero-relative index. |
| * |
| * @param index the index for the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void remove( int index ) { |
| checkWidget(); |
| model.remove( index ); |
| updateFocusIndexAfterItemChange(); |
| adjustTopIndex(); |
| updateScrollBars(); |
| } |
| |
| /** |
| * Removes the items from the receiver which are |
| * between the given zero-relative start and end |
| * indices (inclusive). |
| * |
| * @param start the start of the range |
| * @param end the end of the range |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void remove( int start, int end ) { |
| checkWidget(); |
| model.remove( start, end ); |
| updateFocusIndexAfterItemChange(); |
| adjustTopIndex(); |
| updateScrollBars(); |
| } |
| |
| /** |
| * Removes the items from the receiver at the given |
| * zero-relative indices. |
| * |
| * @param indices the array of indices of the items |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void remove( int[] indices ) { |
| checkWidget(); |
| model.remove( indices ); |
| updateFocusIndexAfterItemChange(); |
| adjustTopIndex(); |
| updateScrollBars(); |
| } |
| |
| /** |
| * Searches the receiver's list starting at the first item |
| * until an item is found that is equal to the argument, |
| * and removes that item from the list. |
| * |
| * @param string the item to remove |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the string is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void remove( String string ) { |
| checkWidget(); |
| model.remove( string ); |
| updateFocusIndexAfterItemChange(); |
| adjustTopIndex(); |
| updateScrollBars(); |
| } |
| |
| /** |
| * Removes all of the items from the receiver. |
| * <p> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void removeAll() { |
| checkWidget(); |
| model.removeAll(); |
| updateFocusIndexAfterItemChange(); |
| adjustTopIndex(); |
| updateScrollBars(); |
| } |
| |
| /** |
| * Sets the text of the item in the receiver's list at the given |
| * zero-relative index to the string argument. This is equivalent |
| * to <code>remove</code>'ing the old item at the index, and then |
| * <code>add</code>'ing the new item at that index. |
| * |
| * @param index the index for the item |
| * @param string the new text for the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * <li>ERROR_NULL_ARGUMENT - if the string is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void setItem( int index, String string ) { |
| checkWidget(); |
| validateMarkup( new String[] { string } ); |
| model.setItem( index, string ); |
| updateScrollBars(); |
| } |
| |
| /** |
| * Sets the receiver's items to be the given array of items. |
| * |
| * @param items the array of items |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the items array is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void setItems( String[] items ) { |
| checkWidget(); |
| validateMarkup( items ); |
| model.setItems( items ); |
| updateScrollBars(); |
| } |
| |
| /** |
| * Returns the item at the given, zero-relative index in the |
| * receiver. Throws an exception if the index is out of range. |
| * |
| * @param index the index of the item to return |
| * @return the item at the given index |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public String getItem( int index ) { |
| checkWidget(); |
| return model.getItem( index ); |
| } |
| |
| /** |
| * Returns the number of items contained in the receiver. |
| * |
| * @return the number of items |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getItemCount() { |
| checkWidget(); |
| return model.getItemCount(); |
| } |
| |
| /** |
| * Returns a (possibly empty) array of <code>String</code>s which |
| * are the items in the receiver. |
| * <p> |
| * Note: This is not the actual structure used by the receiver |
| * to maintain its list of items, so modifying the array will |
| * not affect the receiver. |
| * </p> |
| * |
| * @return the items in the receiver's list |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public String[] getItems() { |
| checkWidget(); |
| return model.getItems(); |
| } |
| |
| /** |
| * Gets the index of an item. |
| * <p> |
| * The list is searched starting at 0 until an |
| * item is found that is equal to the search item. |
| * If no item is found, -1 is returned. Indexing |
| * is zero based. |
| * |
| * @param string the search item |
| * @return the index of the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the string is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int indexOf( String string ) { |
| checkWidget(); |
| return indexOf( string, 0 ); |
| } |
| |
| /** |
| * Searches the receiver's list starting at the given, |
| * zero-relative index until an item is found that is equal |
| * to the argument, and returns the index of that item. If |
| * no item is found or the starting index is out of range, |
| * returns -1. |
| * |
| * @param string the search item |
| * @param start the zero-relative index at which to start the search |
| * @return the index of the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the string is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int indexOf( String string, int start ) { |
| checkWidget(); |
| if( string == null ) { |
| SWT.error( SWT.ERROR_NULL_ARGUMENT ); |
| } |
| return model.indexOf( string, start ); |
| } |
| |
| /** |
| * Returns the height of the area which would be used to |
| * display <em>one</em> of the items in the list. |
| * |
| * @return the height of one item |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getItemHeight() { |
| checkWidget(); |
| int result = customItemHeight; |
| if( result == -1 ) { |
| result = Graphics.getCharHeight( getFont() ) + getItemPadding().height; |
| } |
| return result; |
| } |
| |
| ///////////////////////////////////////// |
| // Listener registration/de-registration |
| |
| /** |
| * Adds the listener to the collection of listeners who will |
| * be notified when the receiver's selection changes, by sending |
| * it one of the messages defined in the <code>SelectionListener</code> |
| * interface. |
| * <p> |
| * <code>widgetSelected</code> is called when the selection changes. |
| * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. |
| * </p> |
| * |
| * @param listener the listener which should be notified |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SelectionListener |
| * @see #removeSelectionListener |
| * @see SelectionEvent |
| */ |
| public void addSelectionListener( SelectionListener listener ) { |
| checkWidget(); |
| if( listener == null ) { |
| SWT.error( SWT.ERROR_NULL_ARGUMENT ); |
| } |
| TypedListener typedListener = new TypedListener( listener ); |
| addListener( SWT.Selection, typedListener ); |
| addListener( SWT.DefaultSelection, typedListener ); |
| } |
| |
| /** |
| * Removes the listener from the collection of listeners who will |
| * be notified when the receiver's selection changes. |
| * |
| * @param listener the listener which should no longer be notified |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SelectionListener |
| * @see #addSelectionListener |
| */ |
| public void removeSelectionListener( SelectionListener listener ) { |
| checkWidget(); |
| if( listener == null ) { |
| SWT.error( SWT.ERROR_NULL_ARGUMENT ); |
| } |
| removeListener( SWT.Selection, listener ); |
| removeListener( SWT.DefaultSelection, listener ); |
| } |
| |
| @Override |
| public void setFont( Font font ) { |
| super.setFont( font ); |
| updateScrollBars(); |
| } |
| |
| @Override |
| boolean isTabGroup() { |
| return true; |
| } |
| |
| @Override |
| public void setData( String key, Object value ) { |
| if( RWT.CUSTOM_ITEM_HEIGHT.equals( key ) ) { |
| setCustomItemHeight( value ); |
| } else if( RWT.MARKUP_ENABLED.equals( key ) && !markupEnabled ) { |
| markupEnabled = Boolean.TRUE.equals( value ); |
| } else if( MarkupValidator.MARKUP_VALIDATION_DISABLED.equals( key ) ) { |
| markupValidationDisabled = Boolean.TRUE.equals( value ); |
| } |
| super.setData( key, value ); |
| } |
| |
| ///////////////////////////////////////// |
| // Widget dimensions |
| |
| @Override |
| public Point computeSize( int wHint, int hHint, boolean changed ) { |
| checkWidget (); |
| int width = getMaxItemWidth(); |
| int height = getItemHeight() * getItemCount(); |
| if( width == 0 ) { |
| width = DEFAULT_WIDTH; |
| } |
| if( height == 0 ) { |
| height = DEFAULT_HEIGHT; |
| } |
| if( wHint != SWT.DEFAULT ) { |
| width = wHint; |
| } |
| if( hHint != SWT.DEFAULT ) { |
| height = hHint; |
| } |
| int border = getBorderWidth(); |
| width += border * 2; |
| height += border * 2; |
| if( ( style & SWT.V_SCROLL ) != 0 ) { |
| width += getVerticalBar().getSize().x; |
| } |
| if( ( style & SWT.H_SCROLL ) != 0 ) { |
| height += getHorizontalBar().getSize().y; |
| } |
| return new Point( width, height ); |
| } |
| |
| ///////////////////////////////// |
| // Helping methods for focusIndex |
| |
| private void setFocusIndex( int focusIndex ) { |
| int count = model.getItemCount(); |
| if( focusIndex == -1 || ( focusIndex >= 0 && focusIndex < count ) ) { |
| this.focusIndex = focusIndex; |
| } |
| } |
| |
| private void updateFocusIndexAfterSelectionChange() { |
| focusIndex = -1; |
| if( model.getItemCount() > 0 ) { |
| if( model.getSelectionIndex() == -1 ) { |
| focusIndex = 0; |
| } else { |
| focusIndex = model.getSelectionIndices()[ 0 ]; |
| } |
| } |
| } |
| |
| private void updateFocusIndexAfterItemChange() { |
| if( model.getItemCount() == 0 ) { |
| focusIndex = -1; |
| } else if( model.getSelectionIndex() == -1 ){ |
| focusIndex = model.getItemCount() - 1; |
| } |
| } |
| |
| ////////////////// |
| // Helping methods |
| |
| private static int checkStyle( int style ) { |
| return checkBits( style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0 ); |
| } |
| |
| private int getItemWidth( String item ) { |
| int margin = getItemPadding().width; |
| return Graphics.stringExtent( getFont(), item ).x + margin; |
| } |
| |
| private int getMaxItemWidth() { |
| int result = 0; |
| String[] items = getItems(); |
| for( int i = 0; i < items.length; i++ ) { |
| int itemWidth = getItemWidth( items[ i ] ); |
| result = Math.max( result, itemWidth ); |
| } |
| return result; |
| } |
| |
| private void adjustTopIndex() { |
| int count = model.getItemCount(); |
| if( count == 0 ) { |
| topIndex = 0; |
| } else if( topIndex >= count - 1 ) { |
| topIndex = count - 1; |
| } |
| } |
| |
| final int getVisibleItemCount() { |
| int clientHeight = getBounds().height; |
| if( ( style & SWT.H_SCROLL ) != 0 ) { |
| clientHeight -= getHorizontalBar().getSize().y; |
| } |
| int result = 0; |
| if( clientHeight >= 0 ) { |
| int itemHeight = getItemHeight(); |
| result = clientHeight / itemHeight; |
| } |
| return result; |
| } |
| |
| Point getItemDimensions() { |
| int width = 0; |
| int height = 0; |
| if( getItemCount() > 0 ) { |
| int availableWidth = getClientArea().width; |
| width = Math.max( getMaxItemWidth(), availableWidth ); |
| height = getItemHeight(); |
| } |
| return new Point( width, height ); |
| } |
| |
| private Rectangle getItemPadding() { |
| if( bufferedItemPadding == null ) { |
| ListThemeAdapter themeAdapter = ( ListThemeAdapter )getAdapter( IThemeAdapter.class ); |
| bufferedItemPadding = themeAdapter.getItemPadding( this ); |
| } |
| return bufferedItemPadding; |
| } |
| |
| private void setCustomItemHeight( Object value ) { |
| if( value == null ) { |
| customItemHeight = -1; |
| } else { |
| if( !( value instanceof Integer ) ) { |
| error( SWT.ERROR_INVALID_ARGUMENT ); |
| } |
| int itemHeight = ( ( Integer )value ).intValue(); |
| if( itemHeight < 0 ) { |
| error( SWT.ERROR_INVALID_RANGE ); |
| } |
| customItemHeight = itemHeight; |
| } |
| } |
| |
| private void validateMarkup( String[] items ) { |
| if( markupEnabled && !markupValidationDisabled && items != null ) { |
| for( int i = 0; i < items.length; i++ ) { |
| if( items[ i ] != null ) { |
| MarkupValidator.getInstance().validate( items[ i ] ); |
| } |
| } |
| } |
| } |
| |
| /////////////////////////////////////// |
| // Helping methods - dynamic scrollbars |
| |
| boolean hasVScrollBar() { |
| return ( style & SWT.V_SCROLL ) != 0 && hasVScrollBar; |
| } |
| |
| boolean hasHScrollBar() { |
| return ( style & SWT.H_SCROLL ) != 0 && hasHScrollBar; |
| } |
| |
| @Override |
| int getVScrollBarWidth() { |
| int result = 0; |
| if( hasVScrollBar() ) { |
| result = getVerticalBar().getSize().x; |
| } |
| return result; |
| } |
| |
| @Override |
| int getHScrollBarHeight() { |
| int result = 0; |
| if( hasHScrollBar() ) { |
| result = getHorizontalBar().getSize().y; |
| } |
| return result; |
| } |
| |
| boolean needsVScrollBar() { |
| int availableHeight = getClientArea().height; |
| int height = getItemCount() * getItemHeight(); |
| return height > availableHeight; |
| } |
| |
| boolean needsHScrollBar() { |
| int availableWidth = getClientArea().width; |
| int width = getMaxItemWidth(); |
| return width > availableWidth; |
| } |
| |
| void updateScrollBars() { |
| hasVScrollBar = false; |
| hasHScrollBar = needsHScrollBar(); |
| if( needsVScrollBar() ) { |
| hasVScrollBar = true; |
| hasHScrollBar = needsHScrollBar(); |
| } |
| ScrollBar hScroll = getHorizontalBar(); |
| if( hScroll != null ) { |
| hScroll.setVisible( hasHScrollBar ); |
| } |
| ScrollBar vScroll = getVerticalBar(); |
| if( vScroll != null ) { |
| vScroll.setVisible( hasVScrollBar ); |
| } |
| } |
| } |