Bug 160153 [Viewers] tableViewer.setUseHashlookup causes wrong selections
I've got a table viewer with a virtual table. In the snippet below in removeSelected() I remove an item from my model, then I refresh the view and set the selected item with tableViewer.setSelection(new StructuredSelection(input.get(nextSelectedIndex)), true); If I use tableViewer.setUseHashlookup(true) and I delete the item before the last one, the setSelection above doesn't work and the selection is lost (I can't select the last one item). No problems deleting the other items. If I use tableViewer.setUseHashlookup(false) all works (with 'false' I have another case where setSelection doesn't work, but I can't produce a short code sample to post). package net.quarella.views; import java.util.ArrayList; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILazyContentProvider; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.part.ViewPart; public abstract class TestTableView extends ViewPart { public final String ID; private TableViewer tableViewer; private Label statusLabel; private Action deleteAction; private Action addAction; private Action editAction; private Action refreshAction; private ArrayList<String> input=new ArrayList<String>(); class ViewContentProvider implements ILazyContentProvider { public void inputChanged(Viewer v, Object oldInput, Object newInput) { } public void dispose() { } public void updateElement(int index) { tableViewer.replace(input.get(index), index); } } class ViewLabelProvider extends LabelProvider implements ITableLabelProvider { public String getColumnText(Object element, int columnIndex) { return element.toString(); } public Image getColumnImage(Object element, int columnIndex) { return null; } } public TestTableView(String id) { ID = id; } public void createPartControl(Composite parent) { GridLayout layout = new GridLayout(); layout.numColumns = 1; layout.marginWidth = 0; layout.marginHeight = 0; layout.verticalSpacing = 2; parent.setLayout(layout); tableViewer = new TableViewer(parent, SWT.MULTI | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL | SWT.VIRTUAL | SWT.BORDER); tableViewer.setContentProvider(new ViewContentProvider()); tableViewer.setLabelProvider(new ViewLabelProvider()); //TODO se true problemi di selezione nella cancellazione //se false nel cambio ordinamento tableViewer.setUseHashlookup(true); setColumns(); tableViewer.getTable().setLinesVisible(true); int i; for(i=0; i<7; i++) input.add(new String("Item " + i)); tableViewer.setInput(input); tableViewer.setItemCount(i); GridData layoutData = new GridData(); layoutData.grabExcessHorizontalSpace = true; layoutData.grabExcessVerticalSpace = true; layoutData.horizontalAlignment = GridData.FILL; layoutData.verticalAlignment = GridData.FILL; tableViewer.getTable().setLayoutData(layoutData); statusLabel = new Label(parent, SWT.NONE); layoutData = new GridData(); layoutData.grabExcessHorizontalSpace = true; layoutData.horizontalAlignment = GridData.FILL; layoutData.horizontalIndent = 6; statusLabel.setLayoutData(layoutData); makeActions(); hookContextMenu(); hookDoubleClickAction(); contributeToActionBars(); } private void hookContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { TestTableView.this.fillContextMenu(manager); } }); Menu menu = menuMgr.createContextMenu(tableViewer.getControl()); tableViewer.getControl().setMenu(menu); getSite().registerContextMenu(menuMgr, tableViewer); } private void contributeToActionBars() { IActionBars bars = getViewSite().getActionBars(); fillLocalPullDown(bars.getMenuManager()); fillLocalToolBar(bars.getToolBarManager()); } private void fillLocalPullDown(IMenuManager manager) { manager.add(addAction); manager.add(deleteAction); manager.add(refreshAction); manager.add(new Separator()); } private void fillContextMenu(IMenuManager manager) { manager.add(addAction); manager.add(deleteAction); manager.add(refreshAction); // Other plug-ins can contribute there actions here manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } private void fillLocalToolBar(IToolBarManager manager) { manager.add(addAction); manager.add(deleteAction); manager.add(refreshAction); } private void makeActions() { addAction = new Action() { public void run() { } }; addAction.setText("New"); addAction.setToolTipText("New"); addAction.setImageDescriptor(Activator.getImageDescriptor("icons/elcl16/add.gif")); addAction.setDisabledImageDescriptor(Activator.getImageDescriptor("icons/dlcl16/add.gif")); deleteAction = new Action() { public void run() { removeSelected(); } }; deleteAction.setText("Delete"); deleteAction.setToolTipText("Delete selected items"); deleteAction.setImageDescriptor(Activator.getImageDescriptor("icons/elcl16/delete.gif")); deleteAction.setDisabledImageDescriptor(Activator.getImageDescriptor("icons/dlcl16/delete.gif")); editAction = new Action() { public void run() { // TODO considerare anche l'editing delle righe della tabella } }; refreshAction = new Action() { public void run() { } }; refreshAction.setText("Refresh"); refreshAction.setToolTipText("Refresh table"); refreshAction.setImageDescriptor(Activator.getImageDescriptor("icons/elcl16/refresh.gif")); refreshAction.setDisabledImageDescriptor(Activator.getImageDescriptor("icons/dlcl16/refresh.gif")); getViewSite().getActionBars().setGlobalActionHandler( ActionFactory.DELETE.getId(), deleteAction); } private void hookDoubleClickAction() { tableViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { editAction.run(); } }); } /** * Passing the focus request to the tableViewer's control. */ public void setFocus() { tableViewer.getControl().setFocus(); } private void setColumns() { Table table = tableViewer.getTable(); table.setRedraw(false); while (table.getColumnCount() > 0) { int lastOne = table.getColumnCount() - 1; table.getColumn(lastOne).dispose(); } String[] columnHeaders = { "Item" }; for (int i = 0; i < columnHeaders.length; i++) { TableColumn column = new TableColumn(table, SWT.RIGHT); column.setText(columnHeaders[i]); column.setWidth(120); column.setMoveable(true); column.addListener(SWT.Selection, sortListener); } table.setRedraw(true); table.setHeaderVisible(true); } private void removeSelected() { if (tableViewer.getSelection().isEmpty()) return; int[] indices = tableViewer.getTable().getSelectionIndices(); int nextSelectedIndex = 0; for (int i = 0; i < indices.length; i++) { if (nextSelectedIndex < indices[i]) nextSelectedIndex = indices[i]; } int itemCount = tableViewer.getTable().getItemCount(); for (int i = 0; i < indices.length; i++) { input.remove(indices[i]); itemCount--; nextSelectedIndex--; } nextSelectedIndex++; tableViewer.setItemCount(itemCount); tableViewer.refresh(); if (nextSelectedIndex >= itemCount) nextSelectedIndex = itemCount - 1; if (itemCount > 0) tableViewer.setSelection(new StructuredSelection( input.get(nextSelectedIndex)), true); } private Listener sortListener = new Listener() { public void handleEvent(Event e) { Table table = tableViewer.getTable(); TableColumn sortColumn = table.getSortColumn(); TableColumn currentColumn = (TableColumn) e.widget; int dir = table.getSortDirection(); if (sortColumn == currentColumn) { dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; } else { table.setSortColumn(currentColumn); dir = SWT.UP; } table.setSortDirection(dir); } }; }
bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/TableViewer.java
* <p>
	 * Sets a new selection for this viewer and optionally makes it visible. The
	 * TableViewer implmentation of this method is ineffecient for the
	 * ILazyContentProvider as lookup is done by indices rather than elements
	 * and may require population of the entire table in worse case.
	 * </p>
	 * <p>
	 * Use Table#setSelection(int[] indices) and Table#showSelection() if you
	 * wish to set selection more effeciently when using a ILazyContentProvider.
	 * </p>
	 * 
	 * @param selection
	 *            the new selection
	 * @param reveal
	 *            <code>true</code> if the selection is to be made visible,
	 *            and <code>false</code> otherwise
	 * @see Table#setSelection(int[])
	 * @see Table#showSelection()
	 */
	public void setSelection(ISelection selection, boolean reveal) {
		super.setSelection(selection, reveal);
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List,
	 *      boolean)
	 */
	protected void setSelectionToWidget(List list, boolean reveal) {
		if (list == null) {