Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 42175

Summary: [Viewers] Adding new tree items to an expanded Tree node scrolls away the current selection
Product: [Eclipse Project] Platform Reporter: Tod Creasey <Tod_Creasey>
Component: SWTAssignee: Platform-SWT-Inbox <platform-swt-inbox>
Status: CLOSED WONTFIX QA Contact: Hitesh <hsoliwal>
Severity: normal    
Priority: P5 CC: birsan, carolynmacleod4, grant_gayed, markus.kell.r, Michael.Valenta, Mike_Wilson, mistria, n.a.edgar, snorthov
Version: 3.0Keywords: helpwanted
Target Milestone: ---   
Hardware: PC   
OS: Windows XP   
Whiteboard: stalebug
Bug Depends on:    
Bug Blocks: 42158    

Description Tod Creasey CLA 2003-08-27 14:47:08 EDT
20030826 M3 candidate

When you add items somewhere in a tree if the node you are adding too is 
expanded the tree will scroll to the new point.

STEPS
1) Run the example below
2) Expand Item 2
3) Scroll down to Item 9 and expand downa few levels until Item 2 is not 
visible 
4) Hit the Add button which adds to Items 2. Tree is scrolled to the added 
item so you lose your current context

/*
 * (c) Copyright 2001 MyCorporation.
 * All Rights Reserved.
 */

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

public class AddToTreeTest {

	private static int depth = 10;

	public static void main(String[] args) {
		Display display = new Display();
		Shell shell = new Shell(display);

		final Tree tree =
			new Tree(
				shell,
				SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | 
SWT.BORDER);

		for (int i = 0; i < depth; i++) {
			TreeItem item1 = new TreeItem(tree, SWT.NONE);
			String name = "Item" + String.valueOf(i);
			item1.setText(name);
			createItem(item1, name, 1);
		}

		tree.setBounds(0, 0, 200, 200);

		Button addButton = new Button(shell, SWT.PUSH);
		addButton.setText("Add entry");
		addButton.addSelectionListener(new SelectionListener() {
			/* (non-Javadoc)
			 * @see 
org.eclipse.swt.events.SelectionListener#widgetSelected
(org.eclipse.swt.events.SelectionEvent)
			 */
			public void widgetSelected(SelectionEvent e) {

				TreeItem[] items = tree.getItems();
				if (items.length > 0) {
					for (int i = 0; i < 10; i++) {
						TreeItem item1 = new TreeItem
(items[2], SWT.NONE);
						String name =
							"Item"
								+ 
String.valueOf(
								
	String.valueOf(System.currentTimeMillis()));
						item1.setText(name);

					}

				}
			}
			/* (non-Javadoc)
			 * @see 
org.eclipse.swt.events.SelectionListener#widgetDefaultSelected
(org.eclipse.swt.events.SelectionEvent)
			 */
			public void widgetDefaultSelected(SelectionEvent e) {
			}
		});

		addButton.setBounds(300, 0, 100, 50);
		shell.setSize(400, 400);
		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
		display.dispose();

	}

	private static void createItem(TreeItem parent, String name, int 
count) {
		TreeItem item = new TreeItem(parent, SWT.NONE);
		count++;
		String newName = name + String.valueOf(count);
		item.setText(newName);
		if (count < depth)
			createItem(item, name, count);
	}

} context.

new TreeItem(parent, SWT.NONE);
Comment 1 Grant Gayed CLA 2003-09-04 12:09:22 EDT
Happens on motif and photon also.
Comment 2 Mike Wilson CLA 2003-09-25 10:02:26 EDT
This definately impacts the usability of the concurrency work that is going into R3.0. Has any 
progress been made?
Comment 3 Grant Gayed CLA 2003-09-25 16:06:40 EDT
I'm looking at motif/photon.  Car can you look at win32?
Comment 4 Tod Creasey CLA 2003-09-25 16:32:17 EDT
The problem now is with deletion. When you delete an item the visible area is 
being set to the top.
Comment 5 Carolyn MacLeod CLA 2003-10-15 17:07:06 EDT
What is happening with adding items is that when you insert an item 'above' 
your current position in the tree, the tree has to push you down to make space 
for the item. It's easier to see this if you modify the snippet code to just 
add one item. Of course, if you add 10 items, the tree has to make space for 
10 items, so it pushes down more stuff. Depending on what you are looking at 
you might lose context. If you insert items 'below' your current position in 
the tree, it won't appear to scroll at all, because it pushes down stuff that 
is below you. Not sure how clear this explanation is, but I think it is 
reasonable behavior, because certainly if you were watching the items being 
added, it is what you would expect to see. The only thing you could do is 
getTopItem before inserting and setTopItem when you are done (but this will 
flash). I will investigate the problem with deletion.
Comment 6 Carolyn MacLeod CLA 2003-10-15 17:37:26 EDT
Looked at delete, and the behavior seems reasonable here, too. Try this code. 
It only adds or deletes one item, but this just lets you see better what's 
going on - feel free to add/delete more at once. Expand item2 and any others 
you like, scroll anywhere, then add, add, add, delete, delete, delete (as many 
times as you like). Scroll somewhere else, and repeat. Please tell me if there 
is anything that you think should be done here, or if we should close this bug 
report.
Thanks.

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

public class AddToTreeTest {

	private static int depth = 10;

	public static void main(String[] args) {
		Display display = new Display();
		Shell shell = new Shell(display);

		final Tree tree =
			new Tree(
				shell,
				SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | 
SWT.BORDER);

		for (int i = 0; i < depth; i++) {
			TreeItem item1 = new TreeItem(tree, SWT.NONE);
			String name = "Item" + String.valueOf(i);
			item1.setText(name);
			createItem(item1, name, 1);
		}

		tree.setBounds(0, 0, 200, 200);

		Button addButton = new Button(shell, SWT.PUSH);
		addButton.setText("Add entry");
		addButton.addSelectionListener(new SelectionListener() {
			public void widgetSelected(SelectionEvent e) {
				System.out.println("BEGIN ADD: Selection=" + 
tree.getSelection()[0]);
				System.out.println("BEGIN ADD: TopItem=" + 
tree.getTopItem());
				TreeItem[] items = tree.getItems();
				if (items.length > 0) {
					for (int i = 0; i < 1; i++) {
						TreeItem item1 = new TreeItem
(items[2], SWT.NONE);
						String name = "Item" + 
String.valueOf(String.valueOf(System.currentTimeMillis()));
						item1.setText(name);
						System.out.println("AFTER new 
TreeItem " + i + ", Selection=" + tree.getSelection()[0]);
						System.out.println("AFTER new 
TreeItem " + i + ", TopItem=" + tree.getTopItem());
					}
				}
				System.out.println("END ADD: Selection=" + 
tree.getSelection()[0]);
				System.out.println("END ADD: TopItem=" + 
tree.getTopItem());
			}
			public void widgetDefaultSelected(SelectionEvent e) {
			}
		});

		Button deleteButton = new Button(shell, SWT.PUSH);
		deleteButton.setText("Delete entry");
		deleteButton.addSelectionListener(new SelectionListener() {
			public void widgetSelected(SelectionEvent e) {
				System.out.println("BEGIN DELETE: Selection=" 
+ tree.getSelection()[0]);
				System.out.println("BEGIN DELETE: TopItem=" + 
tree.getTopItem());
				TreeItem[] items = tree.getItems();
				items = items[2].getItems();
				if (items.length > 1) items[1].dispose();
				System.out.println("END DELETE: Selection=" + 
tree.getSelection()[0]);
				System.out.println("END DELETE: TopItem=" + 
tree.getTopItem());
			}
			public void widgetDefaultSelected(SelectionEvent e) {
			}
		});

		addButton.setBounds(300, 0, 100, 50);
		deleteButton.setBounds(300, 100, 100, 50);
		shell.setSize(400, 400);
		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
		display.dispose();
	}

	private static void createItem(TreeItem parent, String name, int 
count) {
		TreeItem item = new TreeItem(parent, SWT.NONE);
		count++;
		String newName = name + String.valueOf(count);
		item.setText(newName);
		if (count < depth)
			createItem(item, name, count);
	}

}
Comment 7 Mike Wilson CLA 2003-10-16 10:09:45 EDT
Please understand: The reason this PR was entered is that, we believe the behavior is *not* 
reasonable. We should talk about this.
Comment 8 Carolyn MacLeod CLA 2003-10-16 10:37:39 EDT
I will investigate further. I'll see what list and table do, and I'll see what 
tree does on the other platforms, and we'll come up with a plan. Stand 
by...  :)
Comment 9 Tod Creasey CLA 2003-10-16 11:37:03 EDT
My test case does not indicate the problem which was with selection 
maintenance.

I will take this PR and commit a fix that Carolyn and I came up with for the 
JFace viewer.
Comment 10 Tod Creasey CLA 2003-10-16 11:57:33 EDT
Here is Caraolyns fix

protected void setSelectionToWidget(List v, boolean reveal) {
		if (v == null) {
			setSelection(new ArrayList(0));
			return;
		}
		int size = v.size();
		List newSelection = new ArrayList(size);
		for (int i = 0; i < size; ++i) {
			// Use internalExpand since item may not yet be 
created.  See 1G6B1AR.
			Widget w = internalExpand(v.get(i), true);
			if (w instanceof Item) {
				newSelection.add(w);
			}
		}
		
		Tree tree = null;
		if(getControl() instanceof Tree)
			tree = (Tree) getControl();
			
		getControl().setRedraw(false);
		TreeItem topItem = null;
		if(tree != null)
			topItem = tree.getTopItem();
		setSelection(newSelection);
		
		if(tree != null && topItem != null)
			tree.setTopItem(topItem);
		getControl().setRedraw(true);
		if (reveal && newSelection.size() > 0) {
			showItem((Item) newSelection.get(0));
		}
	}
Comment 11 Tod Creasey CLA 2003-10-16 12:54:39 EDT
A cleaner fix (one in TreeViewer.setSelection()) has been released for build 
>20031016
Comment 12 Tod Creasey CLA 2003-10-16 12:54:53 EDT
*** Bug 42158 has been marked as a duplicate of this bug. ***
Comment 13 Nick Edgar CLA 2003-10-16 15:14:56 EDT
The new change uses turns redraw off then back on.  This always forces a 
redraw, which we don't want to have happen whenever the selection is 
programmatically changed.

I haven't verified that this is actually a problem, but I'm betting it is.

Comment 14 Mike Wilson CLA 2003-10-16 15:46:54 EDT
I assumed that the "cleaner fix" would not turn redraw off/on. If it is doing that, then we need to 
find some other answer.
Comment 15 Tod Creasey CLA 2003-10-16 17:12:07 EDT
Further testing without it indicates that it is not required so I have just 
removed the turning off of redraw().
Comment 16 Carolyn MacLeod CLA 2003-10-16 17:37:13 EDT
Sorry, Tod - I should have suggested trying it without the redraw off/on, 
however note that you will want to try your exact use case, the one that you 
know causes the scrolling, because when the topItem is forced to scroll by 
setSelection and then the topItem is reset afterwards, what you may see is a 
very disconcerting double-scroll (scroll away, scroll back) that might be 
undesirable.

You may find that the best possible solution avoids calling setSelection 
entirely. So if you can do that, you are much better off...
Comment 17 Nick Edgar CLA 2004-04-23 13:00:08 EDT
The change to restore the top item causes the seizure-inducing flashiness
described in bug 59449.
Steve and Grant have been looking at this and are willing to help come up with a
better solution.

It's unclear that the change should go in setSelection anyway.  This is called a
lot more often than just for adds and removes.  We're probably getting a free
call from preservingSelection, which most viewer modification methods call.

There's no need to call preservingSelection for adds, since this shouldn't mess
up any existing selection.  And for removes, if selected items are removed then
we should just let that happen, as long as notification of the change is sent
afterwards.
Comment 18 Nick Edgar CLA 2004-04-23 13:00:41 EDT
*** Bug 59449 has been marked as a duplicate of this bug. ***
Comment 19 Carolyn MacLeod CLA 2004-04-27 18:35:03 EDT
Have you guys changed something since this bug was opened?
I am running 200404270800, and I tried the snippet and I tried the "switch 
editors in the editor tab folder while watching the Package Explorer tree" use 
case, and I just don't see anything I would call "seizure-inducing 
flashiness". I see a little blip when I switch editors, but it's not that bad. 
I'm really sorry. Maybe my machine is super-fast? Or maybe there's something 
in the way I am clicking or something? Tod, McQ, Nick, can anyone write down a 
very specific use case, maybe switching between some very specific editors? I 
currently have most of the ui, workbench, and core projects loaded, plus swt 
projects, so if you can show the fail case using any 2 files in those 
projects, that would be best - thanks!

For info for this bug, the setSelection code that caches/restores the topItem 
in the tree is:

protected void setSelection(List items) {
	
	Item[] current = getSelection(getTree());
	
	//Don't bother resetting the same selection
	if(haveSameData(items,current))
		return;
	 
	//Cache the current position first
	Tree cachedTree = getTree();
	TreeItem topItem = cachedTree.getTopItem();
	
	TreeItem[] newItems = new TreeItem[items.size()];
	items.toArray(newItems);
	getTree().setSelection(newItems);
		
	//Restore the current position
	if(topItem != null)
		cachedTree.setTopItem(topItem);		
}
Comment 20 Nick Edgar CLA 2004-04-27 22:23:16 EDT
I suppose "jumpiness" is more accurate than "flashiness".
Try:
- load the platform-ui module from CVS
- open new window in Java perspective
- ensure Link with Editor is on in the Package Explorer
- select org.eclipse.jface
- open type (Ctrl+Shift+T) on WWinPluginAction
- open type on ViewerSorter
- switch back and forth between these, with the occasional Collapse All interspersed
Comment 21 Nick Edgar CLA 2004-04-27 22:25:42 EDT
Note that both of these file are at the end of a package with many types, so
scrolling is typically required after expanding.  It does this in multiple
steps, and the scroll bar's handle jumps around a lot.
Comment 22 Carolyn MacLeod CLA 2004-04-28 14:59:48 EDT
No matter what I try, I can't get this to happen on my machine.
I can see the problem on Steve's machine, so I'll have to debug it there.

We compared:
- comctl32 versions (same - 6.0.2800.1106),
- vm (same - 1.4.2_01),
- vm args (same, after I deleted my extra "-Xmx400m" vm args),
- and screen resolution (same, after I made mine 1400 x 1050),
I also tried running with his workspace (it's on the net).
Nothing. I just don't have a problem with flash in the tree.

The only other thing I can think of is that I have JAWS & Window-Eyes 
installed, and they do tend to do strange things to one's computer.  :)

Tod, since you have JAWS & W-E installed too, can you please follow Nick's use 
case, and tell me what you see? Thanks.
Comment 23 Carolyn MacLeod CLA 2004-04-28 15:25:04 EDT
Tod doesn't have the wild flashing, either. Don't know why my machine and his 
machine don't demonstrate the problem - maybe faster graphics cards? Is that 
ridiculous? Anyhow, guess I have to try to work around it for all those flashy 
machines out there.  :(
Comment 24 Steve Northover CLA 2004-04-28 18:01:03 EDT
Tod, I commented out the caching of the top item, and things seem to work ok, 
except for the very first time (after the workbench is started).
Does this work for you? Why did we add the cache? The bug report doesn't 
really give an eclipse use case for the original problem.

protected void setSelection(List items) {
	
	Item[] current = getSelection(getTree());
	
	//Don't bother resetting the same selection
	if(haveSameData(items,current))
		return;
	 
	//Cache the current position first
	//Tree cachedTree = getTree();
	//TreeItem topItem = cachedTree.getTopItem();
	
	TreeItem[] newItems = new TreeItem[items.size()];
	items.toArray(newItems);
	getTree().setSelection(newItems);
		
	//Restore the current position
	//if(topItem != null)
	//	cachedTree.setTopItem(topItem);		
}
Comment 25 Nick Edgar CLA 2004-04-28 22:04:54 EDT
Re comment 23, I'm on Win2K.  Maybe there's a platform difference.
Comment 26 Carolyn MacLeod CLA 2004-04-29 00:08:42 EDT
I considered that possibility, but the problem occurs on Steve's XP machine.
I'm just waiting now for Tod's answer to what the original problem was, 
because this new problem seems to go away (on Steve's machine) if I take out 
the original workaround.
Comment 27 Tod Creasey CLA 2004-04-29 09:05:20 EDT
The original problem was loss of focus - you would add an item and your 
selection would be scrolled away
Comment 28 Carolyn MacLeod CLA 2004-04-29 14:23:36 EDT
I am guessing that you don't mean "loss of keyboard focus", but something more 
along the lines of "loss of context" - right?

Would you be able to come up with an eclipse example where this is bad 
behaviour? I tried "Add class" and it behaves as I would expect - it selects 
the new item, and scrolls to it if necessary.

I am running eclipse with the commented-out caching in setSelection that I 
gave you above (in comment #24).

I am very sorry that I just can't figure out what the problem is. Please drop 
by, and we'll go use Steve's machine and you can show me what needs to be 
fixed.
Comment 29 Markus Keller CLA 2004-04-30 04:10:18 EDT
Comment 0 and comment 2 explain the original problem: You DON'T want to scroll
to the position where the new item was inserted. The item might be put there by
a background task (e.g. one which replaces a "Pending..." label with the real
items), and you don't want the background task to do anything but inserting the
items, in particular no horizontal nor vertical scrolling.

An eclipse-example is bug 42158, which was blocked by this bug.

I guess Nick's comment 17 only meant to criticize the implementation of this
bug's fix, and not the validity of this PR. IMO bug 59449 is not a dup, but only
a consequence of the solution taken for this PR.
Comment 30 Steve Northover CLA 2004-04-30 09:13:59 EDT
It that is correct, why was the setSelection() method patched?  How does this 
method have anything to do with adding items?
Comment 31 Markus Keller CLA 2004-04-30 09:30:53 EDT
I dunno, maybe because of this call hierarchy: ?

setSelection(List) - TreeViewer
- setSelectionToWidget(List, boolean) - AbstractTreeViewer
  - setSelectionToWidget(ISelection, boolean) - StructuredViewer
    - preservingSelection(Runnable) - StructuredViewer
      - refresh(...)
Comment 32 Nick Edgar CLA 2004-04-30 10:30:27 EDT
The call path you show is accurate.  But I agree that setSelection is too far
down to be trying to keep the selection visible.   Even then, the workaround
might not work if the added items appear between the top item and the selected item.
It also doesn't catch the case where viewer.add is called, since it doesn't call
preservingSelection (since adding elements can't affect the selection).

Perhaps internalRefresh should just call showSelection.  But that may still
cause an unwanted scroll, particularly in the case of multiple selection (not
sure how showSelection behaves in that case).  It would be nice if there was an
isSelectionVisible method -- if true, we wouldn't do anything.

Comment 33 Carolyn MacLeod CLA 2004-04-30 15:25:19 EDT
Just one more data point...
I had to reboot my machine into Win2K to check on something else, and I decided 
to see if I have a jumpy tree over here. Note that my Windows 2000 install is 
really really basic, and I do not have any accessibility tools installed.

Note that I do NOT have the jumpy tree here either. So we have ruled out OS, 
VM, and pretty much anything else software-related. It pretty much has to be 
the hardware of my machine that is doing something right. I must have some 
really fast graphics card or something. (Or maybe everyone else has a slow 
one :)   Anyhow, I realize that this does not make the problem go away in the 
general case - it is just interesting.
Comment 34 Nick Edgar CLA 2004-05-03 11:13:52 EDT
Removed re-setting of setTopItem from TreeViewer.setSelection.
This fixes the flashiness problem, at the expense of the selection possibly
getting hidden when concurrent changes are made to the tree.

If this is problematic for the original concurrent use case, then we can
investigate other options, such as using showSelection after a refresh.
Comment 35 Markus Keller CLA 2004-05-14 12:05:59 EDT
> If this is problematic for the original concurrent use case, then we can
> investigate other options, such as using showSelection after a refresh.

Yes, please. The original problem has come back with the removal of the topItem
re-setting code from setSelection().

I guess we need something like preservingTopItem(Runnable updateCode), which is
modeled after StructuredViewer#preservingSelection(..) and which is used by the
add(..), remove(..), and refresh(..) methods.
Comment 36 Steve Northover CLA 2004-05-14 12:12:53 EDT
Sigh,  Nick or someone please hold my hand and show me the original case.  
Reading this PR, the title says "Adding new ...", TC claims "The problem is 
now with deletion." and the (bogus) fix involved selection.  I have no idea 
what is actually going on.
Comment 37 Nick Edgar CLA 2004-05-14 15:30:55 EDT
Markus, can you please provide reproduceable steps showing the problem described
in your last comment?
Comment 38 Markus Keller CLA 2004-05-18 12:14:22 EDT
I20040518-0816

- synchronize your workspace and make sure you have (lots of) incoming changes
- ensure Preferences > Workbench > "Always run in background" is checked
- expand projects in Synchronize view (e.g. press Numpad_Multiply on each project)
- choose "Update" from context menu of first project
- scroll down without selecting anything in the tree
-> when the update is finished, the first item of the tree is revealed (it got
the focus - but not the selection - because the previous focus has been removed)
Comment 39 Nick Edgar CLA 2005-06-21 13:06:20 EDT
From bug 100565 comment 16:
The check for same selection in TreeViewer.setSelection was added in v1.11 of
TreeViewer (by Tod), as part of the fix for bug 42175.
With the removal of get/setTopItem from this code in v1.15 (by me), it's unclear
whether this check is still needed.

Should review this change when this bug is revisited.
Comment 40 Boris Bokowski CLA 2009-11-26 09:55:28 EST
Hitesh is now responsible for watching bugs in the [Viewers] component area.
Comment 41 Eclipse Genie CLA 2019-12-15 13:17:04 EST
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.

--
The automated Eclipse Genie.