Java tutorial
/******************************************************************************* * Copyright (c) 2002, 2015 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 static org.eclipse.rap.rwt.internal.textsize.TextSizeUtil.stringExtent; import static org.eclipse.swt.internal.widgets.MarkupUtil.isMarkupEnabledFor; import static org.eclipse.swt.internal.widgets.MarkupValidator.isValidationDisabledFor; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA; import org.eclipse.rap.rwt.internal.textsize.TextSizeUtil; import org.eclipse.rap.rwt.internal.theme.ThemeAdapter; import org.eclipse.rap.rwt.theme.BoxDimensions; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.internal.widgets.IListAdapter; import org.eclipse.swt.internal.widgets.ListModel; import org.eclipse.swt.internal.widgets.MarkupValidator; import org.eclipse.swt.internal.widgets.listkit.ListLCA; 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) { 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 BoxDimensions bufferedItemPadding; private int customItemHeight; /** * 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) { if (adapter == IListAdapter.class) { if (listAdapter == null) { listAdapter = new IListAdapter() { @Override public void setFocusIndex(int focusIndex) { List.this.setFocusIndex(focusIndex); } @Override public Point getItemDimensions() { return List.this.getItemDimensions(); } }; } return (T) listAdapter; } if (adapter == WidgetLCA.class) { return (T) ListLCA.INSTANCE; } return super.getAdapter(adapter); } /////////////////////////////// // 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) { BoxDimensions itemPadding = getItemPadding(); result = TextSizeUtil.getCharHeight(getFont()) + itemPadding.top + itemPadding.bottom; } 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); } if (!RWT.MARKUP_ENABLED.equals(key) || !isMarkupEnabledFor(this)) { 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; } BoxDimensions border = getBorder(); width += border.left + border.right; height += border.top + border.bottom; 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) { Point extent = stringExtent(getFont(), item, isMarkupEnabledFor(this)); BoxDimensions itemPadding = getItemPadding(); return extent.x + itemPadding.left + itemPadding.right; } 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; if ((style & SWT.H_SCROLL) == 0 && isMarkupEnabledFor(this)) { width = availableWidth; } else { width = Math.max(getMaxItemWidth(), availableWidth); } height = getItemHeight(); } return new Point(width, height); } private BoxDimensions getItemPadding() { if (bufferedItemPadding == null) { ListThemeAdapter themeAdapter = (ListThemeAdapter) getAdapter(ThemeAdapter.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 (items != null && isMarkupEnabledFor(this) && !isValidationDisabledFor(this)) { 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() { boolean result = false; if ((style & SWT.H_SCROLL) != 0) { int availableWidth = getClientArea().width; int width = getMaxItemWidth(); result = width > availableWidth; } return result; } 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); } } }