diff options
6 files changed, 485 insertions, 19 deletions
diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerTopIndexTests.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerTopIndexTests.java new file mode 100644 index 000000000..f3a764ed7 --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/JFaceViewerTopIndexTests.java @@ -0,0 +1,317 @@ +/******************************************************************************* + * Copyright (c) 2009 Wind River Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipe.debug.tests.viewer.model; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.eclipe.debug.tests.viewer.model.TestModel.TestElement; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; + +/** + * @since 3.6 + */ +public class JFaceViewerTopIndexTests extends TestCase implements ITestModelUpdatesListenerConstants { + + Display fDisplay; + Shell fShell; + TreeModelViewer fViewer; + TestModelUpdatesListener fListener; + + public JFaceViewerTopIndexTests(String name) { + super(name); + } + + /** + * @throws java.lang.Exception + */ + protected void setUp() throws Exception { + fDisplay = PlatformUI.getWorkbench().getDisplay(); + fShell = new Shell(fDisplay/*, SWT.ON_TOP | SWT.SHELL_TRIM*/); + fShell.setSize(300, 100); + fShell.setLayout(new FillLayout()); + + fViewer = createViewer(fDisplay, fShell); + + fListener = new TestModelUpdatesListener(fViewer, false, false); + + fShell.open (); + } + + /** + * @throws java.lang.Exception + */ + protected void tearDown() throws Exception { + fListener.dispose(); + fViewer.getPresentationContext().dispose(); + + // Close the shell and exit. + fShell.close(); + while (!fShell.isDisposed()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + } + + protected void runTest() throws Throwable { + try { + super.runTest(); + } catch (Throwable t) { + throw new ExecutionException("Test failed: " + t.getMessage() + "\n fListener = " + fListener.toString(), t); + } + } + + protected ITreeModelContentProviderTarget getCTargetViewer() { + return (ITreeModelContentProviderTarget)fViewer; + } + + protected TreeModelViewer createViewer(Display display, Shell shell) { + return new TreeModelViewer(fShell, SWT.VIRTUAL | SWT.MULTI, new PresentationContext("TestViewer")); + } + + /** + * Restore REVEAL on simple model with elements without children. + * + */ + public void testRestoreTopIndex() { + TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + + TestModel model = new TestModel(); + + TestElement[] elements = new TestElement[8]; + for (int i = 0; i < elements.length; i++) { + String text = Integer.toString(i + 1); + // elements don't have children + elements[i] = + new TestElement(model, text, new TestElement[0] ); + + } + model.setRoot(new TestElement(model, "root", elements)); + + // Create the listener, only check the first level + fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + + // Stop forcing view updates. + autopopulateAgent.dispose(); + + // scroll to the 5th element + int indexRevealElem = 4; + getCTargetViewer().reveal(TreePath.EMPTY, indexRevealElem); + while(fDisplay.readAndDispatch()) {} + final TreePath originalTopPath = getCTargetViewer().getTopElementPath(); + Assert.assertNotNull("Top item should not be null!", originalTopPath); + Assert.assertEquals(elements[indexRevealElem], originalTopPath.getLastSegment()); + + // Set the viewer input to null. This will trigger the view to save the viewer state. + fListener.reset(true, false); + fListener.addStateUpdates(getCTargetViewer(), TreePath.EMPTY, model.getRootElement()); + + fViewer.setInput(null); + while (!fListener.isFinished(STATE_SAVE_COMPLETE)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + // Set the viewer input back to the model to trigger RESTORE operation. + fListener.reset(false, false); + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + while (fDisplay.readAndDispatch ()) {} + // check if REVEAL was restored OK + final TreePath topPath = getCTargetViewer().getTopElementPath(); + Assert.assertNotNull("Top item should not be null!", topPath); + Assert.assertEquals(elements[indexRevealElem], topPath.getLastSegment()); + Assert.assertEquals(originalTopPath, topPath); + + } + + /** + * Restore REVEAL when having also to restore an expanded element + * that is just above the REVEAL element. + * + * See bug 324100 + */ + public void testRestoreTopAndExpand() { + TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + + TestModel model = new TestModel(); + + TestElement[] elements = new TestElement[10]; + for (int i = 0; i < elements.length; i++) { + String text = Integer.toString(i + 1); + // first element has 2 children + if (i == 0) { + elements[i] = + new TestElement(model, text, new TestElement[] { + new TestElement(model, text + ".1", new TestElement[0] ), + new TestElement(model, text + ".2", new TestElement[0] ) + }); + } else { + // rest of elements don't have children + elements[i] = + new TestElement(model, text, new TestElement[0] ); + } + + } + model.setRoot(new TestElement(model, "root", elements)); + + // Create the listener, only check the first level + fListener.reset(TreePath.EMPTY, model.getRootElement(), 1, false, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + + // Expand first element + fListener.reset(); + fListener.setFailOnRedundantUpdates(false); + int indexFirstElem = 0; + TestElement firstElem = elements[indexFirstElem]; + ModelDelta rootDelta = new ModelDelta(model.getRootElement(), IModelDelta.NO_CHANGE); + ModelDelta delta = model.getBaseDelta(rootDelta); + TreePath firstElemPath = model.findElement(firstElem.getLabel()); + fListener.addUpdates( + firstElemPath, firstElem, 1, + CHILD_COUNT_UPDATES | CHILDREN_UPDATES ); + delta.addNode(firstElem, indexFirstElem, IModelDelta.EXPAND, firstElem.getChildren().length); + + model.postDelta(rootDelta); + + while (!fListener.isFinished(CONTENT_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + // Validate that the first node is expanded + Assert.assertTrue(getCTargetViewer().getExpandedState(firstElemPath) == true); + + // Stop forcing view updates. + autopopulateAgent.dispose(); + + // scroll to the 2nd element + getCTargetViewer().reveal(TreePath.EMPTY, 1); + while(fDisplay.readAndDispatch()) {} + final TreePath originalTopPath = getCTargetViewer().getTopElementPath(); + Assert.assertNotNull("Top item should not be null!", originalTopPath); + Assert.assertEquals(elements[1], originalTopPath.getLastSegment()); + + // Set the viewer input to null. This will trigger the view to save the viewer state. + fListener.reset(true, false); + fListener.addStateUpdates(getCTargetViewer(), TreePath.EMPTY, model.getRootElement()); + fViewer.setInput(null); + while (!fListener.isFinished(STATE_SAVE_COMPLETE)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + // Set the viewer input back to the model + fListener.reset(false, false); + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + while (fDisplay.readAndDispatch ()) {} + // check if REVEAL was restored OK + final TreePath topPath = getCTargetViewer().getTopElementPath(); + Assert.assertNotNull("Top item should not be null!", topPath); + Assert.assertEquals(elements[1], topPath.getLastSegment()); + Assert.assertEquals(originalTopPath, topPath); + + } + + /** + * Restore REVEAL when this operation triggers restoring of an expanded + * element. + * + * See bug 324100 + */ + public void testRestoreTopTriggersExpand() { + TreeModelViewerAutopopulateAgent autopopulateAgent = new TreeModelViewerAutopopulateAgent(fViewer); + + TestModel model = new TestModel(); + + TestElement[] elements = new TestElement[10]; + for (int i = 0; i < elements.length; i++) { + String text = Integer.toString(i + 1); + // last element has 2 children + if (i == elements.length - 1) { + elements[i] = + new TestElement(model, text, new TestElement[] { + new TestElement(model, text + ".1", new TestElement[0] ), + new TestElement(model, text + ".2", new TestElement[0] ) + }); + } else { + // rest of elements don't have children + elements[i] = + new TestElement(model, text, new TestElement[0] ); + } + + } + + fViewer.setAutoExpandLevel(-1); + model.setRoot(new TestElement(model, "root", elements)); + + // Create the listener, only check the first level + fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, false, false); + + // Set the input into the view and update the view. + fViewer.setInput(model.getRootElement()); + + while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + model.validateData(fViewer, TreePath.EMPTY, true); + + int indexLastElem = elements.length-1; + TestElement lastElem = elements[indexLastElem]; + TreePath lastElemePath = model.findElement(lastElem.getLabel()); + + // Validate that the last node is expanded + Assert.assertTrue(getCTargetViewer().getExpandedState(lastElemePath) == true); + + // Stop forcing view updates. + fViewer.setAutoExpandLevel(0); + autopopulateAgent.dispose(); + + // scroll to the element before last element + getCTargetViewer().reveal(TreePath.EMPTY, indexLastElem-1); + while(fDisplay.readAndDispatch()) {} + final TreePath originalTopPath = getCTargetViewer().getTopElementPath(); + Assert.assertNotNull("Top item should not be null!", originalTopPath); + Assert.assertEquals(elements[indexLastElem-1], originalTopPath.getLastSegment()); + + // Set the viewer input to null. This will trigger the view to save the viewer state. + fListener.reset(true, false); + fListener.addStateUpdates(getCTargetViewer(), TreePath.EMPTY, model.getRootElement()); + + fViewer.setInput(null); + while (!fListener.isFinished(STATE_SAVE_COMPLETE)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + // Set the viewer input back to the model. + fListener.reset(false, false); + fViewer.setInput(model.getRootElement()); + while (!fListener.isFinished(ALL_UPDATES_COMPLETE | STATE_RESTORE_COMPLETE)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + while (fDisplay.readAndDispatch ()) {} + // check if REVEAL was restored OK + final TreePath topPath = getCTargetViewer().getTopElementPath(); + Assert.assertNotNull("Top item should not be null!", topPath); + Assert.assertEquals(elements[indexLastElem-1], topPath.getLastSegment()); + Assert.assertEquals(originalTopPath, topPath); + + } +} diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModelUpdatesListener.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModelUpdatesListener.java index 468e7d1c3..d0b831bdf 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModelUpdatesListener.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TestModelUpdatesListener.java @@ -470,6 +470,7 @@ public class TestModelUpdatesListener } public void stateRestoreUpdatesComplete(Object input) { + Assert.assertFalse("RESTORE STATE already complete!", fStateRestoreComplete); fStateRestoreComplete = true; } diff --git a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TreeModelViewerAutopopulateAgent.java b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TreeModelViewerAutopopulateAgent.java index 2600fbebb..38f9eb052 100644 --- a/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TreeModelViewerAutopopulateAgent.java +++ b/org.eclipse.debug.tests/src/org/eclipe/debug/tests/viewer/model/TreeModelViewerAutopopulateAgent.java @@ -36,6 +36,11 @@ public class TreeModelViewerAutopopulateAgent implements IViewerUpdateListener { fViewer.addViewerUpdateListener(this); } + public void dispose() { + fViewer.removeViewerUpdateListener(this); + fViewer = null; + } + public void updateComplete(IViewerUpdate update) { if (update instanceof IChildrenCountUpdate) { TreePath path = update.getElementPath(); diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java index 4d9b0b45b..2e33dc384 100644 --- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java @@ -27,6 +27,7 @@ import org.eclipe.debug.tests.viewer.model.JFaceViewerContentTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerDeltaTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerSelectionTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerStateTests; +import org.eclipe.debug.tests.viewer.model.JFaceViewerTopIndexTests; import org.eclipe.debug.tests.viewer.model.JFaceViewerUpdateTests; import org.eclipe.debug.tests.viewer.model.PresentationContextTests; import org.eclipe.debug.tests.viewer.model.VirtualViewerContentTests; @@ -64,6 +65,7 @@ public class AutomatedSuite extends TestSuite { addTest(new TestSuite(JFaceViewerSelectionTests.class)); addTest(new TestSuite(JFaceViewerStateTests.class)); addTest(new TestSuite(JFaceViewerUpdateTests.class)); + addTest(new TestSuite(JFaceViewerTopIndexTests.class)); // Virtual viewer tests addTest(new TestSuite(VirtualViewerDeltaTests.class)); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java index b7cb28824..4aeebd1b7 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java @@ -10,6 +10,7 @@ * Pawel Piech - Wind River - Bug 205335: ModelContentProvider does not cancel stale updates when switching viewer input * Wind River Systems - Fix for viewer state save/restore [188704] * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489) + * Dorin Ciuca - Top index fix (Bug 324100) *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; @@ -116,13 +117,13 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi /** * Map of updates in progress: element path -> list of requests */ - private Map fRequestsInProgress = new HashMap(); + protected Map fRequestsInProgress = new HashMap(); /** * Map of dependent requests waiting for parent requests to complete: * element path -> list of requests */ - private Map fWaitingRequests = new HashMap(); + protected Map fWaitingRequests = new HashMap(); /** * Map of viewer states keyed by viewer input mementos @@ -132,7 +133,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi /** * Pending viewer state to be restored */ - private ModelDelta fPendingState = null; + protected ModelDelta fPendingState = null; /** * Flag indicating that the content provider is performing @@ -140,6 +141,25 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi */ private boolean fInStateRestore = false; + protected interface IPendingRevealDelta extends IViewerUpdateListener { + /** + * + * @return delta that should be revealed + */ + ModelDelta getDelta(); + + /** + * Dispose the pending operation + */ + void dispose(); + } + + /** + * Postpone restoring REVEAL element until the current updates are complete. + * See bug 324100 + */ + protected IPendingRevealDelta fPendingSetTopItem = null; + private static class CompareRequestKey { CompareRequestKey(TreePath path, IModelDelta delta) { fPath = path; @@ -476,7 +496,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi boolean checkChildrenRealized); public void cancelRestore(final TreePath path, final int flags) { - if (fPendingState == null) { + if (fPendingState == null && fPendingSetTopItem == null) { // Nothing to do return; } @@ -489,6 +509,13 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi } if ((flags & (IModelDelta.SELECT | IModelDelta.REVEAL)) != 0) { + // If we're canceling reveal and this is waiting for updates to complete + // then just cancel it and return + if ((flags & IModelDelta.REVEAL) != 0 && fPendingSetTopItem != null) { + fPendingSetTopItem.dispose(); + return; + } + // If we're canceling select or reveal, cancel it for all of pending deltas final int mask = flags & (IModelDelta.SELECT | IModelDelta.REVEAL); fPendingState.accept(new IModelDeltaVisitor() { @@ -768,6 +795,39 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi System.out.println("\tSAVE DELTA FROM VIEW:\n" + saveDeltaRoot); //$NON-NLS-1$ } + // check if pending restore reveal + if (fPendingSetTopItem != null) { + // set back the pending reveal flag + ModelDelta revealDelta = fPendingSetTopItem.getDelta(); + revealDelta.setFlags(revealDelta.getFlags() | IModelDelta.REVEAL); + + fPendingSetTopItem.dispose(); + + ModelDelta saveDeltaNode = findSubDeltaParent(saveDeltaRoot, revealDelta); + if (saveDeltaNode != null) { + clearRevealFlag(saveDeltaRoot); + boolean childFounded = false; + for (int i = 0; i < saveDeltaNode.getChildDeltas().length; i++) { + ModelDelta child = (ModelDelta)saveDeltaNode.getChildDeltas()[i]; + if (deltasEqual(child, revealDelta)) { + child.setFlags(child.getFlags() | IModelDelta.REVEAL); + childFounded = true; + break; + } + } + + // the node should be added if not found + if (!childFounded) { + saveDeltaNode.setChildCount(revealDelta.getParentDelta().getChildCount()); + copyIntoDelta(revealDelta, saveDeltaNode); + } + } else { + if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("\tSKIPPED: " + revealDelta.getElement()); //$NON-NLS-1$ + } + } + } + if (fPendingState != null) { // If the restore for the current input was never completed, // preserve @@ -1755,11 +1815,17 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi CheckState state = new CheckState(); fPendingState.accept(state); if (state.isComplete()) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("STATE RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$ + // notify restore complete if REVEAL was restored also, otherwise + // postpone until then. + if (fPendingSetTopItem == null) { + if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("STATE RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$ + } + + notifyStateUpdate(fPendingState.getElement(), STATE_RESTORE_SEQUENCE_COMPLETE, null); } - notifyStateUpdate(fPendingState.getElement(), STATE_RESTORE_SEQUENCE_COMPLETE, null); - fPendingState = null; + + fPendingState = null; } } @@ -2177,4 +2243,5 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi } } } + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java index 86eaba6fb..f8d10a49a 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java @@ -9,18 +9,22 @@ * IBM Corporation - initial API and implementation * Wind River Systems - Fix for viewer state save/restore [188704] * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489) + * Dorin Ciuca - Top index fix (Bug 324100) *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; +import org.eclipse.core.runtime.Assert; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.swt.widgets.Display; /** * Content provider for a virtual tree. @@ -560,9 +564,9 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT /** * @param delta */ - void restorePendingStateNode(ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount, boolean checkChildrenRealized) { - TreePath treePath = getViewerTreePath(delta); - ITreeModelContentProviderTarget viewer = getViewer(); + void restorePendingStateNode(final ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount, boolean checkChildrenRealized) { + final TreePath treePath = getViewerTreePath(delta); + final ITreeModelContentProviderTarget viewer = getViewer(); // Attempt to expand the node only if the children are known. if (knowsHasChildren) { @@ -627,15 +631,85 @@ public class TreeModelContentProvider extends ModelContentProvider implements IT } if (setTopItem) { - TreePath parentPath = treePath.getParentPath(); - int index = viewer.findElementIndex(parentPath, treePath.getLastSegment()); - if (index >= 0) { - if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { - System.out.println("\tRESTORE REVEAL: " + treePath.getLastSegment()); //$NON-NLS-1$ + Assert.isTrue(fPendingSetTopItem == null); + final TreePath parentPath = treePath.getParentPath(); + + // listen when current updates are complete and only + // then do the REVEAL + fPendingSetTopItem = new IPendingRevealDelta() { + // Revealing some elements can trigger expanding some of elements + // that have been just revealed. Therefore, we have to check one + // more time after the new triggered updates are completed if we + // have to set again the top index + private int counter = 0; + private Object modelInput = fPendingState.getElement(); + + public void viewerUpdatesBegin() { + } + + public void viewerUpdatesComplete() { + // assume that fRequestsInProgress is empty if we got here + Assert.isTrue(fRequestsInProgress.isEmpty()); + + final Display viewerDisplay = viewer.getDisplay(); + if (fWaitingRequests.isEmpty() && !viewerDisplay.isDisposed()) { + viewerDisplay.asyncExec(new Runnable() { + public void run() { + if (TreeModelContentProvider.this.isDisposed()) { + return; + } + + TreePath topPath = viewer.getTopElementPath(); + if (!treePath.equals(topPath)) { + int index = viewer.findElementIndex(parentPath, treePath.getLastSegment()); + if (index >= 0) { + if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("\tRESTORE REVEAL: " + treePath.getLastSegment()); //$NON-NLS-1$ + } + viewer.reveal(parentPath, index); + + } + } + } + }); + + counter++; + // in case the pending state was already set to null, we assume that + // all others elements are restored, so we don't expect that REVEAL will + // trigger other updates + if (counter > 1 || fPendingState == null) { + dispose(); + } + } + + } + + public void updateStarted(IViewerUpdate update) { + } + + public void updateComplete(IViewerUpdate update) { + } + + public ModelDelta getDelta() { + return delta; + } + + public void dispose() { + // remove myself as viewer update listener + viewer.removeViewerUpdateListener(this); + + // top item is set + fPendingSetTopItem = null; + + if (DEBUG_STATE_SAVE_RESTORE && DEBUG_TEST_PRESENTATION_ID(getPresentationContext())) { + System.out.println("STATE RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$ + } + notifyStateUpdate(modelInput, STATE_RESTORE_SEQUENCE_COMPLETE, null); } - viewer.reveal(parentPath, index); - } - } + + }; + viewer.addViewerUpdateListener(fPendingSetTopItem); + } } // If we know the child count of the element, look for the reveal |