Bug 113472 - [Viewers] New Tableviewer selection methods affect virtual table with ILazyContentProvider
Summary: [Viewers] New Tableviewer selection methods affect virtual table with ILazyCo...
Status: RESOLVED WORKSFORME
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P2 normal (vote)
Target Milestone: 3.2 M4   Edit
Assignee: Boris Bokowski CLA
QA Contact:
URL:
Whiteboard:
Keywords: investigate
Depends on:
Blocks:
 
Reported: 2005-10-23 10:58 EDT by Giovanni Quarella CLA
Modified: 2005-12-08 17:33 EST (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Giovanni Quarella CLA 2005-10-23 10:58:50 EDT
The following code with Eclipse 3.1 works, but with Eclipse 3.2 I have 
problems when I change input.
Exactly when you call for at least the second time

viewer.setInput(movementTableItemProvider);
viewer.setItemCount(movementTableItemProvider.getItemCount());

after setInput this happens:
-----------------------------------------------------------------------
TableView$ViewContentProvider.updateElement(int) line: 109
	TableViewer.virtualSetSelectionToWidget(List, boolean) line: 1151
	TableViewer.setSelectionToWidget(List, boolean) line: 1090
	TableViewer(StructuredViewer).setSelectionToWidget(ISelection, 
boolean) line: 1494
	TableViewer(StructuredViewer).preservingSelection(Runnable) line: 1208
	TableViewer(StructuredViewer).refresh(Object) line: 1262
	TableViewer(StructuredViewer).refresh() line: 1221
	TableViewer.inputChanged(Object, Object) line: 745
	TableViewer(ContentViewer).setInput(Object) line: 248
	TableViewer(StructuredViewer).setInput(Object) line: 1417
	TableView.changeInput(ISelection) line: 277
------------------------------------------------------------------------
When the new input has fewer items than the old one, the method updateElement 
is called for all items of the table not only the visible ones. These items 
doesn't exist at all with new input (the setItemCount is called after setInput)
(I don't have any Selection on the table and I'm not interested to preserve it 
when I change input.)
If I call setItemCount before setInput, in my case I don't have problems with 
non existent items, but the table is still not virtual because the 
updateElement method is called for every index.
With

viewer.setItemCount(0);
viewer.setInput(movementTableItemProvider);
viewer.setItemCount(movementTableItemProvider.getItemCount());

it seems I turn around the bug.


*********************************************************************
*********************************************************************
public class TableView extends ViewPart implements ISelectionListener,
		IPartListener2 {
	public static final String ID 
= "net.mydomain.myrcpapp.views.TableView";

	private TableViewer viewer;

	private ITableItemProviderFactory tableItemProviderFactory;

	private Action action1;

	private Action action2;

	private Action doubleClickAction;

	class ViewContentProvider implements ILazyContentProvider {
		public void inputChanged(Viewer v, Object oldInput, Object 
newInput) {
			if (newInput instanceof TableItemProvider) {

				viewer.getTable().setTopIndex(0);

				if (oldInput instanceof TableItemProvider) {
					if (!((TableItemProvider) 
oldInput).getColumnHeaders()
							.equals(
								
	((TableItemProvider) newInput).getColumnHeaders())) {
						setColumns((TableItemProvider) 
newInput);
					}
				} else if (oldInput == null) {
					setColumns((TableItemProvider) 
newInput);
				}

			}
		}

		public void dispose() {
		}

		public void updateElement(int index) {
			if (viewer.getInput() instanceof TableItemProvider) {
				viewer.replace(
						((TableItemProvider) 
viewer.getInput()).getTableItem(index),
						index);
				// System.out.println("TableItem: " + index);
				// System.out.println("ItemCount: " +
				// viewer.getTable().getItems().length);
			}
		}
	}

	class ViewLabelProvider extends LabelProvider implements
			ITableLabelProvider {
		public String getColumnText(Object obj, int index) {
			return ((ITableItem) obj).getField(index);
		}

		public Image getColumnImage(Object obj, int index) {
			return getImage(obj);
		}

		public Image getImage(Object obj) {
			return PlatformUI.getWorkbench().getSharedImages
().getImage(
					ISharedImages.IMG_OBJ_ELEMENT);
		}
	}

	/*
	 * class NameSorter extends ViewerSorter { }
	 */

	/**
	 * The constructor.
	 */
	public TableView() {
	}

	public void createPartControl(Composite parent) {
		viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
				| SWT.V_SCROLL | SWT.VIRTUAL);

		viewer.setContentProvider(new ViewContentProvider());
		viewer.setLabelProvider(new ViewLabelProvider());
		// viewer.setSorter(new NameSorter());

		viewer.setUseHashlookup(true);

		// getViewSite().getPage().addSelectionListener(this);
		// TODO controllare

		getViewSite().getPage().addPartListener(this);

		makeActions();
		hookContextMenu();
		hookDoubleClickAction();
		contributeToActionBars();
	}

	private void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				TableView.this.fillContextMenu(manager);
			}
		});
		Menu menu = menuMgr.createContextMenu(viewer.getControl());
		viewer.getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, viewer);
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		manager.add(action1);
		manager.add(new Separator());
		manager.add(action2);
	}

	private void fillContextMenu(IMenuManager manager) {
		manager.add(action1);
		manager.add(action2);
		// Other plug-ins can contribute there actions here
		manager.add(new Separator
(IWorkbenchActionConstants.MB_ADDITIONS));
	}

	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(action1);
		manager.add(action2);
	}

	private void makeActions() {
		action1 = new Action() {
			public void run() {
				showMessage("Action 1 executed");
			}
		};
		action1.setText("Action 1");
		action1.setToolTipText("Action 1 tooltip");
		action1.setImageDescriptor(PlatformUI.getWorkbench()
				.getSharedImages()
				.getImageDescriptor
(ISharedImages.IMG_OBJS_INFO_TSK));

		action2 = new Action() {
			public void run() {
				showMessage("Action 2 executed");
			}
		};
		action2.setText("Action 2");
		action2.setToolTipText("Action 2 tooltip");
		action2.setImageDescriptor(PlatformUI.getWorkbench()
				.getSharedImages()
				.getImageDescriptor
(ISharedImages.IMG_OBJS_INFO_TSK));
		doubleClickAction = new Action() {
			public void run() {
				ISelection selection = viewer.getSelection();
				Object obj = ((IStructuredSelection) 
selection).getFirstElement();
				showMessage("Double-click detected on " + 
obj.toString());
			}
		};
	}

	private void hookDoubleClickAction() {
		viewer.addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(DoubleClickEvent event) {
				doubleClickAction.run();
			}
		});
	}

	private void showMessage(String message) {
		MessageDialog.openInformation(viewer.getControl().getShell(),
				"TableView", message);
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		viewer.getControl().setFocus();
	}

	private void changeInput(ISelection navigationViewSelection) {
		if (navigationViewSelection instanceof IStructuredSelection) {

			Object obj = ((IStructuredSelection) 
navigationViewSelection).getFirstElement();

			if (obj instanceof ITableItemProviderFactory) {

				if (!obj.equals(tableItemProviderFactory)) {

									
	
					tableItemProviderFactory = 
(ITableItemProviderFactory) obj;

					TableItemProvider 
movementTableItemProvider = tableItemProviderFactory.getTableItemProvider();

					viewer.setInput
(movementTableItemProvider);
					viewer.setItemCount
(movementTableItemProvider.getItemCount());
				}

			}
		}
	}

	private void setColumns(TableItemProvider tableItemProvider) {
		Table virtualTable = viewer.getTable();

		virtualTable.setRedraw(false);
		// virtualTable.setTopIndex(0);

		while (virtualTable.getColumnCount() > 0) {

			int lastOne = virtualTable.getColumnCount() - 1;

			virtualTable.getColumn(lastOne).dispose();
		}

		String[] columnHeaders = tableItemProvider.getColumnHeaders();

		for (int i = 0; i < columnHeaders.length; i++) {
			TableColumn column = new TableColumn(virtualTable, 
SWT.RIGHT);
			column.setText(columnHeaders[i]);
			column.setWidth(120);
			column.setMoveable(true);
		}

		virtualTable.setRedraw(true);
		virtualTable.setHeaderVisible(true);
	}

	// TODO viene chiamata anche per la stessa selezione!
	public void selectionChanged(IWorkbenchPart part, ISelection 
selection) {
		changeInput(selection);
	}

	public void partActivated(IWorkbenchPartReference partRef) {
		// TODO Auto-generated method stub

	}

	public void partBroughtToTop(IWorkbenchPartReference partRef) {
		// TODO Auto-generated method stub

	}

	public void partClosed(IWorkbenchPartReference partRef) {
		// TODO Auto-generated method stub

	}

	public void partDeactivated(IWorkbenchPartReference partRef) {
		// TODO Auto-generated method stub

	}

	public void partOpened(IWorkbenchPartReference partRef) {
		// TODO Auto-generated method stub

	}

	public void partHidden(IWorkbenchPartReference partRef) {
		if (partRef.getId().equals(ID)) {
			getViewSite().getPage().removeSelectionListener(this);
		}
	}

	public void partVisible(IWorkbenchPartReference partRef) {
		if (partRef.getId().equals(ID)) {
			getViewSite().getPage().addSelectionListener(this);
			changeInput(getViewSite().getPage().getSelection
(NavigationView.ID));
		}

	}

	public void partInputChanged(IWorkbenchPartReference partRef) {
		// TODO Auto-generated method stub

	}

}
Comment 1 Boris Bokowski CLA 2005-10-24 08:57:10 EDT
Giovanni, what exactly happens on line 109 of TableView$ViewContentProvider ?
This looks like a stack trace, but I don't see which exception is being thrown.
Comment 2 Giovanni Quarella CLA 2005-10-24 09:16:45 EDT
(In reply to comment #1)
> Giovanni, what exactly happens on line 109 of TableView$ViewContentProvider ?
> This looks like a stack trace, but I don't see which exception is being 
thrown.

That is a stack trace of a debug session.
I don't have any exception, but a different behavior not present in Eclipse 
3.1 causing the call of updateElement for every element.
On line 109 of my code I put
viewer.replace(((TableItemProvider) viewer.getInput()).getTableItem(index), 
index);

and in your code at virtualSetSelectionToWidget(List list, boolean reveal) of 
Tableviewer class the following piece of code retrieve all table items.
Your comment seems reveal what I'm trying to explain.

if(getContentProvider() instanceof ILazyContentProvider){
			ILazyContentProvider provider = 
				(ILazyContentProvider) getContentProvider();
		
			//Now go through it again until all is done or we are 
no longer virtual
			//This may create all items so it is not a good
			//idea in general.
			//Use #setSelection (int [] indices,boolean reveal) 
instead
			for (int i = 0; i < getTable().getItemCount(); i++) {
				provider.updateElement(i);
				TableItem item = getTable().getItem(i);
				if(virtualElements.contains(item.getData())){
					indices[count++] = i;	
					virtualElements.remove(item.getData());
					if (firstItem == null)
						firstItem = item;
				}
			}
		}
Comment 3 Boris Bokowski CLA 2005-10-24 09:42:37 EDT
This is related to bug 111743 and might be a dupe.
Comment 4 Boris Bokowski CLA 2005-10-28 17:09:46 EDT
After staring at this for some time, my recommendation is to call
viewer.setItemCount from within your lazy content provider's inputChanged method.

Please let me know if this works for you. If this solves the issue, I'll improve
the Javadoc so that others who follow you hopefully don't fall into the same trap.
Comment 5 Giovanni Quarella CLA 2005-10-29 02:39:38 EDT
(In reply to comment #4)
> After staring at this for some time, my recommendation is to call
> viewer.setItemCount from within your lazy content provider's inputChanged 
method.
> Please let me know if this works for you. If this solves the issue, I'll 
improve
> the Javadoc so that others who follow you hopefully don't fall into the same 
trap.

It doesn't work, because all items are retrieved instead of only the visible 
ones.
It works only with this sequence (not in inputChanged method).
viewer.setItemCount(0);
viewer.setInput(myNewInput);
viewer.setItemCount(myNewInput.getItemCount());

So when you scroll, new items are retrieved as needed.
Comment 6 Boris Bokowski CLA 2005-10-29 18:38:40 EDT
OK, but now you are seeing another bug (see bug 111743), which I have fixed
yesterday.
Comment 7 Giovanni Quarella CLA 2005-10-30 11:21:59 EST
(In reply to comment #6)
> OK, but now you are seeing another bug (see bug 111743), which I have fixed
> yesterday.

I download Eclipse N20051030-0010 and it works. Another question... If I call 
setItemCount(300) and then setItemtCount(100) should I care to release/dispose 
something?
In this build seems present another bug. When I start my rcp app, the 
tableviewer is empty, because the navigation view who controls the filling of 
table doesn't receive at startup the default focus on the first Tree node as 
it happend with Eclipse 3.1.
Comment 8 Boris Bokowski CLA 2005-12-08 17:33:53 EST
Setting the item count to a smaller number will dispose of the items that are no longer needed. Marking this as worksforme since I did not change any code for this. 

Regarding the bug you describe in comment #7, could you open another bug and describe it in more detail? Thanks!