From 79273b0411dfed7931ae6a28ee1e55458c7cbf31 Mon Sep 17 00:00:00 2001 From: Martin Oberhuber < martin.oberhuber@windriver.com> Date: Tue, 15 May 2007 20:14:37 +0000 Subject: [PATCH] [181145] Restore selection after refresh --- .../events/ISystemResourceChangeEvents.java | 77 ++++++++++++++-- .../rse/internal/ui/view/SystemView.java | 24 ++++- .../SystemViewLabelAndContentProvider.java | 2 +- .../rse/internal/ui/view/SystemViewPart.java | 4 +- .../rse/ui/actions/SystemRefreshAction.java | 71 +++++++++++---- .../SystemDeferredTreeContentManager.java | 88 +++++++++++++++++-- 6 files changed, 227 insertions(+), 39 deletions(-) diff --git a/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/events/ISystemResourceChangeEvents.java b/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/events/ISystemResourceChangeEvents.java index 2a76b0a0c9c..7c0bf28e60b 100644 --- a/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/events/ISystemResourceChangeEvents.java +++ b/rse/plugins/org.eclipse.rse.core/src/org/eclipse/rse/core/events/ISystemResourceChangeEvents.java @@ -16,6 +16,8 @@ package org.eclipse.rse.core.events; +import org.eclipse.rse.core.subsystems.IRemoteObjectIdentifier; + /** * Interface of event ID constants */ @@ -126,29 +128,78 @@ public interface ISystemResourceChangeEvents * The event is an icon change event */ public static final int EVENT_ICON_CHANGE = 81; + /** - * The event is a full refresh event: all expanded sub-nodes are re-queried for their children, unexpanded nodes lose their children cache. + * Refresh the single item passed in the "source" field of the event. + * + * All expanded sub-nodes are re-queried for their children, unexpanded + * nodes lose their children cache. Selection is not maintained by this + * event (use EVENT_REFRESH_REMOTE instead to maintain the selection). */ public static final int EVENT_REFRESH = 82; + /** - * The event is a selection-dependent refresh event: all expanded sub-nodes are re-queried for their children, unexpanded nodes lose their children cache. + * Refresh the items currently selected in the SystemView. + * + * All expanded sub-nodes are re-queried for their children, unexpanded nodes + * lose their children cache. After refreshing, selection of the currently selected + * elements is restored if possible (in case an absoluteName is available). + * + * In case any of the selected items is a leaf node, the parent of that + * leaf node is refreshed rather than the leaf node itself. In this particular + * case, a multiselect is not considered properly. + * + * The SystemScratchpadView also listens to this event and refreshes those + * elements that are selected in it. + * + * @deprecated obtain the selection yourself and do EVENT_REFRESH or EVENT_REFRESH_REMOTE */ public static final int EVENT_REFRESH_SELECTED = 83; + /** - * The event is a selection-dependent refresh event: refreshes the parent of the current selections + * Refresh the parent of the first item currently selected in the SystemView. + * + * This only refreshes the parent TreeItem of the first item in the selection. + * It does not consider multiselect, multiple occurrences of the Item under multiple + * filters, and does not maintain the current selection. + * + * @deprecated obtain the selection yourself and do EVENT_REFRESH or EVENT_REFRESH_REMOTE */ public static final int EVENT_REFRESH_SELECTED_PARENT = 84; + /** - * The event is a selection-dependent refresh event: from the filter level, all expanded sub-nodes are re-queried for their children, unexpanded nodes lose their children cache. + * Refresh the filter under which the first item currently selected in the + * SystemView is found. + * + * From the filter level, all expanded sub-nodes are re-queried + * for their children, unexpanded nodes lose their children cache. + * After refreshing, selection of the currently selected elements + * is restored if possible. Multiselect is not considered properly. + * + * @deprecated Refreshing a particular context(filter) only can lead + * to inconsistencies, so better obtain the selection yourself + * and do EVENT_REFRESH or EVENT_REFRESH_REMOTE */ public static final int EVENT_REFRESH_SELECTED_FILTER = 135; + /** - * The event is refreshes a remote object (has an ISystemViewRemoteElementAdapter) given either the remote object or a string that will match on getAbsoluteName. - * The tricky part about remote objects is their actual memory object changes on each refresh, so to find one in the tree we must use something + * Refresh a remote object in the SystemView, given either the remote + * object or a string that will match on getAbsoluteName, and optionally + * (re)select a list of objects after refreshing. + * + * An object is considered remote if it has an adapter that implements + * {@link ISystemViewElementAdapter}, so it is possible to get the + * associated subsystem and absolute name. This method refreshes all + * occurrences of the remote object, even under multiple filters. + * The tricky part about remote objects is their actual memory object changes + * on each refresh, so to find one in the tree we must use something * more permanent: hence the use of getAbsoluteName to find it. *

- * You can optionally pass a child remote object, or string, or vector of objects or strings, in the parent parameter, and it/they will be selected after the refresh. - * If it a string then it must be the result of getAbsoluteName on the adapter. + * You can optionally pass a child remote object, or string, or Vector of + * objects or strings, in the "parent" parameter of the event, and it/they + * will be selected after the refresh. When passing a string, it must be + * the result of {@link IRemoteObjectIdentifier#getAbsoluteName(Object)} + * on the adapter. */ public static final int EVENT_REFRESH_REMOTE = 85; @@ -192,8 +243,16 @@ public interface ISystemResourceChangeEvents * The event is simply to force selection of the given object. */ public static final int EVENT_SELECT = 100; + /** - * The event is to select a remote object + * Select one or more remote objects. + * + * The "src" parameter holds a remote object, or string, or Vector of + * objects or strings. When passing a string, it must be the result of + * {@link IRemoteObjectIdentifier#getAbsoluteName(Object)} + * on the adapter of the object. + * The "parent" parameter can optionally hold a model object that is + * the parent of the objects to be refreshed, in order to optimize searches. */ public static final int EVENT_SELECT_REMOTE = 101; diff --git a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemView.java b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemView.java index e00e0e4011c..2d5d6c6b8b8 100644 --- a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemView.java +++ b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemView.java @@ -1609,6 +1609,8 @@ public class SystemView extends SafeTreeViewer public ResourceChangedJob(ISystemResourceChangeEvent event, SystemView originatingViewer) { super("Resource Changed..."); //$NON-NLS-1$ + //FIXME Shouldn't the originatingViewer be taken from the event if possible, if it is instanceof ISystemResourceChangeEventUI? + //See also originatedHere, below _originatingViewer = originatingViewer; _event = event; } @@ -1941,6 +1943,7 @@ public class SystemView extends SafeTreeViewer if ((src == null) || (src == RSECorePlugin.getTheSystemRegistry())) refreshAll(); else { + //FIXME Why do we forceRemote here? EVENT_REFRESH_SELECTED also does not do forceRemote. //smartRefresh(src, false); smartRefresh(src, true); } @@ -1996,6 +1999,7 @@ public class SystemView extends SafeTreeViewer } if (parentElementItem != null) { //refresh(parentElement); + //FIXME IF a multi-select contains elements with a different parent than the one found, they will be ignored. smartRefresh(new TreeItem[] { (TreeItem) parentElementItem }); if (selectedRemoteObjects.size() > 0) { selectRemoteObjects(selectedRemoteObjects, ss, parentElementItem); @@ -2797,18 +2801,27 @@ public class SystemView extends SafeTreeViewer ISystemViewElementAdapter rmtAdapter = null; ISubSystem subsystem = null; String oldElementName = null; + boolean doesDeferredQueries = false; if (!(remoteObject instanceof String)) { rmtAdapter = getViewAdapter(remoteObject); if (rmtAdapter == null) return false; subsystem = rmtAdapter.getSubSystem(remoteObject); oldElementName = rmtAdapter.getAbsoluteName(remoteObject); + doesDeferredQueries = rmtAdapter.supportsDeferredQueries(subsystem); } else oldElementName = (String) remoteObject; Vector matches = new Vector(); // STEP 2: find all references to the object findAllRemoteItemReferences(oldElementName, remoteObject, subsystem, matches); - if (remoteObject instanceof String) remoteObject = getFirstRemoteObject(matches); + if (remoteObject instanceof String) { + remoteObject = getFirstRemoteObject(matches); + rmtAdapter = getViewAdapter(remoteObject); + if (rmtAdapter!=null) { + subsystem = rmtAdapter.getSubSystem(remoteObject); + doesDeferredQueries = rmtAdapter.supportsDeferredQueries(subsystem); + } + } if (remoteObject instanceof ISystemContainer) { ((ISystemContainer) remoteObject).markStale(true); @@ -2842,7 +2855,11 @@ public class SystemView extends SafeTreeViewer // STEP 4: If requested, select the kids in the newly refreshed object. // If the same binary object appears multiple times, select the kids in the first occurrence. // ... what else to do? - if (!(toSelect instanceof SystemViewDummyObject)) selectRemoteObjects(toSelect, null, match); // select the given kids in this parent + if (!doesDeferredQueries && !(toSelect instanceof SystemViewDummyObject)) { + //selecting remote objects makes only sense if not deferred, because + //in the deferred case the objects will be retrieved in a separate job. + selectRemoteObjects(toSelect, null, match); // select the given kids in this parent + } } } } @@ -2894,7 +2911,8 @@ public class SystemView extends SafeTreeViewer if (forceRemote || (isSelectionRemote() && isTreeItemSelectedOrChildSelected(widget))) { if (!isTreeItemSelected(widget)) // it is one of our kids that is selected { - clearSelection(); // there is nothing much else we can do. Calling code will restore it anyway hopefully + //MOB cannot see why the selection is cleared here + //clearSelection(); // there is nothing much else we can do. Calling code will restore it anyway hopefully doOurInternalRefresh(fWidget, fElement, fDoStruct, true); } else // it is us that is selected. This might be a refresh selected operation. TreeItem address won't change { diff --git a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemViewLabelAndContentProvider.java b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemViewLabelAndContentProvider.java index b6d2dc489e9..ef64b06804b 100644 --- a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemViewLabelAndContentProvider.java +++ b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemViewLabelAndContentProvider.java @@ -287,7 +287,7 @@ public class SystemViewLabelAndContentProvider extends LabelProvider // if (ss.isConnected()) { - Object[] children = manager.getChildren(object); + Object[] children = manager.getChildren(object, getViewer()); if (children != null) { // This will be a placeholder to indicate diff --git a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemViewPart.java b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemViewPart.java index cec6ed7b4e4..b3896fdeab3 100644 --- a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemViewPart.java +++ b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/internal/ui/view/SystemViewPart.java @@ -1718,7 +1718,9 @@ public class SystemViewPart list.add(element); //System.out.println("Added to selection list: " + element); } - systemView.setSelection(new StructuredSelection(list)); + if (list.size()>0) { + systemView.setSelection(new StructuredSelection(list)); + } if (remoteElements.size() > 0) { Vector v = new Vector(); diff --git a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/actions/SystemRefreshAction.java b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/actions/SystemRefreshAction.java index e41b0e3162a..237f2cf0c33 100644 --- a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/actions/SystemRefreshAction.java +++ b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/actions/SystemRefreshAction.java @@ -17,7 +17,9 @@ ********************************************************************************/ package org.eclipse.rse.ui.actions; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; import java.util.Vector; import org.eclipse.core.runtime.IAdaptable; @@ -77,40 +79,77 @@ public class SystemRefreshAction extends SystemBaseAction ISystemRegistry sr = RSECorePlugin.getTheSystemRegistry(); if (_selection != null) { - Vector parents = new Vector(); + Set localItems = new HashSet(); + Set remoteItems = new HashSet(); + Vector namesToSelect = new Vector(); Iterator iter = _selection.iterator(); - - while(iter.hasNext()) - { + while(iter.hasNext()) { Object obj = iter.next(); - - if (obj instanceof ISystemContainer) - { + if (obj instanceof ISystemContainer) { ((ISystemContainer)obj).markStale(true); } // get the adapter and find out if it's a leaf node. If so, refresh the parent as well. ISystemViewElementAdapter adapter = getViewAdapter(obj); - if (adapter != null) { - Object parent = adapter.getParent(obj); - boolean hasChildren = adapter.hasChildren((IAdaptable)obj); - - if ((parent != null) && !hasChildren && (!parents.contains(parent))) { - parents.add(parent); - sr.fireEvent(new SystemResourceChangeEvent(parent, ISystemResourceChangeEvents.EVENT_REFRESH, parent)); + // choose the item to refresh -- use parent in case of leaf node + Object itemToRefresh = obj; + if (!adapter.hasChildren((IAdaptable)obj)) { + Object parent = adapter.getParent(obj); + if (parent!=null) { + itemToRefresh = parent; + } + } + + // If we can REFRESH_REMOTE, add the absolute name to reselect + String absoluteName = adapter.getAbsoluteName(obj); + if (absoluteName!=null) { + //Remote items will be refreshed later + remoteItems.add(itemToRefresh); + namesToSelect.add(absoluteName); + } else if (!localItems.contains(obj)) { + localItems.add(obj); + sr.fireEvent(new SystemResourceChangeEvent(obj, ISystemResourceChangeEvents.EVENT_REFRESH, obj)); } - - sr.fireEvent(new SystemResourceChangeEvent(obj, ISystemResourceChangeEvents.EVENT_REFRESH, obj)); } else { sr.fireEvent(new SystemResourceChangeEvent(obj, ISystemResourceChangeEvents.EVENT_REFRESH, obj)); } } + //Free objects + localItems.clear(); + //Deferred refresh of remote items: Try to optimize refresh by reducing the number of parents + boolean itemsChanged = true; + while (remoteItems.size()>1 && itemsChanged) { + itemsChanged = false; + Iterator it = remoteItems.iterator(); + while (it.hasNext()) { + Object obj = it.next(); + ISystemViewElementAdapter adapter = getViewAdapter(obj); + Object parent = adapter.getParent(obj); + if (remoteItems.contains(parent)) { + it.remove(); + itemsChanged = true; + } + } + } + //Fire events + Iterator it = remoteItems.iterator(); + while (it.hasNext()) { + Object obj = it.next(); + String absName = getViewAdapter(obj).getAbsoluteName(obj); + if (absName!=null) { + sr.fireEvent(new SystemResourceChangeEvent(obj, ISystemResourceChangeEvents.EVENT_REFRESH_REMOTE, namesToSelect)); + } else { + sr.fireEvent(new SystemResourceChangeEvent(obj, ISystemResourceChangeEvents.EVENT_REFRESH, obj)); + sr.fireEvent(new SystemResourceChangeEvent(namesToSelect, ISystemResourceChangeEvents.EVENT_SELECT_REMOTE, null)); + } + } } else { + //TODO Check if this is dead code? if ((viewer != null) && (viewer instanceof ISystemResourceChangeListener)) { sr.fireEvent((ISystemResourceChangeListener)viewer, diff --git a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/operations/SystemDeferredTreeContentManager.java b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/operations/SystemDeferredTreeContentManager.java index b50df58b204..9015b543eb5 100644 --- a/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/operations/SystemDeferredTreeContentManager.java +++ b/rse/plugins/org.eclipse.rse.ui/UI/org/eclipse/rse/ui/operations/SystemDeferredTreeContentManager.java @@ -14,11 +14,15 @@ package org.eclipse.rse.ui.operations; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; import org.eclipse.rse.ui.view.IContextObject; import org.eclipse.ui.progress.DeferredTreeContentManager; import org.eclipse.ui.progress.IDeferredWorkbenchAdapter; @@ -54,15 +58,9 @@ public class SystemDeferredTreeContentManager extends return super.getAdapter(element); } - /** - * Returns the child elements of the given element, or in the case of a - * deferred element, returns a placeholder. If a deferred element is used, a - * job is created to fetch the children in the background. - * - * @param parent - * The parent object. - * @return Object[] or null if parent is not an instance of - * IDeferredWorkbenchAdapter. + /* + * (non-Javadoc) + * @see org.eclipse.ui.progress.DeferredTreeContentManager#getChildren(java.lang.Object) */ public Object[] getChildren(final Object parent) { IDeferredWorkbenchAdapter element = getAdapter(parent); @@ -80,6 +78,78 @@ public class SystemDeferredTreeContentManager extends return null; } + /** + * Returns the child elements of the given element, or in the case of a + * deferred element, returns a placeholder. If a deferred element is used, a + * job is created to fetch the children in the background. + * + * When the job for fetching the children is finished and the placeholder + * removed, the original selection is restored. + * + * @param parent + * The parent object. + * @param viewer + * The viewer + * @return Object[] or null if parent is not an instance of + * IDeferredWorkbenchAdapter. + */ + public Object[] getChildren(final Object parent, final Viewer viewer) { + final ISelection selection = viewer.getSelection(); + if (selection.isEmpty()) { + return getChildren(parent); + } + IDeferredWorkbenchAdapter element = getAdapter(parent); + if (element == null) { + return null; + } + PendingUpdateAdapter placeholder = new PendingUpdateAdapter() { + protected void setRemoved(boolean removedValue) { + super.setRemoved(removedValue); + ISelection curSel = viewer.getSelection(); + //Only restore selection if the user has not changed it manually. + //TODO in some cases we might need to send an EVENT_SELECT_REMOTE + //listing the absolute paths of the original selection, in order + //to properly find the items that should be selected. + if (isSelectionContainedIn(curSel, selection)) { + viewer.setSelection(selection); + } + } + }; + + if (!_pendingQueries.contains(parent)) + { + startFetchingDeferredChildren(parent, element, placeholder); + _pendingQueries.add(parent); + return new Object[] { placeholder }; + } + return null; + } + + /** + * Test whether a given selection is a subset of another (parent) selection. + * @param sel Selection to check + * @param parent Parent selection + * @return true if the given selection is a subset. + */ + private boolean isSelectionContainedIn(ISelection sel, ISelection parent) { + if (sel.isEmpty()) + return true; + if (sel.equals(parent)) + return false; + if ((sel instanceof IStructuredSelection) && (parent instanceof IStructuredSelection)) { + IStructuredSelection ssel = (IStructuredSelection)sel; + List spar = ((IStructuredSelection)parent).toList(); + Iterator it = ssel.iterator(); + while (it.hasNext()) { + Object o = it.next(); + if (!spar.contains(o)) + return false; + } + return true; + } + return false; + } + protected void addChildren(final Object parent, final Object[] children, IProgressMonitor monitor) {