| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.swt.widgets; |
| |
| |
| import org.eclipse.swt.*; |
| import org.eclipse.swt.graphics.*; |
| import org.eclipse.swt.internal.Compatibility; |
| |
| import java.util.Enumeration; |
| import java.util.Vector; |
| |
| /** |
| * This class is intended for widgets that display data of |
| * type Item. It provides a framework for scrolling and |
| * handles the screen refresh required when adding and |
| * removing items. |
| */ |
| abstract class SelectableItemWidget extends Composite { |
| private static final int DEFAULT_WIDTH = 64; // used in computeSize if width could not be calculated |
| private static final int DEFAULT_HEIGHT = 64; // used in computeSize if height could not be calculated |
| private static final int HORIZONTAL_SCROLL_INCREMENT = 5; // number of pixel the tree is moved |
| // during horizontal line scrolling |
| private static ImageData UncheckedImageData; // deselected check box image data. used to create an image at run time |
| private static ImageData GrayUncheckedImageData; // grayed deselected check box image data. used to create an image at run time |
| private static ImageData CheckMarkImageData; // check mark image data for check box. used to create an image at run time |
| static { |
| initializeImageData(); |
| } |
| |
| private int topIndex = 0; // index of the first visible item |
| private int itemHeight = 0; // height of a table item |
| private Point itemImageExtent = null; // size of the item images. Null unless an image is set for any item |
| private int textHeight = -1; |
| private int contentWidth = 0; // width of the widget data (ie. table rows/tree items) |
| private int horizontalOffset = 0; |
| private Vector selectedItems; // indices of the selected items |
| private SelectableItem lastSelectedItem; // item that was selected last |
| private SelectableItem lastFocusItem; // item that had the focus last. Always equals lastSelectedItem |
| // for mouse selection but may differ for keyboard selection |
| private SelectableItem insertItem; // item that draws the insert marker to indicate the drop location in a drag and drop operation |
| private boolean isInsertAfter; // indicates where the insert marker is rendered, at the top or bottom of 'insertItem' |
| private boolean isCtrlSelection = false; // the most recently selected item was |
| // selected using the Ctrl modifier key |
| private boolean isRemovingAll = false; // true=all items are removed. Used to optimize screen updates and to control item selection on dispose. |
| private boolean hasFocus; // workaround for 1FMITIE |
| private Image uncheckedImage; // deselected check box |
| private Image grayUncheckedImage; // grayed check box |
| private Image checkMarkImage; // check mark for selected check box |
| private Point checkBoxExtent = null; // width, height of the item check box |
| private Listener listener; // event listener used for all events. Events are dispatched |
| // to handler methods in handleEvents(Event) |
| private int drawCount = 0; // used to reimplement setRedraw(boolean) |
| /** |
| * Create a new instance of ScrollableItemWidget. |
| * @param parent - the parent window of the new instance |
| * @param style - window style for the new instance |
| */ |
| SelectableItemWidget(Composite parent, int style) { |
| super(parent, style | SWT.H_SCROLL | SWT.V_SCROLL | SWT.NO_REDRAW_RESIZE); |
| initialize(); |
| } |
| /** |
| * The SelectableItem 'item' has been added to the tree. |
| * Calculate the vertical scroll bar. |
| * Update the screen to display the new item. |
| * @param item - item that has been added to the receiver. |
| */ |
| void addedItem(SelectableItem item, int index) { |
| calculateVerticalScrollbar(); |
| if (getLastFocus() == null) { // if no item has the focus |
| setLastFocus(item, true); // set focus to new (must be first) item |
| } |
| } |
| /** |
| * The SelectableItem 'item' is about to be added to the tree. |
| * @param item - item that is about to be added to the receiver. |
| */ |
| void addingItem(SelectableItem item, int index) { |
| if (index >= 0 && index <= getBottomIndex()) { |
| scrollVerticalAddingItem(index); |
| } |
| } |
| /** |
| * Set the scroll range of the horizontal scroll bar. |
| * Resize the scroll bar if the scroll range maximum |
| * has changed. |
| */ |
| void calculateHorizontalScrollbar() { |
| int newMaximum = getContentWidth(); |
| ScrollBar horizontalBar = getHorizontalBar(); |
| |
| if (horizontalBar.getMaximum() != newMaximum) { |
| // The call to setMaximum is ignored if newMaximum is 0. |
| // Therefore we can not rely on getMaximum to subsequently return the number of |
| // items in the receiver. We always have to use getVisibleItemCount(). |
| // Never rely on getMaximum to return what you set. It may not accept the |
| // value you set. Even if you use a valid value now the implementation may change |
| // later. That's what caused 1FRLOSG. |
| horizontalBar.setMaximum(newMaximum); |
| if (getVerticalBar().getVisible() == false) { // remove these lines |
| horizontalBar.setMaximum(newMaximum); // when PR 1FIG5CG |
| } // is fixed |
| resizeHorizontalScrollbar(); |
| } |
| } |
| /** |
| * Calculate the height of items in the receiver. |
| * Only the image height is calculated if an item height |
| * has already been calculated. Do nothing if both the item |
| * height and the image height have already been calculated |
| */ |
| void calculateItemHeight(SelectableItem item) { |
| GC gc; |
| String itemText; |
| int itemHeight = -1; |
| |
| if (itemImageExtent != null && textHeight != -1) { |
| return; |
| } |
| itemText = item.getText(); |
| if (itemText != null && textHeight == -1) { |
| gc = new GC(this); |
| itemHeight = gc.stringExtent(itemText).y; |
| textHeight = itemHeight; |
| gc.dispose(); |
| } |
| if (itemImageExtent == null) { |
| itemImageExtent = getImageExtent(item); |
| if (itemImageExtent != null) { |
| if (itemImageExtent.y > textHeight) { |
| itemHeight = itemImageExtent.y; |
| } |
| else { |
| itemHeight = textHeight; |
| } |
| } |
| } |
| itemHeight += getItemPadding(); // make sure that there is empty space below the image/text |
| if (itemHeight > getItemHeight()) { // only set new item height if it's higher because new, |
| setItemHeight(itemHeight); // smaller item height may not include an icon |
| } |
| } |
| /** |
| * Calculate the range of items that need to be selected given |
| * the clicked item identified by 'hitItemIndex' |
| * @param hitItemIndex - item that was clicked and that the new |
| * selection range will be based on. This index is relative to |
| * the top index. |
| */ |
| int [] calculateShiftSelectionRange(int hitItemIndex) { |
| int selectionRange[] = new int[] {-1, -1}; |
| SelectableItem closestItem = null; |
| SelectableItem selectedItem; |
| Enumeration selectedItems = getSelectionVector().elements(); |
| |
| while (selectedItems.hasMoreElements() == true) { |
| selectedItem = (SelectableItem) selectedItems.nextElement(); |
| if (closestItem == null) { |
| closestItem = selectedItem; |
| } |
| else |
| if (Math.abs(hitItemIndex - getVisibleIndex(selectedItem)) < |
| Math.abs(hitItemIndex - getVisibleIndex(closestItem))) { |
| closestItem = selectedItem; |
| } |
| } |
| if (closestItem == null) { // no item selected |
| closestItem = getLastSelection(); // item selected last may still have the focus |
| } |
| if (closestItem != null) { |
| selectionRange[0] = getVisibleIndex(closestItem); |
| selectionRange[1] = hitItemIndex; |
| } |
| return selectionRange; |
| } |
| /** |
| * Set the scroll range of the vertical scroll bar. |
| * Resize the scroll bar if the scroll range maximum |
| * has changed. |
| */ |
| void calculateVerticalScrollbar() { |
| int newMaximum = getVisibleItemCount(); |
| ScrollBar verticalBar = getVerticalBar(); |
| |
| // The call to setMaximum is ignored if newMaximum is 0. |
| // Therefore we can not rely on getMaximum to subsequently return the number of |
| // items in the receiver. We always have to use getVisibleItemCount(). |
| // Never rely on getMaximum to return what you set. It may not accept the |
| // value you set. Even if you use a valid value now the implementation may change |
| // later. That's what caused 1FRLOSG. |
| verticalBar.setMaximum(newMaximum); |
| if (getHorizontalBar().getVisible() == false) { // remove these lines |
| verticalBar.setMaximum(newMaximum); // when PR 1FIG5CG |
| } // is fixed |
| resizeVerticalScrollbar(); |
| } |
| |
| /** |
| * Answer the size of the receiver needed to display all items. |
| * The length of the longest item in the receiver is used for the |
| * width. |
| */ |
| public Point computeSize(int wHint, int hHint, boolean changed) { |
| checkWidget(); |
| int width = getContentWidth(); |
| int height = getItemCount() * getItemHeight(); |
| int scrollBarWidth = computeTrim(0, 0, 0, 0).width; |
| |
| if (width == 0) { |
| width = DEFAULT_WIDTH; |
| } |
| if (height == 0) { |
| height = DEFAULT_HEIGHT; |
| } |
| if (wHint != SWT.DEFAULT) { |
| width = wHint; |
| } |
| if (hHint != SWT.DEFAULT) { |
| height = hHint; |
| } |
| if ((getStyle() & SWT.V_SCROLL) != 0) { |
| width += scrollBarWidth; |
| } |
| if ((getStyle() & SWT.H_SCROLL) != 0) { |
| height += scrollBarWidth; |
| } |
| return new Point(width, height); |
| } |
| /** |
| * Do a ctrl+shift selection meaning the ctrl and shift keys |
| * were pressed when the mouse click on an item occurred. |
| * If an already selected item was clicked the focus is moved to |
| * that item. |
| * If the previous selection was a ctrl or ctrl+shift selection |
| * the range between the last selected item and the clicked item |
| * is selected. |
| * Otherwise a regular shift selection is performed. |
| * @param hitItem - specifies the clicked item |
| * @param hitItemIndex - specifies the index of the clicked item |
| * relative to the first item. |
| */ |
| void ctrlShiftSelect(SelectableItem hitItem, int hitItemIndex) { |
| int fromIndex = -1; |
| int toIndex = -1; |
| int lastSelectionIndex = -1; |
| int selectionRange[]; |
| SelectableItem lastSelection = getLastSelection(); |
| |
| if (lastSelection != null) { |
| lastSelectionIndex = getVisibleIndex(lastSelection); |
| } |
| if ((getSelectionVector().contains(hitItem) == true) && // clicked an already selected item? |
| (hitItemIndex != lastSelectionIndex)) { // and click was not on last selected item? |
| setLastSelection(hitItem, true); // set last selection which also sets the focus |
| } |
| else |
| if (isCtrlSelection() == true) { // was last selection ctrl/ctrl+shift selection? |
| fromIndex = lastSelectionIndex; // select from last selection |
| toIndex = hitItemIndex; |
| } |
| else { // clicked outside existing selection range |
| selectionRange = calculateShiftSelectionRange(hitItemIndex); |
| fromIndex = selectionRange[0]; |
| toIndex = selectionRange[1]; |
| } |
| if (fromIndex != -1 && toIndex != -1) { |
| selectRange(fromIndex, toIndex); |
| } |
| } |
| /** |
| * Deselect 'item'. |
| * @param item - item that should be deselected |
| */ |
| void deselect(SelectableItem item) { |
| Vector selectedItems = getSelectionVector(); |
| |
| if ((item != null) && (item.isSelected() == true)) { |
| item.setSelected(false); |
| redrawSelection(item); |
| selectedItems.removeElement(item); |
| } |
| } |
| /** |
| * Deselect all item except 'keepSelected'. |
| * @param keepSelected - item that should remain selected |
| */ |
| void deselectAllExcept(SelectableItem keepSelected) { |
| Vector selectedItems = getSelectionVector(); |
| Vector deselectedItems = new Vector(selectedItems.size()); |
| Enumeration elements = selectedItems.elements(); |
| SelectableItem item; |
| |
| // deselect and repaint previously selected items |
| while (elements.hasMoreElements() == true) { |
| item = (SelectableItem) elements.nextElement(); |
| if (item.isSelected() == true && item != keepSelected) { |
| item.setSelected(false); |
| // always redraw the selection, even if item is redrawn again |
| // in setLastSelection. Fixes 1G0GQ8W |
| redrawSelection(item); |
| deselectedItems.addElement(item); |
| } |
| } |
| elements = deselectedItems.elements(); |
| while (elements.hasMoreElements() == true) { |
| item = (SelectableItem) elements.nextElement(); |
| selectedItems.removeElement(item); |
| } |
| setLastSelection(keepSelected, false); |
| } |
| /** |
| * Deselect all items except those in 'keepSelected'. |
| * @param keepSelected - items that should remain selected |
| */ |
| void deselectAllExcept(Vector keepSelected) { |
| Vector selectedItems = getSelectionVector(); |
| Vector deselectedItems = new Vector(selectedItems.size()); |
| Enumeration elements = selectedItems.elements(); |
| SelectableItem item; |
| |
| // deselect and repaint previously selected items |
| while (elements.hasMoreElements() == true) { |
| item = (SelectableItem) elements.nextElement(); |
| if (item.isSelected() == true && keepSelected.contains(item) == false) { |
| item.setSelected(false); |
| // always redraw the selection, even if item is redrawn again |
| // in setLastSelection. Fixes 1G0GQ8W |
| redrawSelection(item); |
| deselectedItems.addElement(item); |
| } |
| } |
| elements = deselectedItems.elements(); |
| while (elements.hasMoreElements() == true) { |
| item = (SelectableItem) elements.nextElement(); |
| selectedItems.removeElement(item); |
| } |
| if (keepSelected.size() > 0) { |
| setLastSelection((SelectableItem) keepSelected.firstElement(), false); |
| } |
| } |
| /** |
| * Deselect 'item'. Notify listeners. |
| * @param item - item that should be deselected |
| */ |
| void deselectNotify(SelectableItem item) { |
| Event event = new Event(); |
| |
| if (item.isSelected() == true) { |
| deselect(item); |
| setLastSelection(item, true); |
| update(); // looks better when event notification takes long to return |
| } |
| event.item = item; |
| notifyListeners(SWT.Selection, event); |
| } |
| /** |
| * Deselect all items starting at and including 'fromIndex' |
| * stopping at and including 'toIndex'. |
| * @param fromIndex - index relative to the first item where |
| * deselection should start. Deselecion includes 'fromIndex'. |
| * @param toIndex - index relative to the first item where |
| * deselection should stop. Deselecion includes 'toIndex'. |
| */ |
| void deselectRange(int fromIndex, int toIndex) { |
| if (fromIndex > toIndex) { |
| for (int i = toIndex; i <= fromIndex; i++) { |
| deselect(getVisibleItem(i)); |
| } |
| } |
| else |
| if (fromIndex < toIndex) { |
| for (int i = toIndex; i >= fromIndex; i--) { |
| deselect(getVisibleItem(i)); |
| } |
| } |
| setLastSelection(getVisibleItem(fromIndex), true); |
| } |
| /** |
| * Modifier Key Action |
| * None Remove old selection, move selection down one item |
| * Ctrl Keep old selection, move input focus down one item |
| * Shift Extend selection by one item. |
| * Modifier Key is ignored when receiver has single selection style. |
| * @param keyMask - the modifier key that was pressed |
| */ |
| void doArrowDown(int keyMask) { |
| SelectableItem lastFocus = getLastFocus(); |
| SelectableItem newFocus; |
| int focusItemIndex = getVisibleIndex(lastFocus); |
| |
| if (focusItemIndex < (getVisibleItemCount() - 1)) { // - 1 because indices are 0 based |
| focusItemIndex++; |
| newFocus = getVisibleItem(focusItemIndex); |
| if (keyMask == SWT.MOD1 && isMultiSelect() == true) { |
| setLastFocus(newFocus, true); |
| } |
| else |
| if (keyMask == SWT.MOD2 && isMultiSelect() == true) { |
| shiftSelect(newFocus, focusItemIndex); |
| } |
| else { |
| deselectAllExcept(newFocus); |
| selectNotify(newFocus); |
| } |
| } |
| } |
| /** |
| * Modifier Key Action |
| * None Scroll receiver to the left |
| * Ctrl See None above |
| * Shift See None above |
| * @param keyMask - the modifier key that was pressed |
| */ |
| void doArrowLeft(int keyMask) { |
| ScrollBar horizontalBar = getHorizontalBar(); |
| int scrollSelection = horizontalBar.getSelection(); |
| int scrollAmount; |
| |
| if (horizontalBar.getVisible() == false) { |
| return; |
| } |
| scrollAmount = Math.min(HORIZONTAL_SCROLL_INCREMENT, scrollSelection); |
| horizontalBar.setSelection(scrollSelection - scrollAmount); |
| setHorizontalOffset(horizontalBar.getSelection() * -1); |
| } |
| /** |
| * Modifier Key Action |
| * None Scroll receiver to the right |
| * Ctrl See None above |
| * Shift See None above |
| * @param keyMask - the modifier key that was pressed |
| */ |
| void doArrowRight(int keyMask) { |
| ScrollBar horizontalBar = getHorizontalBar(); |
| int scrollSelection = horizontalBar.getSelection(); |
| int scrollAmount; |
| |
| if (horizontalBar.getVisible() == false) { |
| return; |
| } |
| scrollAmount = Math.min( // scroll by the smaller of |
| HORIZONTAL_SCROLL_INCREMENT, // the scroll increment |
| horizontalBar.getMaximum() // and the remaining scroll range |
| - horizontalBar.getPageIncrement() |
| - scrollSelection); |
| horizontalBar.setSelection(scrollSelection + scrollAmount); |
| setHorizontalOffset(horizontalBar.getSelection() * -1); |
| } |
| /** |
| * Modifier Key Action |
| * None Remove old selection, move selection up one item |
| * Ctrl Keep old selection, move input focus up one item |
| * Shift Extend selection by one item. |
| * Modifier Key is ignored when receiver has single selection style. |
| * @param keyMask - the modifier key that was pressed |
| */ |
| void doArrowUp(int keyMask) { |
| SelectableItem lastFocus = getLastFocus(); |
| SelectableItem newFocus; |
| int focusItemIndex = getVisibleIndex(lastFocus); |
| |
| if (focusItemIndex > 0) { |
| focusItemIndex--; |
| newFocus = getVisibleItem(focusItemIndex); |
| if (keyMask == SWT.MOD1 && isMultiSelect() == true) { |
| setLastFocus(newFocus, true); |
| } |
| else |
| if (keyMask == SWT.MOD2 && isMultiSelect() == true) { |
| shiftSelect(newFocus, focusItemIndex); |
| } |
| else { |
| deselectAllExcept(newFocus); |
| selectNotify(newFocus); |
| } |
| } |
| } |
| /** |
| * Perform a selection operation on the item check box. |
| * @param item - the item that was clicked |
| */ |
| void doCheckItem(SelectableItem item) { |
| Event event = new Event(); |
| |
| item.setChecked(!item.getChecked()); |
| event.item = item; |
| event.detail = SWT.CHECK; |
| notifyListeners(SWT.Selection, event); |
| } |
| /** |
| * Free resources. |
| */ |
| void doDispose() { |
| setRemovingAll(true); |
| getSelectionVector().removeAllElements(); |
| lastFocusItem = null; |
| lastSelectedItem = null; |
| if (uncheckedImage != null) { |
| uncheckedImage.dispose(); |
| } |
| if (grayUncheckedImage != null) { |
| grayUncheckedImage.dispose(); |
| } |
| if (checkMarkImage != null) { |
| checkMarkImage.dispose(); |
| } |
| } |
| /** |
| * Modifier Key Action |
| * None Remove old selection, move selection to the |
| * last item |
| * Ctrl Keep old selection, move input focus to the |
| * last item |
| * Shift Extend selection to the last item. |
| * Modifier Key is ignored when receiver has single selection style. |
| * @param keyMask - the modifier key that was pressed |
| */ |
| void doEnd(int keyMask) { |
| SelectableItem lastFocus = getLastFocus(); |
| SelectableItem newFocus; |
| int focusItemIndex = getVisibleIndex(lastFocus); |
| int lastItemIndex = getVisibleItemCount() - 1; // - 1 because indices are 0 based |
| |
| if (focusItemIndex < lastItemIndex) { |
| newFocus = getVisibleItem(lastItemIndex); |
| if (keyMask == SWT.MOD1 && isMultiSelect() == true) { |
| setLastFocus(newFocus, true); |
| } |
| else |
| if (keyMask == SWT.MOD2 && isMultiSelect() == true) { |
| shiftSelect(newFocus, lastItemIndex); |
| } |
| else { |
| deselectAllExcept(newFocus); |
| selectNotify(newFocus); |
| } |
| } |
| } |
| /** |
| * Modifier Key Action |
| * None Remove old selection, move selection to the |
| * first item |
| * Ctrl Keep old selection, move input focus to the |
| * first item |
| * Shift Extend selection to the first item. |
| * Modifier Key is ignored when receiver has single selection style. |
| * @param keyMask - the modifier key that was pressed |
| */ |
| void doHome(int keyMask) { |
| SelectableItem lastFocus = getLastFocus(); |
| SelectableItem newFocus; |
| int firstItemIndex = 0; |
| |
| if (getVisibleIndex(lastFocus) > firstItemIndex) { |
| newFocus = getVisibleItem(firstItemIndex); |
| if (keyMask == SWT.MOD1 && isMultiSelect() == true) { |
| setLastFocus(newFocus, true); |
| } |
| else |
| if (keyMask == SWT.MOD2 && isMultiSelect() == true) { |
| shiftSelect(newFocus, firstItemIndex); |
| } |
| else { |
| deselectAllExcept(newFocus); |
| selectNotify(newFocus); |
| } |
| } |
| } |
| /** |
| * Perform a mouse select action according to the key state |
| * mask in 'eventStateMask'. |
| * Key state mask is ignored when receiver has the single selection |
| * style. |
| * @param item - the item that was clicked |
| * @param itemIndex - the index of the clicked item relative |
| * to the first item of the receiver |
| * @param eventStateMask - the key state mask of the mouse event |
| * @param button - the mouse button that was pressed |
| */ |
| void doMouseSelect(SelectableItem item, int itemIndex, int eventStateMask, int button) { |
| if (button != 1 && item.isSelected() == true) { |
| // If the item is already selected, do not change the selection when using |
| // button 2 or 3. These buttons may invoke drag and drop or open the |
| // context menu for the current selection. |
| return; |
| } |
| if (((eventStateMask & SWT.MOD1) != 0) && |
| ((eventStateMask & SWT.MOD2) != 0) && |
| (isMultiSelect() == true)) { |
| if (getSelectionVector().size() == 0) { // no old selection? |
| selectNotify(item); // do standard CTRL selection |
| } |
| else { |
| ctrlShiftSelect(item, itemIndex); |
| } |
| setCtrlSelection(true); |
| } |
| else |
| if (((eventStateMask & SWT.MOD2) != 0) && (isMultiSelect() == true)) { |
| shiftSelect(item, itemIndex); |
| setCtrlSelection(false); |
| } |
| else |
| if (((eventStateMask & SWT.MOD1) != 0) && (isMultiSelect() == true)) { |
| toggleSelectionNotify(item); |
| setCtrlSelection(true); |
| } |
| else |
| if ((eventStateMask & (SWT.MOD3 | SWT.MOD4)) == 0) { |
| // On MacOSX, holding the control key down while pressing button 1 brings up the context menu. |
| // Do not change the selection in this case. |
| deselectAllExcept(item); |
| selectNotify(item); |
| setCtrlSelection(false); |
| } |
| } |
| /** |
| * Modifier Key Action |
| * None Remove old selection, move selection one page down |
| * Ctrl Keep old selection, move input focus one page down |
| * Shift Extend selection one page down |
| * One page is the number of items that can be displayed in the |
| * receiver's canvas without truncating the last item. |
| * The selection is set to the last item if there is no full page |
| * of items left. |
| * Modifier Key is ignored when receiver has single selection style. |
| * @param keyMask - the modifier key that was pressed |
| */ |
| void doPageDown(int keyMask) { |
| SelectableItem newFocus; |
| int focusItemIndex = getVisibleIndex(getLastFocus()); |
| int lastItemIndex = getVisibleItemCount() - 1; // - 1 because indices are 0 based |
| int visibleItemCount; |
| |
| if (focusItemIndex < lastItemIndex) { |
| visibleItemCount = getItemCountWhole(); |
| focusItemIndex = Math.min( |
| lastItemIndex, |
| focusItemIndex + (visibleItemCount - 1)); |
| newFocus = getVisibleItem(focusItemIndex); |
| if (newFocus == null) { |
| return; |
| } |
| if (keyMask == SWT.MOD1 && isMultiSelect() == true) { |
| setLastFocus(newFocus, true); |
| } |
| else |
| if (keyMask == SWT.MOD2 && isMultiSelect() == true) { |
| shiftSelect(newFocus, focusItemIndex); |
| } |
| else { |
| deselectAllExcept(newFocus); |
| selectNotify(newFocus); |
| } |
| } |
| } |
| /** |
| * Modifier Key Action |
| * None Remove old selection, move selection one page up |
| * Ctrl Keep old selection, move input focus one page up |
| * Shift Extend selection one page up |
| * One page is the number of items that can be displayed in the |
| * receiver's canvas without truncating the last item. |
| * The selection is set to the first item if there is no full page |
| * of items left. |
| * Modifier Key is ignored when receiver has single selection style. |
| * @param keyMask - the modifier key that was pressed |
| */ |
| void doPageUp(int keyMask) { |
| SelectableItem newFocus; |
| int focusItemIndex = getVisibleIndex(getLastFocus()); |
| int visibleItemCount; |
| |
| if (focusItemIndex > 0) { |
| visibleItemCount = getItemCountWhole(); |
| focusItemIndex = Math.max( |
| 0, |
| focusItemIndex - (visibleItemCount - 1)); |
| newFocus = getVisibleItem(focusItemIndex); |
| if (keyMask == SWT.MOD1 && isMultiSelect() == true) { |
| setLastFocus(newFocus, true); |
| } |
| else |
| if (keyMask == SWT.MOD2 && isMultiSelect() == true) { |
| shiftSelect(newFocus, focusItemIndex); |
| } |
| else { |
| deselectAllExcept(newFocus); |
| selectNotify(newFocus); |
| } |
| } |
| } |
| /** |
| * Modifier Key Action |
| * Ctrl Keep old selection, toggle selection of the item |
| * that has the input focus |
| * Shift Extend selection to the item that has the input |
| * focus |
| * Ctrl & Shift Set selection to the item that has input focus |
| * Do nothing if receiver has single selection style. |
| * @param keyMask - the modifier key that was pressed |
| */ |
| |
| void doSpace(int keyMask) { |
| SelectableItem item = getLastFocus(); |
| if (item == null) return; |
| if (item.isCheckable() == true) doCheckItem(item); |
| int itemIndex = getVisibleIndex(item); |
| |
| if (keyMask == SWT.NULL && item.isSelected() == false) { // do simple space select in SINGLE and MULTI mode |
| deselectAllExcept(item); |
| selectNotify(item); |
| return; |
| } |
| if (isMultiSelect() == false) { |
| return; |
| } |
| if (keyMask == SWT.MOD1) { |
| toggleSelectionNotify(item); |
| } |
| else |
| if (((keyMask & SWT.MOD1) != 0) && ((keyMask & SWT.MOD2) != 0)) { |
| deselectAllExcept(item); |
| selectNotify(item); |
| } |
| else |
| if (keyMask == SWT.MOD2) { |
| shiftSelect(item, itemIndex); |
| } |
| } |
| /** |
| * Make sure that free space at the bottom of the receiver is |
| * occupied. |
| * There will be new space available below the last item when the |
| * receiver's height is increased. In this case the receiver |
| * is scrolled down to occupy the new space.if the top item is |
| * not the first item of the receiver. |
| */ |
| void claimBottomFreeSpace() { |
| int clientAreaItemCount = getItemCountWhole(); |
| int topIndex = getTopIndex(); |
| int newTopIndex; |
| int lastItemIndex = getVisibleItemCount() - topIndex; |
| |
| if ((topIndex > 0) && |
| (lastItemIndex > 0) && |
| (lastItemIndex < clientAreaItemCount)) { |
| newTopIndex = Math.max(0, topIndex-(clientAreaItemCount-lastItemIndex)); |
| setTopIndex(newTopIndex, true); |
| } |
| } |
| /** |
| * Make sure that free space at the right side of the receiver is |
| * occupied. |
| * There will be new space available at the right side of the receiver |
| * when the receiver's width is increased. In this case the receiver |
| * is scrolled to the right to occupy the new space.if possible. |
| */ |
| void claimRightFreeSpace() { |
| int clientAreaWidth = getClientArea().width; |
| int newHorizontalOffset = clientAreaWidth - getContentWidth(); |
| |
| if (newHorizontalOffset - getHorizontalOffset() > 0) { // item is no longer drawn past the right border of the client area |
| newHorizontalOffset = Math.min(0, newHorizontalOffset); // align the right end of the item with the right border of the |
| setHorizontalOffset(newHorizontalOffset); // client area (window is scrolled right) |
| } |
| } |
| /** Not used right now. Replace focusIn/focusOut with this method once |
| * Display.getFocusWindow returns the new focus window on FocusOut event |
| * (see 1FMITIE) |
| * The focus has moved in to or out of the receiver. |
| * Redraw the item selection to reflect the focus change. |
| * @param event - the focus change event |
| */ |
| void focusChange(Event event) { |
| Enumeration items = getSelectionVector().elements(); |
| SelectableItem lastFocusItem = getLastFocus(); |
| SelectableItem item; |
| |
| while (items.hasMoreElements() == true) { |
| item = (SelectableItem) items.nextElement(); |
| redrawSelection(item); |
| } |
| if (lastFocusItem != null) { |
| redrawSelection(lastFocusItem); |
| } |
| } |
| /** |
| * The focus has moved in to or out of the receiver. |
| * Redraw the item selection to reflect the focus change. |
| * @param event - the focus change event |
| */ |
| void focusIn(Event event) { |
| Enumeration items = getSelectionVector().elements(); |
| SelectableItem lastFocusItem = getLastFocus(); |
| SelectableItem item; |
| |
| // Workaround for 1FMITIE |
| hasFocus = true; |
| while (items.hasMoreElements() == true) { |
| item = (SelectableItem) items.nextElement(); |
| redrawSelection(item); |
| } |
| if (lastFocusItem != null) { |
| redrawSelection(lastFocusItem); |
| } |
| // Fix blank item on slow machines/VMs. Also fixes 1G0IFMZ. |
| update(); |
| } |
| /** |
| * The focus has moved in to or out of the receiver. |
| * Redraw the item selection to reflect the focus change. |
| * @param event - the focus change event |
| */ |
| void focusOut(Event event) { |
| Enumeration items = getSelectionVector().elements(); |
| SelectableItem lastFocusItem = getLastFocus(); |
| SelectableItem item; |
| |
| // Workaround for 1FMITIE |
| hasFocus = false; |
| while (items.hasMoreElements() == true) { |
| item = (SelectableItem) items.nextElement(); |
| redrawSelection(item); |
| } |
| if (lastFocusItem != null) { |
| redrawSelection(lastFocusItem); |
| } |
| // Fix blank item on slow machines/VMs. Also fixes 1G0IFMZ. |
| update(); |
| } |
| /** |
| * Answer the index of the last item position in the receiver's |
| * client area. |
| * @return 0-based index of the last item position in the tree's |
| * client area. |
| */ |
| int getBottomIndex() { |
| return getTopIndex() + getItemCountTruncated(getClientArea()); |
| } |
| /** |
| * Answer the size of the check box image. |
| * The calculation is cached and assumes that the images for |
| * the checked and unchecked state are the same size. |
| */ |
| Point getCheckBoxExtent() { |
| Image checkedImage; |
| Rectangle imageBounds; |
| |
| if (checkBoxExtent == null) { |
| checkedImage = getUncheckedImage(); |
| if (checkedImage != null) { |
| imageBounds = checkedImage.getBounds(); |
| checkBoxExtent = new Point(imageBounds.width, imageBounds.height); |
| } |
| else { |
| checkBoxExtent = new Point(0, 0); |
| } |
| } |
| return checkBoxExtent; |
| } |
| /** |
| * Answer the image for the selected check box |
| * Answer null if the image couldn't be loaded. |
| */ |
| Image getCheckMarkImage() { |
| |
| if (checkMarkImage == null) { |
| checkMarkImage = new Image(getDisplay(), CheckMarkImageData); |
| } |
| return checkMarkImage; |
| } |
| /** |
| * Answer the width of the receiver's content. |
| * Needs to be set by subclasses. |
| */ |
| int getContentWidth() { |
| return contentWidth; |
| } |
| /** |
| * Answer the horizontal drawing offset used for scrolling. |
| * This is < 0 if the receiver has been scrolled to the left, |
| * > 0 if the receiver has been scrolled to the right and 0 |
| * if the receiver has not been scrolled. |
| */ |
| int getHorizontalOffset() { |
| return horizontalOffset; |
| } |
| |
| /** |
| * Answer the size of item images. Calculated during the item |
| * height calculation. |
| */ |
| Point getImageExtent() { |
| return itemImageExtent; |
| } |
| /** |
| * Answer the image extent of 'item'. Overridden by subclasses. |
| */ |
| Point getImageExtent(SelectableItem item) { |
| Image image = item.getImage(); |
| Rectangle imageBounds; |
| Point imageExtent = null; |
| |
| if (image != null) { |
| imageBounds = image.getBounds(); |
| imageExtent = new Point(imageBounds.width, imageBounds.height); |
| } |
| return imageExtent; |
| } |
| /** |
| * Answer the index of 'item' in the receiver. |
| */ |
| abstract int getIndex(SelectableItem item); |
| /** |
| * Answer the first and last index of items that can be displayed in |
| * the area defined by 'clipRectangle'. This includes partial items. |
| * @return |
| * Array - |
| * First element is the index of the first item in 'clipRectangle'. |
| * Second element is the index of the last item in 'clipRectangle'. |
| */ |
| int[] getIndexRange(Rectangle clipRectangle) { |
| int visibleRange[] = new int[] {0, 0}; |
| |
| visibleRange[0] = clipRectangle.y / getItemHeight(); |
| visibleRange[1] = |
| visibleRange[0] + |
| getItemCountTruncated(clipRectangle) - 1; // - 1 because item index is 0 based |
| return visibleRange; |
| } |
| /** |
| * Return the item that draws the marker indicating the insert location |
| * in a drag and drop operation |
| */ |
| SelectableItem getInsertItem() { |
| return insertItem; |
| } |
| /** |
| * Answer the number of items in the receiver. |
| */ |
| public abstract int getItemCount(); |
| /** |
| * Answer the number of items that can be displayed in 'rectangle'. |
| * The result includes partially visible items. |
| */ |
| int getItemCountTruncated(Rectangle rectangle) { |
| int itemHeight = getItemHeight(); |
| int itemCount = 0; |
| int startIndex; |
| |
| startIndex = rectangle.y / itemHeight; |
| itemCount = Compatibility.ceil(rectangle.y + rectangle.height, itemHeight)-startIndex; |
| return itemCount; |
| } |
| /** |
| * Answer the number of items that can be displayed in the client |
| * area of the receiver. |
| * The result only includes items that completely fit into the |
| * client area. |
| */ |
| int getItemCountWhole() { |
| return getClientArea().height / getItemHeight(); |
| } |
| /** |
| * Answer the height of an item in the receiver. |
| * The item height is the greater of the item icon height and |
| * text height of the first item that has text or an image |
| * respectively. |
| * Calculate a default item height based on the font height if |
| * no item height has been calculated yet. |
| */ |
| public int getItemHeight() { |
| checkWidget(); |
| GC gc; |
| if (itemHeight == 0) { |
| gc = new GC(this); |
| itemHeight = gc.stringExtent("String").y + getItemPadding(); // initial item height=font height + item spacing |
| // use real font height here when available in SWT, instead of GC.textExtent |
| gc.dispose(); |
| } |
| return itemHeight; |
| } |
| /** |
| * Answer the number of pixels that should be added to the item height. |
| */ |
| int getItemPadding() { |
| return 2 + getDisplay().textHighlightThickness; |
| } |
| /** |
| * Answer the item that most recently received the input focus. |
| */ |
| SelectableItem getLastFocus() { |
| return lastFocusItem; |
| } |
| /** |
| * Answer the item that was selected most recently. |
| */ |
| SelectableItem getLastSelection() { |
| return lastSelectedItem; |
| } |
| /** |
| * Answer the event listener used for all events. Events are |
| * dispatched to handler methods in handleEvents(Event). |
| * This scheme saves a lot of inner classes. |
| */ |
| Listener getListener() { |
| return listener; |
| } |
| /** |
| * Answer the y coordinate at which 'item' is drawn. |
| * @param item - SelectableItem for which the paint position |
| * should be returned |
| * @return the y coordinate at which 'item' is drawn. |
| * Return -1 if 'item' is not an item of the receiver |
| */ |
| int getRedrawY(SelectableItem item) { |
| int redrawIndex = getVisibleIndex(item); |
| int redrawY = -1; |
| |
| if (redrawIndex != -1) { |
| redrawY = (redrawIndex - getTopIndex()) * getItemHeight(); |
| } |
| return redrawY; |
| } |
| /** |
| * Answer the number of selected items in the receiver. |
| */ |
| public int getSelectionCount() { |
| checkWidget(); |
| return getSelectionVector().size(); |
| } |
| /** |
| * Answer the selected items of the receiver. |
| * @return The selected items of the receiver stored in a Vector. |
| * Returned Vector is empty if no items are selected. |
| */ |
| Vector getSelectionVector() { |
| return selectedItems; |
| } |
| /** |
| * Answer the width of 'text' in pixel. |
| * Answer 0 if 'text' is null. |
| */ |
| int getTextWidth(String text) { |
| int textWidth = 0; |
| GC gc; |
| |
| if (text != null) { |
| gc = new GC(this); |
| textWidth = gc.stringExtent(text).x; |
| gc.dispose(); |
| } |
| return textWidth; |
| } |
| /** |
| * Answer the index of the first visible item in the receiver's |
| * client area. |
| * @return 0-based index of the first visible item in the |
| * receiver's client area. |
| */ |
| int getTopIndex() { |
| return topIndex; |
| } |
| /** |
| * Answer the image for the deselected check box. |
| */ |
| Image getUncheckedImage() { |
| |
| if (uncheckedImage == null) { |
| uncheckedImage = new Image(getDisplay(), UncheckedImageData); |
| } |
| return uncheckedImage; |
| } |
| |
| /** |
| * Answer the image for the grayed eck box. |
| */ |
| Image getGrayUncheckedImage() { |
| |
| if (grayUncheckedImage == null) { |
| grayUncheckedImage = new Image(getDisplay(), GrayUncheckedImageData); |
| } |
| return grayUncheckedImage; |
| } |
| |
| /** |
| * Answer the index of 'item' in the receiver. |
| * Answer -1 if the item is not visible. |
| * The returned index must refer to a visible item. |
| * Note: |
| * Visible in this context does not neccessarily mean that the |
| * item is displayed on the screen. It only means that the item |
| * would be displayed if it is located inside the receiver's |
| * client area. |
| * Normally, every item of the receiver is visible. |
| */ |
| abstract int getVisibleIndex(SelectableItem item); |
| /** |
| * Answer the SelectableItem located at 'itemIndex' in the |
| * receiver. |
| * @param itemIndex - location of the SelectableItem object |
| * to return |
| */ |
| abstract SelectableItem getVisibleItem(int itemIndex); |
| /** |
| * Answer the number of visible items of the receiver. |
| * Note: |
| * Visible in this context does not neccessarily mean that the |
| * item is displayed on the screen. It only means that the item |
| * would be displayed if it is located inside the receiver's |
| * client area. |
| * Normally every item of the receiver is visible. |
| */ |
| int getVisibleItemCount() { |
| return getItemCount(); |
| } |
| /** |
| * Answer the y coordinate at which 'item' is drawn. |
| * @param item - SelectableItem for which the paint position |
| * should be returned |
| * @return the y coordinate at which 'item' is drawn. |
| * Return -1 if 'item' is null or outside the client area |
| */ |
| abstract int getVisibleRedrawY(SelectableItem item); |
| /** |
| * Handle the events the receiver is listening to. |
| */ |
| void handleEvents(Event event) { |
| switch (event.type) { |
| case SWT.Dispose: |
| doDispose(); |
| break; |
| case SWT.KeyDown: |
| keyDown(event); |
| break; |
| case SWT.Resize: |
| resize(event); |
| break; |
| case SWT.Selection: |
| if (event.widget == getVerticalBar()) { |
| scrollVertical(event); |
| } |
| else |
| if (event.widget == getHorizontalBar()) { |
| scrollHorizontal(event); |
| } |
| break; |
| case SWT.FocusOut: |
| focusOut(event); |
| break; |
| case SWT.FocusIn: |
| focusIn(event); |
| break; |
| case SWT.Traverse: |
| switch (event.detail) { |
| case SWT.TRAVERSE_ESCAPE: |
| case SWT.TRAVERSE_RETURN: |
| case SWT.TRAVERSE_TAB_NEXT: |
| case SWT.TRAVERSE_TAB_PREVIOUS: |
| event.doit = true; |
| break; |
| } |
| break; |
| } |
| } |
| |
| |
| |
| /** |
| * Answer whether 'item' has the input focus. |
| */ |
| boolean hasFocus(SelectableItem item) { |
| return (isFocusControl() && item == getLastFocus()); |
| } |
| /** |
| * Initialize the receiver. Add event listeners and set widget |
| * colors. |
| */ |
| void initialize() { |
| Display display = getDisplay(); |
| ScrollBar horizontalBar = getHorizontalBar(); |
| ScrollBar verticalBar = getVerticalBar(); |
| |
| // listener may be needed by overridden installListeners() |
| listener = new Listener() { |
| public void handleEvent(Event event) {handleEvents(event);} |
| }; |
| setSelectionVector(new Vector()); |
| installListeners(); |
| calculateVerticalScrollbar(); |
| calculateHorizontalScrollbar(); |
| horizontalBar.setMinimum(0); |
| verticalBar.setMinimum(0); |
| horizontalBar.setIncrement(HORIZONTAL_SCROLL_INCREMENT); |
| setForeground(display.getSystemColor(SWT.COLOR_LIST_FOREGROUND)); |
| setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND)); |
| } |
| /** |
| * Initialize the ImageData used for the checked/unchecked images. |
| */ |
| static void initializeImageData() { |
| PaletteData uncheckedPalette = new PaletteData( |
| new RGB[] {new RGB(128, 128, 128), new RGB(255, 255, 255)}); |
| PaletteData grayUncheckedPalette = new PaletteData( |
| new RGB[] {new RGB(128, 128, 128), new RGB(192, 192, 192)}); |
| PaletteData checkMarkPalette = new PaletteData( |
| new RGB[] {new RGB(0, 0, 0), new RGB(252, 3, 251)}); |
| byte[] checkbox = new byte[] {0, 0, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 127, -64, 0, 0}; |
| |
| // Each pixel is represented by one bit in the byte data. |
| // The bit references the palette position (0 or 1). Each pixel row of an image is padded to one byte. |
| // Arguments: width, height, depth, palette, scanline padding, data |
| UncheckedImageData = new ImageData(11, 11, 1, uncheckedPalette, 2, checkbox); |
| GrayUncheckedImageData = new ImageData(11, 11, 1, grayUncheckedPalette, 2, checkbox); |
| CheckMarkImageData = new ImageData(7, 7, 1, checkMarkPalette, 1, new byte[] {-4, -8, 112, 34, 6, -114, -34}); |
| CheckMarkImageData.transparentPixel = 1; |
| } |
| /** |
| * Add event listeners to the tree widget and its scroll bars. |
| */ |
| void installListeners() { |
| Listener listener = getListener(); |
| |
| addListener(SWT.Dispose, listener); |
| addListener(SWT.Resize, listener); |
| addListener(SWT.KeyDown, listener); |
| addListener(SWT.FocusOut, listener); |
| addListener(SWT.FocusIn, listener); |
| addListener(SWT.Traverse, listener); |
| |
| getVerticalBar().addListener(SWT.Selection, listener); |
| getHorizontalBar().addListener(SWT.Selection, listener); |
| } |
| /** |
| * Answer whether the currently selected items were selected |
| * using the ctrl key. |
| */ |
| boolean isCtrlSelection() { |
| return isCtrlSelection; |
| } |
| /** |
| * Answer true if all items in the widget are disposed. |
| * Used to optimize item disposal. Prevents unnecessary screen |
| * updates. |
| */ |
| boolean isRemovingAll() { |
| return isRemovingAll; |
| } |
| /** |
| * Answer whether the receiver has the input focus. |
| * Workaround for 1FMITIE |
| */ |
| public boolean isFocusControl() { |
| return hasFocus; |
| } |
| /** |
| * Return whether the drop insert position is before or after the |
| * item set using motif_setInsertMark. |
| * @return |
| * true=insert position is after the insert item |
| * false=insert position is before the insert item |
| */ |
| boolean isInsertAfter() { |
| return isInsertAfter; |
| } |
| /** |
| * Answer whether the receiver has the MULTI selection style set. |
| * @return |
| * true = receiver is in multiple selection mode. |
| * false = receiver is in single selection mode. |
| */ |
| boolean isMultiSelect() { |
| return ((getStyle() & SWT.MULTI) != 0); |
| } |
| /** |
| * The item identified by 'changedItem' has changed. |
| * Calculate the item height based on the new item data (it might |
| * now have an image which could also be the first image in the |
| * receiver) |
| * Redraw the whole window if the item height has changed. Otherwise |
| * redraw only the changed item or part of it depending on the |
| * 'repaintStartX' and 'repaintWidth' parameters. |
| * @param changedItem - the item that has changed |
| * @param repaintStartX - x position of the item redraw. |
| * @param repaintWidth - width of the item redraw. |
| */ |
| void itemChanged(SelectableItem changedItem, int repaintStartX, int repaintWidth) { |
| int yPosition; |
| int itemHeight; |
| int oldItemHeight = getItemHeight(); |
| Point oldImageExtent = getImageExtent(); |
| |
| calculateItemHeight(changedItem); // make sure that the item height is recalculated |
| // no redraw necessary if redraw width is 0 or item is not visible |
| if (repaintWidth == 0 || (yPosition = getVisibleRedrawY(changedItem)) == -1) { |
| return; |
| } // if changedItem is the first item with image. |
| itemHeight = getItemHeight(); |
| if ((oldItemHeight == itemHeight) && // only redraw changed item if the item height and |
| (oldImageExtent == getImageExtent())) { // image size has not changed. The latter will only change once, |
| // from null to a value-so it's safe to test using != |
| // redrawing outside the client area redraws the widget border on Motif. |
| // adjust the redraw width if necessary. Workaround for 1G4TQRW |
| repaintWidth = Math.min(repaintWidth, getClientArea().width - repaintStartX); |
| if (repaintWidth > 0) { |
| redraw(repaintStartX, yPosition, repaintWidth, itemHeight, true); |
| } |
| } |
| else { |
| redraw(); // redraw all items if the item height has changed |
| } |
| } |
| /** |
| * A key was pressed. Call the appropriate handler method. |
| * @param event - the key event |
| */ |
| void keyDown(Event event) { |
| boolean isCtrlSelection = isCtrlSelection(); |
| |
| if (event.stateMask != SWT.MOD1) { |
| isCtrlSelection = false; |
| } |
| switch (event.keyCode) { |
| case SWT.ARROW_UP: |
| doArrowUp(event.stateMask); |
| break; |
| case SWT.ARROW_DOWN: |
| doArrowDown(event.stateMask); |
| break; |
| case SWT.ARROW_LEFT: |
| doArrowLeft(event.stateMask); |
| break; |
| case SWT.ARROW_RIGHT: |
| doArrowRight(event.stateMask); |
| break; |
| case SWT.PAGE_UP: |
| doPageUp(event.stateMask); |
| break; |
| case SWT.PAGE_DOWN: |
| doPageDown(event.stateMask); |
| break; |
| case SWT.HOME: |
| doHome(event.stateMask); |
| break; |
| case SWT.END: |
| doEnd(event.stateMask); |
| break; |
| default: // no selection occurred, keep previous |
| isCtrlSelection = isCtrlSelection(); // selection type information |
| } |
| if (event.character == ' ') { |
| doSpace(event.stateMask); |
| isCtrlSelection = (event.stateMask == SWT.MOD1); |
| } |
| //forward the enter key pressed to defaultSelection listenters |
| if(event.character == SWT.CR){ |
| Event forwardEvent = new Event(); |
| forwardEvent.item = getLastFocus(); |
| notifyListeners(SWT.DefaultSelection, forwardEvent); |
| } |
| setCtrlSelection(isCtrlSelection); |
| } |
| /** |
| * Sets the drop insert item. |
| * The drop insert item has a visual hint to show where a dragged item |
| * will be inserted when dropped on the tree. |
| * <p> |
| * @param item the insert item |
| * @param after true places the insert mark below 'item'. false places |
| * the insert mark above 'item'. |
| */ |
| void motif_setInsertMark(SelectableItem item, boolean after) { |
| SelectableItem currentItem = getInsertItem(); |
| int redrawY; |
| |
| setInsertItem(item); |
| setInsertAfter(after); |
| if (currentItem != null) { |
| redrawY = getVisibleRedrawY(currentItem); |
| if (redrawY != -1) { |
| currentItem.redrawInsertMark(redrawY); |
| } |
| } |
| if (item != null) { |
| redrawY = getVisibleRedrawY(item); |
| if (redrawY != -1) { |
| item.redrawInsertMark(redrawY); |
| } |
| } |
| } |
| |
| |
| /** |
| * Overridden to implement setRedraw(). Redraw is ignored if setRedraw was |
| * set to false. |
| */ |
| public void redraw () { |
| checkWidget(); |
| if (drawCount == 0) { |
| super.redraw(); |
| } |
| } |
| /** |
| * Overridden to implement setRedraw(). Redraw is ignored if setRedraw was |
| * set to false. |
| */ |
| public void redraw (int x, int y, int width, int height, boolean all) { |
| checkWidget(); |
| if (drawCount == 0) { |
| super.redraw(x, y, width, height, all); |
| } |
| } |
| |
| /** |
| * Redraw the selection of 'item' |
| * @param item - SelectableItem that should have the selection redrawn. |
| */ |
| void redrawSelection(SelectableItem item) { |
| int redrawPosition = getVisibleRedrawY(item); |
| if (redrawPosition != -1) { |
| item.redrawSelection(redrawPosition); |
| } |
| } |
| /** |
| * 'item' has been removed from the receiver. |
| * Update the display and the scroll bars. |
| */ |
| void removedItem(SelectableItem item) { |
| claimBottomFreeSpace(); |
| calculateVerticalScrollbar(); |
| if (getItemCount() == 0) { |
| reset(); |
| } |
| } |
| /** |
| * 'item' is about to be removed from the tree. |
| * Move the selection/input focus if 'item' is selected or has the |
| * input focus, |
| * @param item - item that is about to be removed from the tree. |
| */ |
| void removingItem(SelectableItem item) { |
| SelectableItem nextFocusItem = null; |
| int itemIndex = getVisibleIndex(item); |
| int itemCount = getVisibleItemCount(); |
| |
| // deselect item and remove from selection |
| if (item.isSelected() == true) { |
| getSelectionVector().removeElement(item); |
| } |
| if (item == getLastFocus() && itemCount > 1) { |
| // select previous item if removed item is bottom item |
| // otherwise select next item. Fixes 1GA6L85 |
| if (itemIndex == itemCount - 1) { |
| nextFocusItem = getVisibleItem(itemIndex - 1); |
| } |
| else { |
| nextFocusItem = getVisibleItem(itemIndex + 1); |
| } |
| setLastFocus(nextFocusItem, true); |
| } |
| // ignore items below widget client area |
| if (itemIndex != -1 && itemIndex <= getBottomIndex()) { |
| scrollVerticalRemovedItem(itemIndex); |
| } |
| } |
| /** |
| * Reset state that is dependent on or calculated from the state |
| * of the receiver. |
| */ |
| void reset() { |
| setSelectionVector(new Vector()); |
| setTopIndexNoScroll(0, true); |
| lastSelectedItem = null; |
| lastFocusItem = null; |
| resetItemData(); |
| } |
| /** |
| * Reset state that is dependent on or calculated from the items |
| * of the receiver. |
| */ |
| void resetItemData() { |
| setHorizontalOffset(0); |
| setItemHeight(0); |
| itemImageExtent = null; |
| textHeight = -1; |
| claimRightFreeSpace(); |
| } |
| /** |
| * The receiver has been resized. Update the scroll bars and |
| * make sure that new space is being occupied by items. |
| */ |
| void resize(Event event) { |
| int horizontalPageSize = getHorizontalBar().getPageIncrement(); |
| |
| resizeHorizontalScrollbar(); |
| resizeVerticalScrollbar(); |
| if (getClientArea().width > horizontalPageSize) { // window resized wider? - Do this check here |
| claimRightFreeSpace(); // because claimRightFreeSpace is called elsewhere. |
| } |
| claimBottomFreeSpace(); |
| } |
| /** |
| * Display the horizontal scroll bar if items are drawn off |
| * screen. Update the page size. |
| */ |
| void resizeHorizontalScrollbar() { |
| ScrollBar horizontalBar = getHorizontalBar(); |
| int clientAreaWidth = getClientArea().width; |
| |
| if (clientAreaWidth < getContentWidth()) { |
| if (horizontalBar.getVisible() == false) { |
| horizontalBar.setVisible(true); |
| horizontalBar.setSelection(0); |
| } |
| } |
| else |
| if (horizontalBar.getVisible() == true) { |
| horizontalBar.setVisible(false); |
| } |
| horizontalBar.setThumb(clientAreaWidth); |
| horizontalBar.setPageIncrement(clientAreaWidth); |
| } |
| /** |
| * Display the vertical scroll bar if items are drawn off screen. |
| * Update the page size. |
| */ |
| void resizeVerticalScrollbar() { |
| int clientAreaItemCount = getItemCountWhole(); |
| ScrollBar verticalBar = getVerticalBar(); |
| |
| if (clientAreaItemCount == 0) { |
| return; |
| } |
| if (clientAreaItemCount < getVisibleItemCount()) { |
| if (verticalBar.getVisible() == false) { |
| verticalBar.setVisible(true); |
| } |
| // Only set the page size to something smaller than the scroll |
| // range maximum. Otherwise the scroll selection will be reset. |
| verticalBar.setPageIncrement(clientAreaItemCount); |
| verticalBar.setThumb(clientAreaItemCount); |
| } |
| else |
| if (verticalBar.getVisible() == true) { |
| verticalBar.setVisible(false); |
| } |
| } |
| /** |
| * Scroll the rectangle specified by x, y, width, height to the destination |
| * position. Do nothing if redraw is set to false using setRedraw(). |
| * |
| * @param destX - destination x position of the scrolled rectangle |
| * @param destY - destination y position of the scrolled rectangle |
| * @param x - x location of the upper left corner of the scroll rectangle |
| * @param y - y location of the upper left corner of the scroll rectangle |
| * @param width - width of the scroll rectangle |
| * @param height - height of the scroll rectangle |
| * @param all - not used. Used to be true=scroll children intersecting the |
| * scroll rectangle. |
| */ |
| void scroll(int destX, int destY, int x, int y, int width, int height, boolean all) { |
| if (drawCount == 0) { |
| update(); |
| GC gc = new GC(this); |
| gc.copyArea(x, y, width, height, destX, destY); |
| gc.dispose(); |
| } |
| } |
| /** |
| * Scroll horizontally by 'numPixel' pixel. |
| * @param numPixel - the number of pixel to scroll |
| * numPixel > 0 = scroll to left. numPixel < 0 = scroll to right |
| */ |
| abstract void scrollHorizontal(int numPixel); |
| /** |
| * The position of the horizontal scroll bar has been modified |
| * by the user. |
| * Adjust the horizontal offset to trigger a horizontal scroll. |
| * @param event-the scroll event |
| */ |
| void scrollHorizontal(Event event) { |
| setHorizontalOffset(getHorizontalBar().getSelection() * -1); |
| } |
| void scrollShowItem(int index) { |
| int itemIndexFromTop = index - getTopIndex(); |
| int clientAreaWholeItemCount = getItemCountWhole(); |
| int scrollAmount = 0; |
| |
| if (itemIndexFromTop >= clientAreaWholeItemCount) { // show item below visible items? |
| scrollAmount = itemIndexFromTop; |
| if (clientAreaWholeItemCount > 0) { // will be 0 if showItem is called and receiver hasn't been displayed yet |
| scrollAmount -= clientAreaWholeItemCount - 1; |
| } |
| } |
| else |
| if (itemIndexFromTop < 0) { // show item above visible items? |
| scrollAmount = itemIndexFromTop; |
| } |
| setTopIndex(getTopIndex() + scrollAmount, true); |
| } |
| /** |
| * Scroll vertically by 'scrollIndexCount' items. |
| * @param scrollIndexCount - the number of items to scroll. |
| * scrollIndexCount > 0 = scroll up. scrollIndexCount < 0 = scroll down |
| */ |
| abstract void scrollVertical(int scrollIndexCount); |
| /** |
| * The position of the horizontal scroll bar has been modified |
| * by the user. |
| * Adjust the index of the top item to trigger a vertical scroll. |
| * @param event-the scroll event |
| */ |
| void scrollVertical(Event event) { |
| setTopIndex(getVerticalBar().getSelection(), false); |
| } |
| /** |
| * Scroll items down to make space for a new item added to |
| * the receiver at position 'index'. |
| * @param index - position at which space for one new item |
| * should be made. This index is relative to the first item |
| * of the receiver. |
| */ |
| void scrollVerticalAddingItem(int index) { |
| Rectangle clientArea = getClientArea(); |
| int itemHeight = getItemHeight(); |
| int sourceY = Math.max(0, (index - getTopIndex()) * itemHeight); // need to scroll in visible area |
| |
| scroll( |
| 0, sourceY + itemHeight, // destination x, y |
| 0, sourceY, // source x, y |
| clientArea.width, clientArea.height, true); |
| } |
| /** |
| * Scroll the items below the item at position 'index' up |
| * so that they cover the removed item. |
| * @param index - index of the removed item |
| */ |
| void scrollVerticalRemovedItem(int index) { |
| Rectangle clientArea = getClientArea(); |
| int itemHeight = getItemHeight(); |
| int destinationY = Math.max(0, (index - getTopIndex()) * itemHeight); |
| |
| scroll( |
| 0, destinationY, // destination x, y |
| 0, destinationY + itemHeight, // source x, y |
| clientArea.width, clientArea.height, true); |
| } |
| /** |
| * Select 'item' if it is not selected. |
| * @param item - item that should be selected |
| */ |
| void select(SelectableItem item) { |
| Vector selectedItems = getSelectionVector(); |
| |
| if (item != null && item.isSelected() == false && isRemovingAll() == false) { |
| item.setSelected(true); |
| redrawSelection(item); |
| selectedItems.addElement(item); |
| } |
| } |
| /** |
| * Select 'item' if it is not selected. Send a Selection event |
| * if the selection was changed. |
| * @param item - item that should be selected |
| * @param asyncNotify |
| * true=send the selection event asynchronously |
| * false=send the selection event immediately |
| */ |
| void selectNotify(final SelectableItem item, boolean asyncNotify) { |
| if (isRemovingAll() == false) { |
| if (item.isSelected() == false) { |
| select(item); |
| setLastSelection(item, true); |
| update(); // looks better when event notification takes long to return |
| } |
| if (asyncNotify == false) { |
| Event event = new Event(); |
| event.item = item; |
| notifyListeners(SWT.Selection, event); |
| } |
| else { |
| getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| // Only send a selection event when the item has not been disposed. |
| // Fixes 1GE6XQA |
| if (item.isDisposed() == false) { |
| Event event = new Event(); |
| event.item = item; |
| notifyListeners(SWT.Selection, event); |
| } |
| } |
| }); |
| } |
| } |
| } |
| /** |
| * Select 'item' if it is not selected. Send a Selection event |
| * if the selection was changed. |
| * @param item - item that should be selected |
| */ |
| void selectNotify(SelectableItem item) { |
| selectNotify(item, false); |
| } |
| /** |
| * Select all items of the receiver starting at 'fromIndex' |
| * and including 'toIndex'. |
| */ |
| void selectRange(int fromIndex, int toIndex) { |
| if (fromIndex > toIndex) { |
| for (int i = fromIndex; i > toIndex; i--) { |
| select(getVisibleItem(i)); |
| } |
| } |
| else { |
| for (int i = fromIndex; i < toIndex; i++) { |
| select(getVisibleItem(i)); |
| } |
| } |
| selectNotify(getVisibleItem(toIndex)); // select last item, notifying listeners |
| } |
| /** |
| * Set the width of the receiver's contents to 'newWidth'. |
| * Content width is used to calculate the horizontal scrollbar. |
| */ |
| void setContentWidth(int newWidth) { |
| ScrollBar horizontalBar; |
| boolean scrollBarVisible; |
| |
| if (contentWidth != newWidth) { |
| horizontalBar = getHorizontalBar(); |
| scrollBarVisible = horizontalBar.getVisible(); |
| contentWidth = newWidth; |
| calculateHorizontalScrollbar(); |
| if (scrollBarVisible != horizontalBar.getVisible()) { |
| resizeVerticalScrollbar(); // the vertical scroll bar needs to be resized if the horizontal |
| } // scroll bar was hidden or made visible during recalculation |
| } |
| } |
| /** |
| * Set whether the currently selected items were selected using the |
| * ctrl key. |
| * @param isCtrlSelection - |
| * true = currently selected items were selected using the ctrl key. |
| * false = currently selected items were not selected using the |
| * ctrl key. |
| */ |
| void setCtrlSelection(boolean isCtrlSelection) { |
| this.isCtrlSelection = isCtrlSelection; |
| } |
| /** |
| * The font is changing. Reset the text height to force a |
| * recalculation during itemChanged(). |
| */ |
| public void setFont(Font font) { |
| checkWidget(); |
| super.setFont(font); |
| textHeight = -1; |
| } |
| /** |
| * Set the horizontal drawing offset to 'offset'. |
| * Scroll the receiver's contents according to the offset change. |
| * @param offset - value < 0 = widget contents is drawn left of the client area. |
| */ |
| void setHorizontalOffset(int offset) { |
| int offsetChange = offset - horizontalOffset; |
| if (offsetChange != 0) { |
| scrollHorizontal(offsetChange); |
| horizontalOffset = offset; |
| } |
| } |
| |
| /** |
| * Set whether the drop insert position is before or after the |
| * item set using motif_setInsertMark. |
| * @param after true=insert position is after the insert item |
| * false=insert position is before the insert item |
| */ |
| void setInsertAfter(boolean after) { |
| isInsertAfter = after; |
| } |
| |
| /** |
| * Set the item that draws the marker indicating the insert location |
| * in a drag and drop operation. |
| * @param item the item that draws the insert marker |
| */ |
| void setInsertItem(SelectableItem item) { |
| insertItem = item; |
| } |
| /** |
| * Set the height of the receiver's items to 'height'. |
| */ |
| void setItemHeight(int height) { |
| itemHeight = height; |
| } |
| /** |
| * Set the item that most recently received the input focus |
| * to 'focusItem'. Redraw both, the item that lost focus |
| * and the one that received focus. |
| * @param focusItem - the item that most recently received |
| * the input focus |
| * @param showItem true=new focus item, if any, should be scrolled |
| * into view. false=don't scroll |
| */ |
| void setLastFocus(SelectableItem focusItem, boolean showItem) { |
| SelectableItem oldFocusItem = lastFocusItem; |
| |
| if (focusItem != lastFocusItem) { |
| lastFocusItem = focusItem; |
| if (oldFocusItem != null) { |
| redrawSelection(oldFocusItem); |
| } |
| if (lastFocusItem != null && isFocusControl() == true) { |
| redrawSelection(lastFocusItem); |
| } |
| } |
| if (focusItem != null && showItem == true) { |
| showSelectableItem(focusItem); |
| } |
| } |
| /** |
| * Set the item that was selected most recently to 'selectedItem'. |
| * Set the input focus to that item. |
| * @param selectedItem - the item that was selected most recently |
| * @param showItem true=new focus item, if any, should be scrolled |
| * into view. false=don't scroll |
| */ |
| void setLastSelection(SelectableItem selectedItem, boolean showItem) { |
| if (selectedItem == null) { // always store the item selected last |
| return; |
| } |
| setLastFocus(selectedItem, showItem); |
| lastSelectedItem = selectedItem; |
| } |
| /** |
| * Sets the redraw flag. |
| * @param redraw - |
| * true = redraw and scroll operations are performed normally |
| * false = redraw and scroll operations are ignored |
| */ |
| public void setRedraw (boolean redraw) { |
| checkWidget(); |
| if (redraw) { |
| if (--drawCount == 0) redraw(); |
| } else { |
| drawCount++; |
| } |
| } |
| /** |
| * Set whether all items in the widget are disposed. |
| * Used to optimize item disposal. Prevents unnecessary screen |
| * updates. |
| * @param removingAll |
| * true=all items are removed. |
| * false=normal state, no items or not all items are removed. |
| */ |
| void setRemovingAll(boolean removingAll) { |
| isRemovingAll = removingAll; |
| } |
| /** |
| * Select the items stored in 'selectionItems'. |
| * A SWT.Selection event is not going to be sent. |
| * @param selectionItems - Array containing the items that should |
| * be selected |
| */ |
| void setSelectableSelection(SelectableItem selectionItems[]) { |
| SelectableItem item = null; |
| int selectionCount = selectionItems.length; |
| Vector keepSelected; |
| |
| if (isMultiSelect() == false && selectionCount > 1) { |
| selectionCount = 1; |
| } |
| keepSelected = new Vector(selectionItems.length); |
| for (int i = 0; i < selectionCount; i++) { |
| if (selectionItems[i] != null) { |
| if (selectionItems[i].isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); |
| keepSelected.addElement(selectionItems[i]); |
| } |
| } |
| deselectAllExcept(keepSelected); |
| // select in the same order as all the other selection and deslection methods. |
| // Otherwise setLastSelection repeatedly changes the lastSelectedItem for repeated |
| // selections of the items, causing flash. |
| for (int i = selectionCount - 1; i >= 0; i--) { |
| item = selectionItems[i]; |
| if (item != null) { |
| select(item); |
| } |
| } |
| if (item != null) { |
| setLastSelection(item, true); |
| } |
| } |
| /** |
| * Set the vector used to store the selected items of the receiver |
| * to 'newVector'. |
| * @param newVector - the vector used to store the selected items |
| * of the receiver |
| */ |
| void setSelectionVector(Vector newVector) { |
| selectedItems = newVector; |
| } |
| /** |
| * Scroll the item at 'index' to the top. |
| * @param index - 0-based index of the first visible item in the |
| * receiver's client area. |
| * @param adjustScrollbar - true=set the position of the vertical |
| * scroll bar to the new top index. |
| * false=don't adjust the vertical scroll bar |
| */ |
| void setTopIndex(int index, boolean adjustScrollbar) { |
| int indexDiff = index-topIndex; |
| |
| if (indexDiff != 0) { |
| scrollVertical(indexDiff); |
| setTopIndexNoScroll(index, adjustScrollbar); |
| } |
| } |
| /** |
| * Set the index of the first visible item in the receiver's client |
| * area to 'index'. |
| * @param index - 0-based index of the first visible item in the |
| * receiver's client area. |
| * @param adjustScrollbar - true=set the position of the vertical |
| * scroll bar to the new top index. |
| * false=don't adjust the vertical scroll bar |
| */ |
| void setTopIndexNoScroll(int index, boolean adjustScrollbar) { |
| topIndex = index; |
| if (adjustScrollbar == true) { |
| getVerticalBar().setSelection(index); |
| } |
| } |
| /** |
| * The shift key was pressed when the mouse click on an item |
| * occurred. Do a shift selection. If an already selected item was |
| * clicked the selection is expanded/reduced to that item |
| * @param hitItem - specifies the clicked item |
| * @param hitItemIndex - specifies the index of the clicked item |
| * relative to the first item. |
| */ |
| void shiftSelect(SelectableItem hitItem, int hitItemIndex) { |
| int fromIndex = -1; |
| int toIndex = -1; |
| int lastSelectionIndex = -1; |
| int selectionRange[]; |
| SelectableItem lastSelection = getLastSelection(); |
| |
| if (lastSelection != null) { |
| lastSelectionIndex = getVisibleIndex(lastSelection); |
| } |
| if (isCtrlSelection() == true) { // was last selection ctrl selection? |
| deselectAllExcept(lastSelection); |
| fromIndex = lastSelectionIndex; // select from last selection |
| toIndex = hitItemIndex; |
| } |
| else |
| if (getSelectionVector().contains(hitItem) == true) { // clicked an item already selected? |
| deselectRange(hitItemIndex, lastSelectionIndex); // reduce selection |
| } |
| else { // clicked outside existing selection range |
| selectionRange = calculateShiftSelectionRange(hitItemIndex); |
| fromIndex = selectionRange[0]; |
| toIndex = selectionRange[1]; |
| } |
| if (hitItemIndex == lastSelectionIndex) { // was click on last selected item? |
| return; |
| } |
| if (fromIndex == -1 || toIndex == -1) { // are there previously selected items? |
| toggleSelectionNotify(hitItem); // do a single select. |
| } |
| else { |
| if (((lastSelectionIndex < fromIndex) && (hitItemIndex > fromIndex)) || // does selection reverse direction? |
| ((lastSelectionIndex > fromIndex) && (hitItemIndex < fromIndex))) { |
| deselectAllExcept((SelectableItem) null); // remove old selection |
| } |
| selectRange(fromIndex, toIndex); |
| } |
| } |
| /** |
| * Make 'item' visible by scrolling it into the receiver's client |
| * area if necessary. |
| * @param item - the item that should be made visible to the user. |
| */ |
| void showSelectableItem(SelectableItem item) { |
| if (item.getSelectableParent() != this) { |
| return; |
| } |
| int index = getIndex (item); |
| showSelectableItem(index); |
| } |
| /** |
| * Make 'index' visible by scrolling it into the receiver's client |
| * area if necessary. |
| * @param index - the item index that should be made visible to the user. |
| */ |
| void showSelectableItem(int index) { |
| scrollShowItem(index); |
| scrollShowItem(index); // second call makes sure that the item is still visible |
| // even if the first scroll caused the horizontal scroll |
| // to be displayed and the item to be hidden again. |
| } |
| /** |
| * Show the selection. If there is no selection or the |
| * selection is already visible, this method does nothing. |
| * If the selection is not visible, the top index of the |
| * widget is changed such that the selection becomes visible. |
| */ |
| public void showSelection() { |
| checkWidget(); |
| Vector selection = getSelectionVector(); |
| SelectableItem selectionItem; |
| |
| if (selection.size() > 0) { |
| selectionItem = (SelectableItem) selection.firstElement(); |
| showSelectableItem(selectionItem); |
| } |
| } |
| /** |
| * Sorts the specified range in the array. |
| * |
| * @param array the SelectableItem array to be sorted |
| * @param start the start index to sort |
| * @param end the last + 1 index to sort |
| */ |
| void sort(SelectableItem[] array, int start, int end) { |
| int middle = (start + end) / 2; |
| if (start + 1 < middle) sort(array, start, middle); |
| if (middle + 1 < end) sort(array, middle, end); |
| if (start + 1 >= end) return; // this case can only happen when this method is called by the user |
| if (getVisibleIndex(array[middle-1]) <= getVisibleIndex(array[middle])) return; |
| if (start + 2 == end) { |
| SelectableItem temp = array[start]; |
| array[start] = array[middle]; |
| array[middle] = temp; |
| return; |
| } |
| int i1 = start, i2 = middle, i3 = 0; |
| SelectableItem[] merge = new SelectableItem[end - start]; |
| while (i1 < middle && i2 < end) { |
| merge[i3++] = getVisibleIndex(array[i1]) <= getVisibleIndex(array[i2]) ? |
| array[i1++] : array[i2++]; |
| } |
| if (i1 < middle) System.arraycopy(array, i1, merge, i3, middle - i1); |
| System.arraycopy(merge, 0, array, start, i2 - start); |
| } |
| /** |
| * Toggle the selection of 'item'. |
| * @param item - item that should be selected/deselected |
| */ |
| void toggleSelectionNotify(SelectableItem item) { |
| if (item.isSelected() == true) { |
| deselectNotify(item); |
| } |
| else { |
| selectNotify(item); |
| } |
| } |
| } |