Bug 230833 - item.setImage(..) in setData(..) of virtual Tree works "recursive"
Summary: item.setImage(..) in setData(..) of virtual Tree works "recursive"
Status: NEW
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 3.3.1   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Steve Northover CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-05-07 04:27 EDT by Steffen Klössel CLA
Modified: 2019-09-06 15:30 EDT (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 Steffen Klössel CLA 2008-05-07 04:27:10 EDT
SWT version 3347

Steps To Reproduce:
1. create a Tree widget with SWT.VIRTUAL
2. implement a SWT.setData Listener
3. invoke item.setImage(...) in that listener

More information:

When item.setImage(..) is called, SWT (win32 version) will send a message to the windows system to enforce the system to draw the image:

SWT code in the TreeItem class (line 1650) 
public void setImage (int index, Image image) {
	checkWidget();

.......... CUT ............. 

		/*
		* Bug in Windows.  When I_IMAGECALLBACK is used with TVM_SETITEM
		* to indicate that an image has changed, Windows does not draw
		* the new image.  The fix is to use LPSTR_TEXTCALLBACK to force
		* Windows to ask for the text, causing Windows to ask for both.
		*/
		tvItem.mask |= OS.TVIF_TEXT;
		tvItem.pszText = OS.LPSTR_TEXTCALLBACK;
		OS.SendMessage (hwnd, OS.TVM_SETITEM, 0, tvItem);
	} else {
		boolean drawText = (image == null && oldImage != null) || (image != null && oldImage == null);
		redraw (index, drawText, true);
	}
}

This "Bug-Fix" provokes a subsequent signal from windows, back to the virtual Tree, wich feels coerced to call the setData listener for the next item (and so on) - notwithstanding the previous setData-invokation has not been finished.

In a standard scenario, where you read data from a row of any resultset (addressed by the index/number) of the item, the cursor of the resultset will move away during a setData operation. 

A workaround for me is to buffer all required columns from the items row to put them in the columns at a later moment. But i think that is not fine - not least - because SWT send this "redraw-signal" if setRedraw(..) is false as well <---- !!!

Thanks 
Steffen
Comment 1 Steve Northover CLA 2008-05-21 15:00:36 EDT
Do you have a snippet that shows the problem?
Comment 2 Steffen Klössel CLA 2008-05-27 14:01:43 EDT
(In reply to comment #1)
> Do you have a snippet that shows the problem?

My pleasure!

Thanks 
Steffen

package snippets.bug330833;

import java.sql.ResultSet;

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.program.Program;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

/**
 * The Bug: Normally the tree should display the strings of array RESULTSET_DATA
 * 	in the same order. But this snippet shows you that in the use-case explained
 * 	here: <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=230833">Bug 230833</a>
 *  the tree does not what I think, I may expect :o)</p> 
 */
public class Main230833 {

	
	/**
	 * <p>simulates a {@link ResultSet}</p>
	 */
	private static class SimulatedResultSet {
		
		private static final String[] RESULTSET_DATA = {
			"ROW 0",
			"ROW 1",
			"ROW 2",
			"ROW 3",
			"ROW 4",
			"ROW 5",
			"ROW 6",
			"ROW 7",
			"ROW 8",
			"ROW 9",
		};

		private int current_row = 0;
		
		public void moveTo(int row) {
		
			this.current_row = row;
		}
		
		public String getFieldText() {

			return RESULTSET_DATA[this.current_row];
		}
		
		public int getRowCount() {
			
			return RESULTSET_DATA.length;
		}
		
	}
	
	public static void main(String[] args) {
    
		// create display and shell
		Display display = new Display();
		
		Shell shell = new Shell(display);
		shell.setLayout(new GridLayout(1, true));
		
		shell.setText("Bug 230833");

		Label l = new Label(shell, SWT.NONE);
		l.setText("dbl-click on tree to refresh");
		l.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
		
		// we need one or more images .. 
		Program p = Program.findProgram("bmp");
		final Image bug = new Image(display, p.getImageData());
		
		// open a ResultSet .. e.g. from database or other data sources .. 
		// the ResultSet holds its own position in a CURSOR <-- important
		final SimulatedResultSet myResultSet = new SimulatedResultSet();
		
		// create a VIRTUAL Tree
		final Tree tree = new Tree(shell, SWT.BORDER | SWT.VIRTUAL);
		tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		shell.pack();
		shell.open();

		tree.addListener(SWT.SetData, new Listener() {

			@Override
			public void handleEvent(Event event) {
				
				TreeItem i = (TreeItem) event.item;
				
				// WHAT I WANT TO DO:
				// associate the items index with the "row-number" of
				// this items "source" ... that is my "pragmatic way" which
				// was thwarted by Bug 230833 
				int row = event.index;
				
				// move the results position to the index / row
				myResultSet.moveTo(row);

				// within this call the win32 system (or SWT) will signal
				// a next SWT.SetData event .. 
				// HINT: it seems that this happens only when the trees first item
				// becomes initialized (setData) 
				// when you run this snippet you will see that the first item is NOT "ROW 0"
				i.setImage(bug);
				
				// here the cursor of myResultSet has been modified because
				// this handler becomes invoked due i.setImage(bug)
				// -> Bug 230833
				i.setText(myResultSet.getFieldText());
				
				/* ****************************************************************
				 * I know - in this snippet i could call i.setText() before calling
				 * i.setImage() - but in my real application this handler does not 
				 * initialize the item directly .. there are some methods which are
				 * called by this handler, and i cannot ensure that all other initializations
				 * are done before i.setImage()
				 * ****************************************************************/
			}
			
		});
		
		tree.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				
				Tree t = (Tree) e.widget;
			
				tree.setRedraw(false);
				try {
					t.removeAll();
					t.setItemCount(myResultSet.getRowCount());
				} finally {
					tree.setRedraw(true);
				}
				
			}
		});
		
		// disable redraw 
		tree.setRedraw(false);
		try {
			tree.setItemCount(myResultSet.getRowCount());
		} finally {
			tree.setRedraw(true);
		}
		
		// do the known stuff 
		while (!shell.isDisposed()) {
		  if (!display.readAndDispatch())
		    display.sleep();
		}
		display.dispose();
  	}
}

Comment 3 Eclipse Webmaster CLA 2019-09-06 15:30: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.

If you have further information on the current state of the bug, please add it. 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.