Bug 173571 [Viewers] TreeViewer.updateHasChildren() does not work if item is already expanded
When the ILazyTreePathContentProvider.updateElement() is called, the content provider needs to call TreeViewer.replace() and TreeViewer.setChildCount() to refresh the element's state. However, if the old element at the same index was previously expanded, the setHasChildren() has no effect. This is because a test in TreeViewer.setHasChildren() causes the update to be inored if the item is already expanded. Adding a call to update the child count for the node seems to fix the problem (patch to be attached). Use sample below to reproduce the problem: 1) Expand the first item. 2) Press the "Change Order" button. 3) Expand second item. 4) Press the "Change Order" button again. Problem: The '+' disappears from the second item. public class TestJFace { public static void main(String[] args) { Display display = new Display(); Shell shell = new Shell(display, SWT.SHELL_TRIM); shell.setLayout(new GridLayout()); GridData data = new GridData(GridData.FILL_BOTH); shell.setLayoutData(data); Font font = new Font(display, "Courier", 10, SWT.NORMAL); final TreeViewer treeViewer = new TreeViewer(shell, SWT.VIRTUAL | SWT.BORDER); treeViewer.getControl().setLayoutData(data); final ContentProvider contentProvider = new ContentProvider(treeViewer); treeViewer.setContentProvider(contentProvider); treeViewer.setInput(new Object()); Button button = new Button(shell, SWT.NONE); button.setText("Change Order"); button.addSelectionListener(new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) {} public void widgetSelected(SelectionEvent e) { fgParentFirst = !fgParentFirst; treeViewer.refresh(); } }); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } font.dispose(); } public static boolean fgParentFirst = true; public static class ContentProvider implements ILazyTreePathContentProvider { TreeViewer fViewer; ContentProvider(TreeViewer viewer) { fViewer = viewer; } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } public void updateHasChildren(TreePath path) { if (path.getSegmentCount() == 0) { fViewer.setHasChildren(path, true); } else if (path.getSegmentCount() == 1) { if ("Parent".equals(path.getSegment(0))) { fViewer.setHasChildren(path, true); } else { fViewer.setHasChildren(path, false); } } else { fViewer.setHasChildren(path, false); } } public void updateChildCount(TreePath path, int currentChildCount) { if (path.getSegmentCount() == 0) { fViewer.setChildCount(path, 2); } else if (path.getSegmentCount() == 1) { if ("Parent".equals(path.getSegment(0))) { fViewer.setChildCount(path, 1); } else { fViewer.setChildCount(path, 0); } } else { fViewer.setChildCount(path, 0); } } public void updateElement(TreePath path, int index) { Object object = null; if (path.getSegmentCount() == 0) { if (index == 0) { object = fgParentFirst ? "Parent" : "Non-Parent"; } else if (index == 1) { object = fgParentFirst ? "Non-Parent" : "Parent"; } } else if (path.getSegmentCount() == 1) { if ("Parent".equals(path.getSegment(0))) { object = "Child"; } } if (object != null) { fViewer.replace(path, index, object); updateHasChildren(path.createChildPath(object)); } } public TreePath[] getParents(Object element) { return new TreePath[0]; } public void dispose() {} } }
bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/TreeViewer.java
} else {
				((ILazyTreePathContentProvider) getContentProvider())
						.updateChildCount(treePath, currentChildCount);
			}
		} else {
			((ILazyTreeContentProvider) getContentProvider()).updateChildCount(item.getData(), currentChildCount);
		}
	}
	protected void disassociate(Item item) {
		if (contentProviderIsLazy) {
			
			item.setText(" "); 
		}
		super.disassociate(item);
	}
	protected int doGetColumnCount() {
		return tree.getColumnCount();
	}
	
	/**
	 * Sets a new selection for this viewer and optionally makes it visible.
	 * <p>
	 * <b>Currently the <code>reveal</code> parameter is not honored because
	 * {@link Tree} does not provide an API to only select an item without
	 * scrolling it into view</b>
	 * </p>
	 * 
	 * @param selection
	 *            the new selection
	 * @param reveal