Bug 92763 - [Viewers] CheckboxTableViewer with DeferredContentProvider: getCheckedElements() causes rows out of the visivle range to disappear on a Virtual Table
Summary: [Viewers] CheckboxTableViewer with DeferredContentProvider: getCheckedElement...
Status: CLOSED WONTFIX
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.1   Edit
Hardware: PC Windows XP
: P5 major with 4 votes (vote)
Target Milestone: ---   Edit
Assignee: Platform UI Triaged CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords: helpwanted
Depends on: 92769
Blocks: 509006
  Show dependency tree
 
Reported: 2005-04-26 09:38 EDT by Chad Gustafson CLA
Modified: 2020-04-11 14:48 EDT (History)
5 users (show)

See Also:


Attachments
Zip with Chad and Tods examples (8.50 KB, application/octet-stream)
2005-05-18 08:48 EDT, Tod Creasey CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Chad Gustafson CLA 2005-04-26 09:38:14 EDT
I have a virtual table that performs actions on checked rows after a button 
is clicked. To figure out what rows to act on the button's selection listener 
calls the CheckboxTableViewer's getCheckedElements method. Invoking this 
method causes all of the rows that were not originally visible (the ones that 
you need to scroll to get to) disappear.

Here is the getCheckedElements method from CheckboxTableViewer

    public Object[] getCheckedElements() {
        TableItem[] children = getTable().getItems();
        ArrayList v = new ArrayList(children.length);
        for (int i = 0; i < children.length; i++) {
            TableItem item = children[i];
            if (item.getChecked())
                v.add(item.getData());
        }
        return v.toArray();
    }

If you comment out the getChecked() line of code the table gets drawn 
correctly.

I have included the source code for this test case. Here is how you can 
reproduce it.

1.  Launch TestCheckBoxTableViewer
2.  Scroll to the bottom. You should be able to see all the rows.
3.  Click on the button
4.  Scroll to the bottom. You will see allot of blank rows.
5.  Comment out the call to item.getChecked in MyCheckboxTableViewer's 
getCheckedElements method. Rerun the test case. This time it works.

Here is the test case source code:

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.deferred.AbstractConcurrentModel;
import org.eclipse.jface.viewers.deferred.DeferredContentProvider;
import org.eclipse.jface.viewers.deferred.IConcurrentModelListener;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;

/**
 * 
 * @author cgustafson
 *
 */
public class TestCheckBoxTableViewer {
	
	MyCheckBoxTableViewer tableViewer;
	
	public TestCheckBoxTableViewer(Composite parent){
		createContents(parent);
	}

	private void createContents(Composite parent){
		parent.setLayout(new GridLayout());
		
		Button button = new Button(parent,SWT.PUSH);
		button.setText("Click here to see rows disapear");
		
		Table table = new Table(parent,SWT.VIRTUAL | SWT.V_SCROLL | 
SWT.CHECK);
		table.setHeaderVisible(true);
		table.setLinesVisible(true);
		
		GridData gd = new GridData();
		gd.grabExcessVerticalSpace = true;
		gd.verticalAlignment = SWT.FILL;
		gd.grabExcessHorizontalSpace = true;
		gd.horizontalAlignment = SWT.FILL;
		gd.heightHint = 500;
		table.setLayoutData(gd);

        TableColumn checkCol = new TableColumn(table, SWT.LEFT);
		checkCol.setWidth(20);
		
        TableColumn dateCol = new TableColumn(table, SWT.LEFT);
		dateCol.setText("Date");
		dateCol.setWidth(200);
		
		tableViewer = new MyCheckBoxTableViewer(table);
        tableViewer.setContentProvider(new TestContentProvider(new 
TestComparator(true)));
        tableViewer.setLabelProvider(new TestLabelProvider());
        
		int size = 90;
		Date data[] = new Date[size];
		
		Calendar cal = Calendar.getInstance();

		for (int i=0;i < size;i++){
			cal.add(Calendar.DAY_OF_MONTH,1);
			data[i] = cal.getTime();
		}

		TestModel model = new TestModel();
		model.set(data);
		tableViewer.setInput(model);
		tableViewer.refresh();
		
		button.addSelectionListener(new SelectionAdapter(){
			
			public void widgetSelected(SelectionEvent e) {
				tableViewer.getCheckedElements();	
			}
		});
	}
	
	class TestContentProvider extends DeferredContentProvider{
				
		public void dispose() {
		}
		
		public TestContentProvider(Comparator sorter){
			super(sorter);
		}
	}
	
	class TestLabelProvider implements ITableLabelProvider{
		
		private List listeners = new ArrayList();
 	    
 	    public String getColumnText(Object element, int columnIndex){
			if (columnIndex == 1){
				return element.toString();
			}
			else return null;
 	    }
 	    
 	    public Image getColumnImage(Object element, int columnIndex){
 	        return null;
 	    }
		
	    public void dispose(){
	    }
		
	    public void removeListener(ILabelProviderListener listener){
	        listeners.remove(listener);
	    }
		
	    public boolean isLabelProperty(Object element, String property){
	        return false;
	    }
		
	    public void addListener(ILabelProviderListener listener){
	        listeners.add(listener);
	    }
 	}
	
	class TestComparator implements Comparator{
		
		boolean ascending;
		
		TestComparator(boolean ascending){
			this.ascending = ascending;
		}
		
		public int compare(Object o1, Object o2) {		
		
			Date lhs = (Date) o1;
			Date rhs = (Date) o2;
						
			int answer;
	        if (ascending){
				answer = lhs.compareTo(rhs);
	        }
	        else{
				answer = rhs.compareTo(lhs);
	        }
				
			return answer;
		}	
	}
	
	public class TestModel extends AbstractConcurrentModel {
			    
		private ArrayList data = new ArrayList();
		
		public Object[] getElements() {
			return data.toArray();
		}
				
		/**
		 * Sets the contents to the given array of elements
		 * 
		 * @param newContents new contents of this set
		 */
		public void set(Object[] newContents) {
			data.clear();
			
			for (int i = 0;i<newContents.length;i++){
				data.add(newContents[i]);
			}
			
			IConcurrentModelListener[] listeners = getListeners();
			for (int i = 0; i < listeners.length; i++) {
				IConcurrentModelListener listener = listeners
[i];
				listener.setContents(getElements());
			}
		}
		
		/**
		 * Empties the set
		 */
		public void clear() {
		    Object[] removed = getElements();
		    data.clear();
		    fireRemove(removed);
		}
				
		/* (non-Javadoc)
		 * @see 
org.eclipse.jface.viewers.deferred.IConcurrentContentProvider#requestUpdate
(org.eclipse.jface.viewers.deferred.IConcurrentContentProviderListener)
		 */
		public void requestUpdate(IConcurrentModelListener listener) {
		    listener.setContents(getElements());
		}
		
		public int size(){
			return data.size();
		}
	}
	
	public class MyCheckBoxTableViewer extends CheckboxTableViewer{
		
	    public MyCheckBoxTableViewer(Table table) {
	        super(table);
	    }
		
		// I copied this from CheckboxTableViewer
	    public Object[] getCheckedElements() {
	        TableItem[] children = getTable().getItems();
	        ArrayList v = new ArrayList(children.length);
	        for (int i = 0; i < children.length; i++) {
	            TableItem item = children[i];
				// Comment out the following line to see the 
test case work.
				if (item.getChecked())
	                v.add(item.getData());
	            
	        }
	        return v.toArray();
	    }
	}
	
	
	public static void main(String[] args){
		System.out.println("Starting table");
		Display display = new Display();
		Shell shell = new Shell(display);
		shell.setText("Testing Stuff");
		
		TestCheckBoxTableViewer table = new TestCheckBoxTableViewer
(shell);
		
		shell.pack();
		shell.open();
		
		while (!shell.isDisposed()){
			if (!display.readAndDispatch()){
				display.sleep();
			}
		}
		
		display.dispose();
	}
}

Thanks
Chad
Comment 1 Chad Gustafson CLA 2005-05-04 13:50:34 EDT
I also noticed the same behavior while looping through the rows of the table. 
For example:

int total = table.getItemCount();
for (int i=0;i<total;i++){
   System.out.println(table.getItem(i).getText(1));
}
Comment 2 Tod Creasey CLA 2005-05-18 08:42:30 EDT
This is likely the same problem as Bug 92769. I have made a version of this that
uses a standard content provider (TestCheckBoxTableViewerSimple) that does not
have this issue. I will attach a project that has all of the examples in this
Bug and Bug 92769.
Comment 3 Tod Creasey CLA 2005-05-18 08:48:29 EDT
Created attachment 21326 [details]
Zip with Chad and Tods examples

Here is a zip file with Chads two examples for this and Bug 92769. The versions
with Simple in the title do not use the DeferredContentProvider and do not have
the issues either bug is reporting.
Comment 4 Matthew Kirkley CLA 2009-01-22 10:58:08 EST
Any chance this will ever be fixed?
Comment 5 Boris Bokowski CLA 2009-01-22 12:24:40 EST
(In reply to comment #4)
> Any chance this will ever be fixed?

It has a pretty low priority, but is marked as helpwanted. See http://wiki.eclipse.org/Platform_UI/How_to_Contribute
Comment 6 Matthew Kirkley CLA 2009-01-22 13:13:14 EST
Seems like a pretty big issue... To me deferredcontentprovider + SWT.VIRTUAL doesn't work.
Comment 7 Tod Creasey CLA 2009-01-22 13:18:50 EST
DeferredContentProvider needs a fair amount of work and is not in use within the SDK for that reason.
Comment 8 Matthew Kirkley CLA 2009-01-22 13:26:15 EST
Are there any other work arounds to populate the table in a background thread?
Comment 9 Boris Bokowski CLA 2009-01-22 13:39:05 EST
(In reply to comment #8)
> Are there any other work arounds to populate the table in a background thread?

Peter, can you recommend anything?
Comment 10 Boris Bokowski CLA 2009-11-26 09:47:15 EST
Hitesh is now responsible for watching bugs in the [Viewers] component area.
Comment 11 Eclipse Genie CLA 2020-04-11 14:48:24 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.