From c90aeb7d2c1833e3a2b4748613a22b11b01fc634 Mon Sep 17 00:00:00 2001 From: Greg Watson Date: Sat, 2 Nov 2013 14:17:35 -0400 Subject: [PATCH] [396334] - Fix new folder button so that it actually creates a new folder. Also, move resource browser to a separate widget so that it can be embedded in other controls. Signed-off-by: Greg Watson --- .../remote/internal/ui/DeferredFileStore.java | 143 +++-- .../ui/DeferredFileStoreComparer.java | 55 ++ .../internal/ui/PendingUpdateAdapter.java | 105 ++++ .../internal/ui/RemoteContentProvider.java | 33 +- .../internal/ui/RemoteResourceComparator.java | 22 +- .../internal/ui/RemoteTreeContentManager.java | 384 ++++++++++++ .../remote/internal/ui/RemoteTreeViewer.java | 451 ++++++++++++++ .../remote/internal/ui/messages/Messages.java | 6 + .../internal/ui/messages/messages.properties | 7 +- .../ui/dialogs/RemoteResourceBrowser.java | 350 ++--------- .../remote/ui/widgets/RemoteFileWidget.java | 16 +- .../widgets/RemoteResourceBrowserWidget.java | 577 ++++++++++++++++++ 12 files changed, 1751 insertions(+), 398 deletions(-) create mode 100644 bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/DeferredFileStoreComparer.java create mode 100644 bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/PendingUpdateAdapter.java create mode 100644 bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteTreeContentManager.java create mode 100644 bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteTreeViewer.java create mode 100644 bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/widgets/RemoteResourceBrowserWidget.java diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/DeferredFileStore.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/DeferredFileStore.java index ce129e63e8e..9a42d13c69c 100644 --- a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/DeferredFileStore.java +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/DeferredFileStore.java @@ -31,27 +31,33 @@ import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.ui.progress.IDeferredWorkbenchAdapter; import org.eclipse.ui.progress.IElementCollector; -public class DeferredFileStore implements IDeferredWorkbenchAdapter { +public class DeferredFileStore implements IDeferredWorkbenchAdapter, IAdaptable { private final IFileStore fFileStore; private IFileInfo fFileInfo; private IFileInfo fTargetInfo; private ImageDescriptor fImage; private final boolean fExcludeHidden; + private final DeferredFileStore fParent; - /** - * @since 7.0 - */ public DeferredFileStore(IFileStore store, boolean exclude) { - this(store, null, exclude); + this(store, null, exclude, null); } /** * @since 7.0 */ - public DeferredFileStore(IFileStore store, IFileInfo info, boolean exclude) { + public DeferredFileStore(IFileStore store, boolean exclude, DeferredFileStore parent) { + this(store, null, exclude, parent); + } + + /** + * @since 7.0 + */ + public DeferredFileStore(IFileStore store, IFileInfo info, boolean exclude, DeferredFileStore parent) { fFileStore = store; fFileInfo = info; fExcludeHidden = exclude; + fParent = parent; } /* @@ -60,13 +66,14 @@ public class DeferredFileStore implements IDeferredWorkbenchAdapter { * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#fetchDeferredChildren(java.lang.Object, * org.eclipse.ui.progress.IElementCollector, org.eclipse.core.runtime.IProgressMonitor) */ + @Override public void fetchDeferredChildren(Object object, IElementCollector collector, IProgressMonitor monitor) { ArrayList children = new ArrayList(); try { IFileInfo[] childInfos = fFileStore.childInfos(EFS.NONE, monitor); for (IFileInfo info : childInfos) { if (!(fExcludeHidden && info.getName().startsWith("."))) { //$NON-NLS-1$ - children.add(new DeferredFileStore(fFileStore.getChild(info.getName()), info, fExcludeHidden)); + children.add(new DeferredFileStore(fFileStore.getChild(info.getName()), info, fExcludeHidden, this)); } } } catch (CoreException e) { @@ -75,6 +82,7 @@ public class DeferredFileStore implements IDeferredWorkbenchAdapter { if (children != null) { collector.add(children.toArray(), monitor); } + collector.done(); } /** @@ -99,6 +107,15 @@ public class DeferredFileStore implements IDeferredWorkbenchAdapter { } } + @SuppressWarnings("rawtypes") + @Override + public Object getAdapter(Class adapter) { + if (IWorkbenchAdapter.class.equals(adapter)) { + return this; + } + return null; + } + /** * Return the IWorkbenchAdapter for element or the element if it is * an instance of IWorkbenchAdapter. If it does not exist return @@ -111,58 +128,6 @@ public class DeferredFileStore implements IDeferredWorkbenchAdapter { return (IWorkbenchAdapter) getAdapter(element, IWorkbenchAdapter.class); } - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object) - */ - public Object[] getChildren(Object o) { - try { - IFileStore[] stores = fFileStore.childStores(EFS.NONE, null); - List def = new ArrayList(); - for (int i = 0; i < stores.length; i++) { - if (!(fExcludeHidden && stores[i].getName().startsWith("."))) { //$NON-NLS-1$ - def.add(new DeferredFileStore(stores[i], fExcludeHidden)); - } - } - return def.toArray(); - } catch (CoreException e) { - return new Object[0]; - } - } - - /** - * Get the filestore backing this object - * - * @return - */ - public IFileStore getFileStore() { - return fFileStore; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object) - */ - public ImageDescriptor getImageDescriptor(Object object) { - fetchInfo(); - if (fImage == null) { - boolean isDir = fFileInfo.isDirectory() || (fTargetInfo != null && fTargetInfo.isDirectory()); - FileSystemElement element = new FileSystemElement(fFileStore.getName(), null, isDir); - IWorkbenchAdapter adapter = getAdapter(element); - if (adapter != null) { - ImageDescriptor imageDesc = adapter.getImageDescriptor(object); - if (fTargetInfo != null) { - imageDesc = new OverlayImageDescriptor(imageDesc, RemoteUIImages.DESC_OVR_SYMLINK, - OverlayImageDescriptor.BOTTOM_RIGHT); - } - fImage = imageDesc; - } - } - return fImage; - } - /** * If it is possible to adapt the given object to the given type, this returns the adapter. Performs the following checks: * @@ -212,11 +177,66 @@ public class DeferredFileStore implements IDeferredWorkbenchAdapter { return null; } + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object) + */ + @Override + public Object[] getChildren(Object o) { + try { + IFileStore[] stores = fFileStore.childStores(EFS.NONE, null); + List def = new ArrayList(); + for (int i = 0; i < stores.length; i++) { + if (!(fExcludeHidden && stores[i].getName().startsWith("."))) { //$NON-NLS-1$ + def.add(new DeferredFileStore(stores[i], fExcludeHidden, this)); + } + } + return def.toArray(); + } catch (CoreException e) { + return new Object[0]; + } + } + + /** + * Get the filestore backing this object + * + * @return + */ + public IFileStore getFileStore() { + return fFileStore; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object) + */ + @Override + public ImageDescriptor getImageDescriptor(Object object) { + fetchInfo(); + if (fImage == null) { + boolean isDir = fFileInfo.isDirectory() || (fTargetInfo != null && fTargetInfo.isDirectory()); + FileSystemElement element = new FileSystemElement(fFileStore.getName(), null, isDir); + IWorkbenchAdapter adapter = getAdapter(element); + if (adapter != null) { + ImageDescriptor imageDesc = adapter.getImageDescriptor(object); + if (fTargetInfo != null) { + imageDesc = new OverlayImageDescriptor(imageDesc, RemoteUIImages.DESC_OVR_SYMLINK, + OverlayImageDescriptor.BOTTOM_RIGHT); + } + fImage = imageDesc; + } + } + return fImage; + } + /* * (non-Javadoc) * * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object) */ + @Override public String getLabel(Object o) { return fFileStore.getName(); } @@ -226,8 +246,9 @@ public class DeferredFileStore implements IDeferredWorkbenchAdapter { * * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object) */ + @Override public Object getParent(Object o) { - return fFileStore.getParent(); + return fParent; } /* @@ -235,6 +256,7 @@ public class DeferredFileStore implements IDeferredWorkbenchAdapter { * * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#getRule(java.lang.Object) */ + @Override public ISchedulingRule getRule(Object object) { return null; } @@ -244,6 +266,7 @@ public class DeferredFileStore implements IDeferredWorkbenchAdapter { * * @see org.eclipse.ui.progress.IDeferredWorkbenchAdapter#isContainer() */ + @Override public boolean isContainer() { fetchInfo(); return fFileInfo.isDirectory() || (fTargetInfo != null && fTargetInfo.isDirectory()); diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/DeferredFileStoreComparer.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/DeferredFileStoreComparer.java new file mode 100644 index 00000000000..fb6741b1a7f --- /dev/null +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/DeferredFileStoreComparer.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2013 IBM Corporation 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.remote.internal.ui; + +import org.eclipse.jface.viewers.IElementComparer; + +public class DeferredFileStoreComparer implements IElementComparer { + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IElementComparer#equals(java.lang.Object, java.lang.Object) + */ + @Override + public boolean equals(Object o1, Object o2) { + if (o1 == o2) { + return true; + } + if (o1 == null) { + return false; // o2 != null if we reach this point + } + if (o1.equals(o2)) { + return true; + } + + // Assume they are DeferredFileStore + DeferredFileStore c1 = (o1 instanceof DeferredFileStore) ? (DeferredFileStore) o1 : null; + DeferredFileStore c2 = (o2 instanceof DeferredFileStore) ? (DeferredFileStore) o2 : null; + if (c1 == null || c2 == null) { + return false; + } + return c1.getFileStore().equals(c2.getFileStore()); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IElementComparer#hashCode(java.lang.Object) + */ + @Override + public int hashCode(Object element) { + if (element instanceof DeferredFileStore) { + return ((DeferredFileStore) element).getFileStore().hashCode(); + } + return element.hashCode(); + } +} diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/PendingUpdateAdapter.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/PendingUpdateAdapter.java new file mode 100644 index 00000000000..daa192fb88a --- /dev/null +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/PendingUpdateAdapter.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2003, 2009 IBM Corporation 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.remote.internal.ui; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.remote.internal.ui.messages.Messages; +import org.eclipse.ui.model.IWorkbenchAdapter; + +/** + * The PendingUpdateAdapter is a convenience object that can be used + * by a BaseWorkbenchContentProvider that wants to show a pending update. + */ +public class PendingUpdateAdapter implements IWorkbenchAdapter, IAdaptable { + + boolean removed = false; + + /** + * Return whether or not this has been removed from the tree. + * + * @return boolean + */ + public boolean isRemoved() { + return removed; + } + + /** + * Set whether or not this has been removed from the tree. + * + * @param removedValue + * boolean + */ + public void setRemoved(boolean removedValue) { + this.removed = removedValue; + } + + /** + * Create a new instance of the receiver. + */ + public PendingUpdateAdapter() { + // No initial behavior + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) + */ + @Override + @SuppressWarnings("rawtypes") + public Object getAdapter(Class adapter) { + if (adapter == IWorkbenchAdapter.class) { + return this; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object) + */ + @Override + public Object[] getChildren(Object o) { + return new Object[0]; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object) + */ + @Override + public ImageDescriptor getImageDescriptor(Object object) { + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object) + */ + @Override + public String getLabel(Object o) { + return Messages.PendingUpdateAdapter_Pending; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object) + */ + @Override + public Object getParent(Object o) { + return null; + } +} diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteContentProvider.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteContentProvider.java index a97b8dd5481..44fc5a5934e 100755 --- a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteContentProvider.java +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteContentProvider.java @@ -10,39 +10,44 @@ *******************************************************************************/ package org.eclipse.remote.internal.ui; -import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.ui.IWorkingSet; import org.eclipse.ui.model.WorkbenchContentProvider; -import org.eclipse.ui.progress.DeferredTreeContentManager; /** * Extension to the generic workbench content provider mechanism - * to lazily determine whether an element has children. That is, + * to lazily determine whether an element has children. That is, * children for an element aren't fetched until the user clicks * on the tree expansion box. */ public class RemoteContentProvider extends WorkbenchContentProvider { private IWorkingSet workingSet; - private DeferredTreeContentManager manager; + private RemoteTreeContentManager manager; - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, + * java.lang.Object) */ + @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (newInput != null) { - manager = new DeferredTreeContentManager((AbstractTreeViewer) viewer); + manager = new RemoteTreeContentManager(this, (RemoteTreeViewer) viewer, null); } else { manager = null; } super.inputChanged(viewer, oldInput, newInput); } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.eclipse.ui.model.BaseWorkbenchContentProvider#hasChildren(java.lang.Object) */ + @Override public boolean hasChildren(Object element) { - if (manager != null && manager.isDeferredAdapter(element)) { + if (manager != null /* && manager.isDeferredAdapter(element) */) { return manager.mayHaveChildren(element); } @@ -51,7 +56,9 @@ public class RemoteContentProvider extends WorkbenchContentProvider { /** * Sets the workingSet. - * @param workingSet The workingSet to set + * + * @param workingSet + * The workingSet to set */ public void setWorkingSet(IWorkingSet workingSet) { this.workingSet = workingSet; @@ -59,15 +66,19 @@ public class RemoteContentProvider extends WorkbenchContentProvider { /** * Returns the workingSet. + * * @return IWorkingSet */ public IWorkingSet getWorkingSet() { return workingSet; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.eclipse.ui.model.WorkbenchContentProvider#getChildren(java.lang.Object) */ + @Override public Object[] getChildren(Object element) { if (manager != null) { Object[] children = manager.getChildren(element); diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteResourceComparator.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteResourceComparator.java index 70849fdac45..c29f7d6fb0a 100755 --- a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteResourceComparator.java +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteResourceComparator.java @@ -10,35 +10,39 @@ *******************************************************************************/ package org.eclipse.remote.internal.ui; - import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; public class RemoteResourceComparator extends ViewerComparator { - + private boolean ascending = true; - + public RemoteResourceComparator() { super(); } - + public void setAscending(boolean ascending) { this.ascending = ascending; } - + public boolean isAscending() { return ascending; } + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) + */ + @Override public int compare(Viewer viewer, Object o1, Object o2) { if (o1 instanceof IFileInfo && o2 instanceof IFileInfo) { - int compareResult = ((IFileInfo)o1).getName().compareToIgnoreCase(((IFileInfo)o2).getName());; + int compareResult = ((IFileInfo) o1).getName().compareToIgnoreCase(((IFileInfo) o2).getName()); return ascending ? compareResult : -compareResult; } - + return super.compare(viewer, o1, o2); } - -} +} diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteTreeContentManager.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteTreeContentManager.java new file mode 100644 index 00000000000..e58fcf80eb3 --- /dev/null +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteTreeContentManager.java @@ -0,0 +1,384 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.remote.internal.ui; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.IDeferredWorkbenchAdapter; +import org.eclipse.ui.progress.IElementCollector; +import org.eclipse.ui.progress.IWorkbenchSiteProgressService; +import org.eclipse.ui.progress.WorkbenchJob; + +/** + * A remote content manager that merges content into a tree rather then replacing its children with a + * "pending" node, and then the real children when they are available. This avoids collapsing the viewer when + * a refresh is performed. This implementation is currently tied to the RemoteTreeViewer. + * + * @since 3.1 + */ +public class RemoteTreeContentManager { + private final RemoteTreeViewer fViewer; + private IWorkbenchSiteProgressService progressService; + + /** + * Job to fetch children + */ + private final Job fFetchJob = new FetchJob(); + + /** + * Queue of parents to fetch children for, and associated element collectors and deferred adapters. + */ + private final List fElementQueue = new ArrayList(); + private final List fCollectors = new ArrayList(); + private final List fAdapaters = new ArrayList(); + + /** + * Fetching children is done in a single background job. This makes fetching single threaded/serial per + * view. + */ + private class FetchJob extends Job { + + public FetchJob() { + super("FetchJob"); //$NON-NLS-1$ + setSystem(true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + protected IStatus run(IProgressMonitor monitor) { + while (!fElementQueue.isEmpty() && !monitor.isCanceled()) { + Object element = null; + IElementCollector collector = null; + IDeferredWorkbenchAdapter adapter = null; + synchronized (fElementQueue) { + // could have been cancelled after entering the while loop + if (fElementQueue.isEmpty()) { + return Status.CANCEL_STATUS; + } + element = fElementQueue.remove(0); + collector = fCollectors.remove(0); + adapter = fAdapaters.remove(0); + } + adapter.fetchDeferredChildren(element, collector, monitor); + } + if (monitor.isCanceled()) { + return Status.CANCEL_STATUS; + } + return Status.OK_STATUS; + } + + } + + /** + * Element collector + */ + private class Collector implements IElementCollector { + // number of children added to the tree + int offset = 0; + Object fParent; + + public Collector(Object parent) { + fParent = parent; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.progress.IElementCollector#add(java.lang.Object, + * org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public void add(Object element, IProgressMonitor monitor) { + add(new Object[] { element }, monitor); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.progress.IElementCollector#add(java.lang.Object[], + * org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public void add(Object[] elements, IProgressMonitor monitor) { + Object[] filtered = fViewer.filter(elements); + if (fViewer.getComparator() != null) { + fViewer.getComparator().sort(fViewer, filtered); + } + if (filtered.length > 0) { + replaceChildren(fParent, filtered, offset, monitor); + offset = offset + filtered.length; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.progress.IElementCollector#done() + */ + @Override + public void done() { + prune(fParent, offset); + } + } + + /** + * Contructs a new content manager. + * + * @param provider + * content provider + * @param viewer + * viewer + * @param site + * part site + */ + public RemoteTreeContentManager(ITreeContentProvider provider, RemoteTreeViewer viewer, IWorkbenchPartSite site) { + fViewer = viewer; + if (site != null) { + Object siteService = site.getAdapter(IWorkbenchSiteProgressService.class); + if (siteService != null) { + progressService = (IWorkbenchSiteProgressService) siteService; + } + } + } + + /** + * Create the element collector for the receiver. + * + * @param parent + * The parent object being filled in, + * @param placeholder + * The adapter that will be used to indicate that results are pending, possibly null + * @return IElementCollector + */ + protected IElementCollector createElementCollector(Object parent, PendingUpdateAdapter placeholder) { + return new Collector(parent); + } + + /** + * 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. + */ + public Object[] getChildren(final Object parent) { + IDeferredWorkbenchAdapter element = getAdapter(parent); + if (element == null) { + return null; + } + Object[] currentChildren = fViewer.getCurrentChildren(parent); + PendingUpdateAdapter placeholder = null; + if (currentChildren == null || currentChildren.length == 0) { + placeholder = new PendingUpdateAdapter(); + } + startFetchingDeferredChildren(parent, element, placeholder); + if (placeholder == null) { + return currentChildren; + } + return new Object[] { placeholder }; + } + + /** + * Create a UIJob to replace the children of the parent in the tree viewer. + * + * @param parent + * the parent for which children are to be replaced + * @param children + * the replacement children + * @param offset + * the offset at which to start replacing children + * @param monitor + * progress monitor + */ + protected void replaceChildren(final Object parent, final Object[] children, final int offset, IProgressMonitor monitor) { + if (monitor.isCanceled()) { + return; + } + WorkbenchJob updateJob = new WorkbenchJob("IncrementalDeferredTreeContentManager") { //$NON-NLS-1$ + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public IStatus runInUIThread(IProgressMonitor updateMonitor) { + // Cancel the job if the tree viewer got closed + if (fViewer.getControl().isDisposed()) { + return Status.CANCEL_STATUS; + } + fViewer.replace(parent, children, offset); + return Status.OK_STATUS; + } + }; + updateJob.setSystem(true); + updateJob.setPriority(Job.INTERACTIVE); + updateJob.schedule(); + } + + /** + * Create a UIJob to prune the children of the parent in the tree viewer, starting at the given offset. + * + * @param parent + * the parent for which children should be pruned + * @param offset + * the offset at which children should be pruned. All children at and after this index will be + * removed from the tree. + */ + protected void prune(final Object parent, final int offset) { + WorkbenchJob updateJob = new WorkbenchJob("DeferredTree") { //$NON-NLS-1$ + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public IStatus runInUIThread(IProgressMonitor updateMonitor) { + // Cancel the job if the tree viewer got closed + if (fViewer.getControl().isDisposed()) { + return Status.CANCEL_STATUS; + } + fViewer.prune(parent, offset); + return Status.OK_STATUS; + } + }; + updateJob.setSystem(true); + updateJob.setPriority(Job.INTERACTIVE); + updateJob.schedule(); + } + + /** + * Run a job to clear the placeholder. This is used when the update for the tree is complete so that the + * user is aware that no more updates are pending. + * + * @param placeholder + */ + protected void runClearPlaceholderJob(final PendingUpdateAdapter placeholder) { + if (placeholder == null || placeholder.isRemoved() || !PlatformUI.isWorkbenchRunning()) { + return; + } + // Clear the placeholder if it is still there + WorkbenchJob clearJob = new WorkbenchJob("DeferredTreeContentManager_ClearJob") { //$NON-NLS-1$ + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + if (!placeholder.isRemoved()) { + Control control = fViewer.getControl(); + if (control.isDisposed()) { + return Status.CANCEL_STATUS; + } + fViewer.remove(placeholder); + placeholder.setRemoved(true); + } + return Status.OK_STATUS; + } + }; + clearJob.setSystem(true); + clearJob.schedule(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.progress.DeferredTreeContentManager#getFetchJobName(java.lang.Object, + * org.eclipse.ui.progress.IDeferredWorkbenchAdapter) + */ + protected String getFetchJobName(Object parent, IDeferredWorkbenchAdapter adapter) { + return "RemoteTreeContentManager"; //$NON-NLS-1$ + } + + /** + * Return the IDeferredWorkbenchAdapter for element or the element if it is an instance of + * IDeferredWorkbenchAdapter. If it does not exist return null. + * + * @param element + * @return IDeferredWorkbenchAdapter or null + */ + protected IDeferredWorkbenchAdapter getAdapter(Object element) { + if (element instanceof IDeferredWorkbenchAdapter) { + return (IDeferredWorkbenchAdapter) element; + } + if (!(element instanceof IAdaptable)) { + return null; + } + Object adapter = ((IAdaptable) element).getAdapter(IDeferredWorkbenchAdapter.class); + if (adapter == null) { + return null; + } + return (IDeferredWorkbenchAdapter) adapter; + } + + protected void startFetchingDeferredChildren(final Object parent, final IDeferredWorkbenchAdapter adapter, + PendingUpdateAdapter placeholder) { + final IElementCollector collector = createElementCollector(parent, placeholder); + synchronized (fElementQueue) { + if (!fElementQueue.contains(parent)) { + fElementQueue.add(parent); + fCollectors.add(collector); + fAdapaters.add(adapter); + } + } + if (progressService == null) { + fFetchJob.schedule(); + } else { + progressService.schedule(fFetchJob); + } + } + + /** + * Provides an optimized lookup for determining if an element has children. This is required because + * elements that are populated lazilly can't answer getChildren just to determine the + * potential for children. Throw an AssertionFailedException if element is null. + * + * @param element + * The Object being tested. This should not be null. + * @return boolean true if there are potentially children. + * @throws RuntimeException + * if the element is null. + */ + public boolean mayHaveChildren(Object element) { + // Assert.isNotNull(element, ProgressMessages.DeferredTreeContentManager_NotDeferred); + IDeferredWorkbenchAdapter adapter = getAdapter(element); + return adapter != null && adapter.isContainer(); + } + + /** + * Cancels any content this provider is currently fetching. + */ + public void cancel() { + synchronized (fElementQueue) { + fFetchJob.cancel(); + fElementQueue.clear(); + fAdapaters.clear(); + fCollectors.clear(); + } + } + +} diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteTreeViewer.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteTreeViewer.java new file mode 100644 index 00000000000..59e90cd33a8 --- /dev/null +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/RemoteTreeViewer.java @@ -0,0 +1,451 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.remote.internal.ui; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Item; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.swt.widgets.Widget; +import org.eclipse.ui.model.IWorkbenchAdapter; +import org.eclipse.ui.progress.UIJob; + +public class RemoteTreeViewer extends TreeViewer { + private ExpansionJob fExpansionJob = null; + private SelectionJob fSelectionJob = null; + + private class ExpansionJob extends UIJob { + + private Object element; + private final List parents = new ArrayList(); // top down + + /** + * Constructs a job to expand the given element. + * + */ + public ExpansionJob() { + super("Expansion"); //$NON-NLS-1$ + setPriority(Job.INTERACTIVE); + setSystem(true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + if (getControl().isDisposed() || element == null) { + return Status.OK_STATUS; + } + synchronized (RemoteTreeViewer.this) { + boolean allParentsExpanded = true; + Iterator iterator = parents.iterator(); + while (iterator.hasNext() && !monitor.isCanceled()) { + Object parent = iterator.next(); + Widget item = findItem(parent); + if (item != null) { + expandToLevel(parent, 1); + } else { + allParentsExpanded = false; + break; + } + } + if (allParentsExpanded) { + Widget item = findItem(element); + if (item != null) { + if (isExpandable(element)) { + expandToLevel(element, 1); + } + element = null; + parents.clear(); + return Status.OK_STATUS; + } + } + return Status.OK_STATUS; + } + } + + public void validate(Object object) { + if (element != null) { + if (element.equals(object) || parents.contains(object)) { + cancel(); + element = null; + } + } + } + + public void setDeferredExpansion(Object toExpand) { + element = toExpand; + parents.clear(); + addAllParents(parents, element); + } + + } + + private class SelectionJob extends UIJob { + + private IStructuredSelection selection; + private Object first; + private final List parents = new ArrayList(); // top down + + /** + * Constucts a job to select the given element. + * + */ + public SelectionJob() { + super("Selection"); //$NON-NLS-1$ + setPriority(Job.INTERACTIVE); + setSystem(true); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + if (getControl().isDisposed() || selection == null) { + return Status.OK_STATUS; + } + synchronized (RemoteTreeViewer.this) { + boolean allParentsExpanded = true; + Iterator iterator = parents.iterator(); + while (iterator.hasNext() && !monitor.isCanceled()) { + Object parent = iterator.next(); + Widget item = findItem(parent); + if (item != null) { + expandToLevel(parent, 1); + } else { + allParentsExpanded = false; + break; + } + } + if (allParentsExpanded) { + if (findItem(first) != null) { + setSelection(selection, true); + selection = null; + first = null; + parents.clear(); + return Status.OK_STATUS; + } + } + + return Status.OK_STATUS; + } + } + + public void setDeferredSelection(IStructuredSelection sel) { + selection = sel; + first = selection.getFirstElement(); + parents.clear(); + addAllParents(parents, first); + } + + public void validate(Object object) { + if (first != null) { + if (first.equals(object) || parents.contains(object)) { + cancel(); + selection = null; + } + } + } + } + + /** + * Constructs a remote tree viewer parented by the given composite. + * + * @param parent + * parent composite + */ + public RemoteTreeViewer(Composite parent) { + super(parent); + addDisposeListener(); + fExpansionJob = new ExpansionJob(); + fSelectionJob = new SelectionJob(); + } + + /** + * Constructs a remote tree viewer parented by the given composite + * with the given style. + * + * @param parent + * parent composite + * @param style + * style bits + */ + public RemoteTreeViewer(Composite parent, int style) { + super(parent, style); + addDisposeListener(); + fExpansionJob = new ExpansionJob(); + fSelectionJob = new SelectionJob(); + } + + /** + * Constructs a remote tree viewer with the given tree. + * + * @param tree + * tree widget + */ + public RemoteTreeViewer(Tree tree) { + super(tree); + addDisposeListener(); + fExpansionJob = new ExpansionJob(); + fSelectionJob = new SelectionJob(); + } + + private void addDisposeListener() { + getControl().addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + cancelJobs(); + } + }); + } + + protected void runDeferredUpdates() { + if (fExpansionJob != null) { + fExpansionJob.schedule(); + } + if (fSelectionJob != null) { + fSelectionJob.schedule(); + } + } + + /** + * The given element is being removed from the tree. Cancel + * any deferred updates for the element. + * + * @param element + */ + protected void validateDeferredUpdates(Object element) { + if (element != null) { + if (fExpansionJob != null) { + fExpansionJob.validate(element); + } + if (fSelectionJob != null) { + fSelectionJob.validate(element); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.AbstractTreeViewer#add(java.lang.Object, java.lang.Object) + */ + @Override + public synchronized void add(Object parentElement, Object childElement) { + super.add(parentElement, childElement); + runDeferredUpdates(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.AbstractTreeViewer#add(java.lang.Object, java.lang.Object[]) + */ + @Override + public synchronized void add(Object parentElement, Object[] childElements) { + super.add(parentElement, childElements); + runDeferredUpdates(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.AbstractTreeViewer#remove(java.lang.Object) + */ + @Override + public synchronized void remove(Object element) { + validateDeferredUpdates(element); + super.remove(element); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.AbstractTreeViewer#remove(java.lang.Object[]) + */ + @Override + public synchronized void remove(Object[] elements) { + for (Object element : elements) { + validateDeferredUpdates(element); + } + super.remove(elements); + } + + /** + * Cancels any deferred updates currently scheduled/running. + */ + public void cancelJobs() { + cancel(fSelectionJob); + cancel(fExpansionJob); + } + + public synchronized void deferExpansion(Object element) { + TreeItem treeItem = (TreeItem) findItem(element); + if (treeItem == null) { + fExpansionJob.setDeferredExpansion(element); + fExpansionJob.schedule(); + } else { + if (!getExpanded(treeItem)) { + fExpansionJob.setDeferredExpansion(element); + fExpansionJob.schedule(); + } + } + } + + public synchronized void deferSelection(IStructuredSelection selection) { + if (fSelectionJob == null) { + fSelectionJob = new SelectionJob(); + } + + fSelectionJob.setDeferredSelection(selection); + fSelectionJob.schedule(); + } + + public IStructuredSelection getDeferredSelection() { + if (fSelectionJob != null) { + return fSelectionJob.selection; + } + return null; + } + + private void cancel(Job job) { + if (job != null) { + job.cancel(); + } + } + + private void addAllParents(List list, Object element) { + if (element instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) element; + IWorkbenchAdapter adapter = (IWorkbenchAdapter) adaptable.getAdapter(IWorkbenchAdapter.class); + if (adapter != null) { + Object parent = adapter.getParent(element); + if (parent != null) { + list.add(0, parent); + addAllParents(list, parent); + } + } + } + } + + @Override + public Object[] filter(Object[] elements) { + return super.filter(elements); + } + + public Object[] getCurrentChildren(Object parent) { + Widget widget = findItem(parent); + if (widget != null) { + Item[] items = getChildren(widget); + Object[] children = new Object[items.length]; + for (int i = 0; i < children.length; i++) { + Object data = items[i].getData(); + if (data == null) { + return null; + } + children[i] = data; + } + return children; + } + return null; + } + + public synchronized void prune(final Object parent, final int offset) { + Widget widget = findItem(parent); + if (widget != null) { + final Item[] currentChildren = getChildren(widget); + if (offset < currentChildren.length) { + preservingSelection(new Runnable() { + @Override + public void run() { + for (int i = offset; i < currentChildren.length; i++) { + if (currentChildren[i].getData() != null) { + disassociate(currentChildren[i]); + } + currentChildren[i].dispose(); + } + } + }); + } + } + } + + public synchronized void replace(final Object parent, final Object[] children, final int offset) { + preservingSelection(new Runnable() { + @Override + public void run() { + Widget widget = findItem(parent); + if (widget == null) { + add(parent, children); + } else { + Item[] currentChildren = getChildren(widget); + int pos = offset; + if (pos >= currentChildren.length) { + // append + add(parent, children); + } else { + // replace + for (int i = 0; i < children.length; i++) { + Object child = children[i]; + if (pos < currentChildren.length) { + // replace + Item item = currentChildren[pos]; + Object data = item.getData(); + if (!child.equals(data)) { + // no need to cancel pending updates here, the child may have shifted up/down + internalRefresh(item, child, true, true); + } else { + // If it's the same child, the label/content may still have changed + doUpdateItem(item, child); + updatePlus(item, child); + } + } else { + // add + int numLeft = children.length - i; + if (numLeft > 1) { + Object[] others = new Object[numLeft]; + System.arraycopy(children, i, others, 0, numLeft); + add(parent, others); + } else { + add(parent, child); + } + break; + } + pos++; + } + } + } + runDeferredUpdates(); + } + }); + } + +} diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/messages/Messages.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/messages/Messages.java index 13577fb9588..3fe6a39e43e 100644 --- a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/messages/Messages.java +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/messages/Messages.java @@ -53,6 +53,8 @@ public class Messages extends NLS { public static String LocalUIConnectionManager_2; public static String LocalUIConnectionManager_3; + public static String PendingUpdateAdapter_Pending; + public static String PTPRemoteUIPlugin_3; public static String PTPRemoteUIPlugin_4; @@ -91,6 +93,10 @@ public class Messages extends NLS { public static String RemoteResourceBrowser_NewFolder; public static String RemoteResourceBrowser_Show_hidden_files; public static String RemoteResourceBrowser_UpOneLevel; + + public static String RemoteResourceBrowserWidget_New_Folder; + + public static String RemoteResourceBrowserWidget_Unable_to_create_new_folder; public static String RemoteUIServices_Configuring_remote_services; diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/messages/messages.properties b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/messages/messages.properties index ef10df6b39b..259ba83df25 100644 --- a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/messages/messages.properties +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/internal/ui/messages/messages.properties @@ -25,6 +25,7 @@ LocalUIConnectionManager_0=Connection Error LocalUIConnectionManager_1=Could not open connection LocalUIConnectionManager_2=Can not create local connection LocalUIConnectionManager_3=It is not possible to create a connection for the local connection provider. Select a different remote provider first. +PendingUpdateAdapter_Pending=Pending... PTPRemoteUIPlugin_3=Internal Error PTPRemoteUIPlugin_4=Initializing remote services RemoteConnectionWidget_Connection_Type=Connection Type @@ -48,13 +49,15 @@ RemoteResourceBrowser_resourceTitle=Browse Resource RemoteResourceBrowser_fileTitle=Browse File RemoteResourceBrowser_directoryTitle=Browse Directory RemoteResourceBrowser_resourceLabel=Select resource: -RemoteResourceBrowser_fileLabel=Select file: -RemoteResourceBrowser_directoryLabel=Select directory: +RemoteResourceBrowser_fileLabel=Selected file: +RemoteResourceBrowser_directoryLabel=Selected directory: RemoteResourceBrowser_connectonLabel=Connection: RemoteResourceBrowser_newConnection=New... RemoteResourceBrowser_NewFolder=New folder RemoteResourceBrowser_Show_hidden_files=Show hidden files RemoteResourceBrowser_UpOneLevel=Up one level +RemoteResourceBrowserWidget_New_Folder=New Folder +RemoteResourceBrowserWidget_Unable_to_create_new_folder=Unable to create new folder RemoteUIServices_Configuring_remote_services=Configuring remote services... RemoteUIServicesProxy_1=Missing {0} attribute RemoteUIServicesProxy_2=Failed to instantiate factory: {0} in type: {1} in plugin: {2} diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/dialogs/RemoteResourceBrowser.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/dialogs/RemoteResourceBrowser.java index f65710b7d97..7c97ed7e62f 100644 --- a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/dialogs/RemoteResourceBrowser.java +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/dialogs/RemoteResourceBrowser.java @@ -10,51 +10,23 @@ *******************************************************************************/ package org.eclipse.remote.ui.dialogs; -import java.util.Vector; - -import org.eclipse.core.filesystem.IFileStore; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.Path; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.remote.core.IRemoteConnection; -import org.eclipse.remote.core.IRemoteFileManager; import org.eclipse.remote.core.IRemoteServices; -import org.eclipse.remote.internal.ui.DeferredFileStore; -import org.eclipse.remote.internal.ui.RemoteContentProvider; -import org.eclipse.remote.internal.ui.RemoteResourceComparator; -import org.eclipse.remote.internal.ui.RemoteUIImages; import org.eclipse.remote.internal.ui.messages.Messages; -import org.eclipse.remote.ui.IRemoteUIConnectionManager; -import org.eclipse.remote.ui.RemoteUIServices; -import org.eclipse.remote.ui.widgets.RemoteConnectionWidget; +import org.eclipse.remote.ui.widgets.RemoteResourceBrowserWidget; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.ui.model.WorkbenchLabelProvider; -import org.eclipse.ui.progress.PendingUpdateAdapter; /** * Generic file/directory browser for remote resources. @@ -70,29 +42,16 @@ public class RemoteResourceBrowser extends Dialog { public static final int MULTI = 0x02; private final static int widthHint = 300; - private final static int heightHint = 300; - private Tree tree = null; - private TreeViewer treeViewer; - private Text remotePathText; private Button okButton; - private Button upButton; - private Button newFolderButton; - private RemoteConnectionWidget fRemoteConnectionWidget; + private RemoteResourceBrowserWidget fWidget; private int browserType; private String dialogTitle; - private String dialogLabel; private boolean showConnections = false; - private boolean showHidden = false; - private String remotePath = EMPTY_STRING; - private String remotePaths[]; private String fInitialPath; - private IPath fRootPath; - private IRemoteFileManager fFileMgr; - private IRemoteConnection fConnection; - private final IRemoteUIConnectionManager fUIConnMgr; + private final IRemoteConnection fConnection; private int optionFlags = SINGLE; public RemoteResourceBrowser(IRemoteServices services, IRemoteConnection conn, Shell parent, int flags) { @@ -103,64 +62,8 @@ public class RemoteResourceBrowser extends Dialog { if (conn == null) { showConnections = true; } - fUIConnMgr = RemoteUIServices.getRemoteUIServices(services).getUIConnectionManager(); setTitle(Messages.RemoteResourceBrowser_resourceTitle); - setType(FILE_BROWSER | DIRECTORY_BROWSER); - } - - /** - * Change the viewers input. Called when a new connection is selected. - * - * @param conn - * new connection - * @return true if input successfully changed - */ - private boolean changeInput(final IRemoteConnection conn) { - if (conn == null) { - return false; - } - if (fUIConnMgr != null) { - fUIConnMgr.openConnectionWithProgress(getShell(), null, conn); - } - if (!conn.isOpen()) { - return false; - } - - fFileMgr = conn.getFileManager(); - if (fFileMgr != null) { - /* - * Note: the call to findInitialPath must happen before the - * treeViewer input is set or the treeViewer fails. No idea why this - * is. - */ - String cwd = conn.getWorkingDirectory(); - IPath initial = findInitialPath(cwd, fInitialPath); - - // TODO: not platform independent - needs IRemotePath - setRoot(initial.toString()); - - fConnection = conn; - return true; - } - - return false; - } - - /** - * When a new connection is selected, make sure it is open before using it. - */ - private void connectionSelected() { - /* - * Make sure the connection is open before we try and read from the - * connection. - */ - final IRemoteConnection conn = fRemoteConnectionWidget.getConnection(); - if (!changeInput(conn)) { - /* - * Reset combo back to the previous selection - */ - fRemoteConnectionWidget.setConnection(fConnection); - } + setType(FILE_BROWSER); } /* @@ -190,10 +93,13 @@ public class RemoteResourceBrowser extends Dialog { protected Control createContents(Composite parent) { Control contents = super.createContents(parent); setTitle(dialogTitle); - remotePathText.setText(remotePath); if (!showConnections) { - changeInput(fConnection); + fWidget.setConnection(fConnection); } + if (fInitialPath != null) { + fWidget.setInitialPath(fInitialPath); + } + updateDialog(); return contents; } @@ -207,188 +113,56 @@ public class RemoteResourceBrowser extends Dialog { @Override protected Control createDialogArea(Composite parent) { Composite main = (Composite) super.createDialogArea(parent); + GridData gd = new GridData(SWT.FILL, SWT.TOP, true, true); + gd.widthHint = widthHint; + main.setLayoutData(gd); + main.setLayout(new GridLayout(1, true)); - final Composite dialogComp = new Composite(main, SWT.NONE); - dialogComp.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); - GridLayout layout = new GridLayout(); - layout.numColumns = 4; - dialogComp.setLayout(layout); - + int options = RemoteResourceBrowserWidget.SHOW_HIDDEN_CHECKBOX | RemoteResourceBrowserWidget.SHOW_NEW_FOLDER_BUTTON; + int style = SWT.NONE; if (showConnections) { - fRemoteConnectionWidget = new RemoteConnectionWidget(dialogComp, SWT.NONE, null, - RemoteConnectionWidget.FLAG_NO_LOCAL_SELECTION, null); - fRemoteConnectionWidget.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 4, 1)); - fRemoteConnectionWidget.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent event) { - connectionSelected(); - updateDialog(); - } - }); + options |= RemoteResourceBrowserWidget.SHOW_CONNECTIONS; + } + if (browserType == DIRECTORY_BROWSER) { + options |= RemoteResourceBrowserWidget.DIRECTORY_BROWSER; + } + if ((optionFlags & MULTI) == MULTI) { + style = SWT.MULTI; } - Label label = new Label(dialogComp, SWT.NONE); - label.setText(dialogLabel); - GridData gd = new GridData(GridData.FILL_HORIZONTAL); - gd.horizontalSpan = 1; - label.setLayoutData(gd); - - remotePathText = new Text(dialogComp, SWT.BORDER | SWT.SINGLE); - remotePathText.addModifyListener(new ModifyListener() { + fWidget = new RemoteResourceBrowserWidget(main, style, options); + fWidget.addModifyListener(new ModifyListener() { + @Override public void modifyText(ModifyEvent e) { - remotePath = remotePathText.getText(); updateDialog(); } }); - remotePathText.addFocusListener(new FocusListener() { + fWidget.addFocusListener(new FocusListener() { + @Override public void focusGained(FocusEvent e) { getShell().setDefaultButton(null); // allow text widget to receive SWT.DefaultSelection event } + @Override public void focusLost(FocusEvent e) { getShell().setDefaultButton(okButton); } }); - remotePathText.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetDefaultSelected(SelectionEvent e) { - remotePathText.setSelection(remotePathText.getText().length()); - setRoot(remotePathText.getText()); - } - - }); - gd = new GridData(GridData.FILL_HORIZONTAL); - gd.widthHint = widthHint; - remotePathText.setLayoutData(gd); - - upButton = new Button(dialogComp, SWT.PUSH | SWT.FLAT); - upButton.setImage(RemoteUIImages.get(RemoteUIImages.IMG_ELCL_UP_NAV)); - upButton.setToolTipText(Messages.RemoteResourceBrowser_UpOneLevel); - upButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - if (!fRootPath.isRoot()) { - setRoot(fRootPath.removeLastSegments(1).toOSString()); - } - } - }); - // new folder: See Bug 396334 - newFolderButton = new Button(dialogComp, SWT.PUSH | SWT.FLAT); - newFolderButton.setImage(RemoteUIImages.get(RemoteUIImages.IMG_ELCL_NEW_FOLDER)); - newFolderButton.setToolTipText(Messages.RemoteResourceBrowser_NewFolder); - newFolderButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - String pathText = remotePathText.getText(); - String newname = "/newfolder"; //$NON-NLS-1$ - remotePathText.setText(pathText + newname); - remotePathText.setSelection(pathText.length() + 1, pathText.length() + newname.length()); - remotePathText.setFocus(); - } - }); - - if ((optionFlags & MULTI) == MULTI) { - tree = new Tree(main, SWT.MULTI | SWT.BORDER); - } else { - tree = new Tree(main, SWT.SINGLE | SWT.BORDER); - } - - gd = new GridData(GridData.FILL_BOTH); - gd.horizontalSpan = 4; - // see bug 158380 - gd.heightHint = Math.max(main.getParent().getSize().y, heightHint); - tree.setLayoutData(gd); - - treeViewer = new TreeViewer(tree); - treeViewer.setContentProvider(new RemoteContentProvider()); - treeViewer.setLabelProvider(new WorkbenchLabelProvider()); - treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { - ISelection selection = event.getSelection(); - if (!selection.isEmpty() && selection instanceof IStructuredSelection) { - IStructuredSelection ss = (IStructuredSelection) selection; - Object element = ss.getFirstElement(); - if (element instanceof DeferredFileStore) { - DeferredFileStore dfs = (DeferredFileStore) element; - remotePathText.setText(dfs.getFileStore().toURI().getPath()); - } - Vector selectedPaths = new Vector(ss.size()); - for (Object currentSelection : ss.toArray()) { - if (currentSelection instanceof DeferredFileStore) { - selectedPaths.add(((DeferredFileStore) currentSelection).getFileStore().toURI().getPath()); - } - } - remotePaths = selectedPaths.toArray(new String[0]); - } - } - }); - treeViewer.setComparator(new RemoteResourceComparator()); - treeViewer.addDoubleClickListener(new IDoubleClickListener() { - public void doubleClick(DoubleClickEvent event) { - IStructuredSelection s = (IStructuredSelection) event.getSelection(); - Object o = s.getFirstElement(); - if (treeViewer.isExpandable(o)) { - treeViewer.setExpandedState(o, !treeViewer.getExpandedState(o)); - } - } - - }); - if (browserType == DIRECTORY_BROWSER) { - treeViewer.addFilter(new ViewerFilter() { - @Override - public boolean select(Viewer viewer, Object parentElement, Object element) { - if ((element instanceof DeferredFileStore)) { - return ((DeferredFileStore) element).isContainer(); - } - return element instanceof PendingUpdateAdapter; - } - }); - } - - final Button showHiddenButton = new Button(main, SWT.CHECK); - showHiddenButton.setText(Messages.RemoteResourceBrowser_Show_hidden_files); - showHiddenButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - showHidden = showHiddenButton.getSelection(); - setRoot(fRootPath.toString()); - } - }); - - updateDialog(); + fWidget.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true)); return main; } - /** - * Determine the initial path for the browser. If the initial path is not - * supplied or does not exist on the remote machine, then the initial path - * will be the cwd. - * - * @param cwd - * @param initialPath - * @return initial path - */ - private IPath findInitialPath(String cwd, String initialPath) { - if (initialPath != null) { - IPath path = new Path(initialPath); - if (!path.isAbsolute()) { - path = new Path(cwd).append(path); - } - if (fFileMgr.getResource(path.toString()).fetchInfo().exists()) { - return path; - } - } - return new Path(cwd); - } - /** * Get the connection that was selected * * @return selected connection */ public IRemoteConnection getConnection() { - return fConnection; + if (fWidget != null) { + return fWidget.getConnection(); + } + return null; } /** @@ -397,10 +171,10 @@ public class RemoteResourceBrowser extends Dialog { * @return selected path */ public String getPath() { - if (remotePath.equals("")) { //$NON-NLS-1$ - return null; + if (fWidget != null && fWidget.getPaths().size() > 0) { + return fWidget.getPaths().get(0); } - return remotePath; + return null; } /** @@ -409,7 +183,10 @@ public class RemoteResourceBrowser extends Dialog { * @return selected paths */ public String[] getPaths() { - return remotePaths; + if (fWidget != null) { + return fWidget.getPaths().toArray(new String[0]); + } + return null; } /** @@ -423,23 +200,6 @@ public class RemoteResourceBrowser extends Dialog { fInitialPath = path; } - /** - * Set the root directory for the browser. This will also update the text - * field with the path. - * - * @param path - * path of root directory - */ - private void setRoot(String path) { - if (fFileMgr != null) { - IFileStore root = fFileMgr.getResource(path); - treeViewer.setInput(new DeferredFileStore(root, !showHidden)); - remotePathText.setText(path); - remotePathText.setSelection(remotePathText.getText().length()); - fRootPath = new Path(path); - } - } - /** * Set the dialogTitle of the dialog. * @@ -463,15 +223,10 @@ public class RemoteResourceBrowser extends Dialog { */ public void setType(int type) { browserType = type; - if (type == FILE_BROWSER) { - dialogLabel = Messages.RemoteResourceBrowser_fileLabel; - setTitle(Messages.RemoteResourceBrowser_fileTitle); - } else if (type == DIRECTORY_BROWSER) { - dialogLabel = Messages.RemoteResourceBrowser_directoryLabel; + if (type == DIRECTORY_BROWSER) { setTitle(Messages.RemoteResourceBrowser_directoryTitle); } else { - dialogLabel = Messages.RemoteResourceBrowser_resourceLabel; - setTitle(Messages.RemoteResourceBrowser_resourceTitle); + setTitle(Messages.RemoteResourceBrowser_fileTitle); } } @@ -485,24 +240,9 @@ public class RemoteResourceBrowser extends Dialog { } private void updateDialog() { - if (okButton != null && upButton != null && newFolderButton != null) { - okButton.setEnabled(false); - upButton.setEnabled(false); - newFolderButton.setEnabled(false); - - if (fConnection != null) { - if (remotePathText != null) { - String pathText = remotePathText.getText(); - if (!pathText.equals(EMPTY_STRING)) { - okButton.setEnabled(true); - newFolderButton.setEnabled(true); - IPath path = new Path(pathText); - if (!path.isRoot()) { - upButton.setEnabled(true); - } - } - } - } + if (okButton != null) { + String path = getPath(); + okButton.setEnabled(getConnection() != null && path != null && !path.equals(EMPTY_STRING)); } } } diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/widgets/RemoteFileWidget.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/widgets/RemoteFileWidget.java index c04df8d2807..fa24961937f 100644 --- a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/widgets/RemoteFileWidget.java +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/widgets/RemoteFileWidget.java @@ -37,7 +37,10 @@ import org.eclipse.swt.widgets.Text; * button that uses the currently specified connection and a "Restore Default" * button to revert to the initial setting. * - * If title is supplied then the widget will be placed in a group. + * If GROUP_FLAG is set, then the widget will be placed in a group. + * If RESTORE_BUTTON_FLAG is set, then a "Restore Default" button will be added + * + * If defaultPath is not null, then the initial path will be set to its value. * * The browse message can be modified using {@link #setBrowseMessage(String)} * @@ -76,10 +79,6 @@ public class RemoteFileWidget extends Composite { body = group; } - // Composite textComp = new Composite(body, SWT.NONE); - // textComp.setLayout(new GridLayout(2, false)); - // textComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - fLabel = new Label(body, SWT.NONE); fLabel.setText(Messages.RemoteFileWidget_File); fLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); @@ -87,6 +86,7 @@ public class RemoteFileWidget extends Composite { fText = new Text(body, SWT.BORDER); fText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); fText.addModifyListener(new ModifyListener() { + @Override public void modifyText(ModifyEvent e) { String path = fText.getText(); setSavedPath(path); @@ -94,12 +94,6 @@ public class RemoteFileWidget extends Composite { } }); - // Composite buttonComp = new Composite(body, SWT.NONE); - // buttonComp.setLayout(new GridLayout(2, true)); - // GridData buttonCompData = new GridData(SWT.FILL, SWT.FILL, false, false); - // buttonCompData.horizontalAlignment = SWT.END; - // buttonComp.setLayoutData(buttonCompData); - fBrowseButton = new Button(body, SWT.NONE); fBrowseButton.setText(Messages.RemoteFileWidget_Browse); GridData browseButtonData = new GridData(SWT.LEFT, SWT.CENTER, false, false); diff --git a/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/widgets/RemoteResourceBrowserWidget.java b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/widgets/RemoteResourceBrowserWidget.java new file mode 100644 index 00000000000..6c770c27006 --- /dev/null +++ b/bundles/org.eclipse.remote.ui/src/org/eclipse/remote/ui/widgets/RemoteResourceBrowserWidget.java @@ -0,0 +1,577 @@ +/******************************************************************************* + * Copyright (c) 2008,2013 IBM Corporation 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: + * IBM - Initial API and implementation + *******************************************************************************/ +package org.eclipse.remote.ui.widgets; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.remote.core.IRemoteConnection; +import org.eclipse.remote.core.IRemoteFileManager; +import org.eclipse.remote.internal.ui.DeferredFileStore; +import org.eclipse.remote.internal.ui.DeferredFileStoreComparer; +import org.eclipse.remote.internal.ui.PendingUpdateAdapter; +import org.eclipse.remote.internal.ui.RemoteContentProvider; +import org.eclipse.remote.internal.ui.RemoteResourceComparator; +import org.eclipse.remote.internal.ui.RemoteTreeViewer; +import org.eclipse.remote.internal.ui.RemoteUIImages; +import org.eclipse.remote.internal.ui.messages.Messages; +import org.eclipse.remote.ui.IRemoteUIConnectionManager; +import org.eclipse.remote.ui.RemoteUIServices; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.model.WorkbenchLabelProvider; + +/** + * Generic file/directory browser for remote resources. + * + * @author greg + * + */ +public class RemoteResourceBrowserWidget extends Composite { + /** + * Browse for files + */ + public static final int FILE_BROWSER = 0x01; + /** + * Browse for directories (files are not shown) + */ + public static final int DIRECTORY_BROWSER = 0x02; + /** + * Display checkbox to show/hide hidden files + */ + public static final int SHOW_HIDDEN_CHECKBOX = 0x10; + /** + * Display button to create new folders + */ + public static final int SHOW_NEW_FOLDER_BUTTON = 0x20; + /** + * Display widget to select a connection + */ + public static final int SHOW_CONNECTIONS = 0x40; + + private static final String EMPTY_STRING = ""; //$NON-NLS-1$ + + private static final int widthHint = 300; + private static final int heightHint = 300; + + private RemoteTreeViewer treeViewer; + private Text remotePathText; + private Button upButton; + private Button newFolderButton; + private RemoteConnectionWidget fRemoteConnectionWidget; + + private String dialogTitle; + private String dialogLabel; + + private boolean showHidden; + private final List remotePaths = new ArrayList(); + private String fInitialPath; + private IPath fRootPath; + private IRemoteFileManager fFileMgr; + private IRemoteConnection fConnection; + + private final ListenerList fModifyListeners = new ListenerList(); + + private int optionFlags = FILE_BROWSER | SHOW_HIDDEN_CHECKBOX | SHOW_NEW_FOLDER_BUTTON; + + public RemoteResourceBrowserWidget(Composite parent, int style, int flags) { + super(parent, style); + setTitle(Messages.RemoteResourceBrowser_resourceTitle); + + if (flags != 0) { + optionFlags = flags; + } + + setType(); + + GridLayout layout = new GridLayout(1, false); + layout.marginHeight = 0; + layout.marginWidth = 0; + setLayout(layout); + + final Composite mainComp = new Composite(this, SWT.NONE); + mainComp.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); + layout = new GridLayout(); + layout.numColumns = 4; + mainComp.setLayout(layout); + + if ((optionFlags & SHOW_CONNECTIONS) != 0) { + fRemoteConnectionWidget = new RemoteConnectionWidget(mainComp, SWT.NONE, null, + RemoteConnectionWidget.FLAG_NO_LOCAL_SELECTION, null); + fRemoteConnectionWidget.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 4, 1)); + fRemoteConnectionWidget.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent event) { + connectionSelected(); + updateEnablement(); + } + }); + } + + Label label = new Label(mainComp, SWT.NONE); + label.setText(dialogLabel); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 1; + label.setLayoutData(gd); + + remotePathText = new Text(mainComp, SWT.BORDER | SWT.SINGLE); + remotePathText.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + if (remotePaths.size() == 0) { + remotePaths.add(remotePathText.getText()); + } else { + remotePaths.set(0, remotePathText.getText()); + } + notifyListeners(e); + updateEnablement(); + } + }); + remotePathText.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetDefaultSelected(SelectionEvent e) { + remotePathText.setSelection(remotePathText.getText().length()); + setRoot(remotePathText.getText()); + } + + }); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.widthHint = widthHint; + remotePathText.setLayoutData(gd); + + upButton = new Button(mainComp, SWT.PUSH | SWT.FLAT); + upButton.setImage(RemoteUIImages.get(RemoteUIImages.IMG_ELCL_UP_NAV)); + upButton.setToolTipText(Messages.RemoteResourceBrowser_UpOneLevel); + upButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + if (!fRootPath.isRoot()) { + setRoot(fRootPath.removeLastSegments(1).toOSString()); + } + } + }); + + if ((optionFlags & SHOW_NEW_FOLDER_BUTTON) != 0) { + // new folder: See Bug 396334 + newFolderButton = new Button(mainComp, SWT.PUSH | SWT.FLAT); + newFolderButton.setImage(RemoteUIImages.get(RemoteUIImages.IMG_ELCL_NEW_FOLDER)); + newFolderButton.setToolTipText(Messages.RemoteResourceBrowser_NewFolder); + newFolderButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + ISelection selection = treeViewer.getSelection(); + if (!selection.isEmpty()) { + if (selection instanceof TreeSelection) { + TreePath[] treePaths = ((TreeSelection) selection).getPaths(); + /* + * There should only be one path + */ + if (treePaths.length > 0) { + TreePath treePath = treePaths[0]; + if (treePath.getLastSegment() instanceof DeferredFileStore) { + DeferredFileStore element = ((DeferredFileStore) treePath.getLastSegment()); + String path = element.getFileStore().toURI().getPath(); + String newPath = createNewFolder(path); + if (newPath != null) { + treeViewer.expandToLevel(element, 1); + treeViewer.refresh(element); + Object[] children = element.getChildren(null); + for (Object child : children) { + if (child instanceof DeferredFileStore + && newPath.equals(((DeferredFileStore) child).getFileStore().getName())) { + treeViewer.deferSelection(new StructuredSelection(child)); + } + } + } + } + } + } + } else { + DeferredFileStore root = (DeferredFileStore) treeViewer.getInput(); + String path = root.getFileStore().toURI().getPath(); + String newPath = createNewFolder(path); + if (newPath != null) { + treeViewer.refresh(); + treeViewer.getTree().setFocus(); + Object[] children = root.getChildren(null); + for (Object child : children) { + if (child instanceof DeferredFileStore + && newPath.equals(((DeferredFileStore) child).getFileStore().getName())) { + treeViewer.deferSelection(new StructuredSelection(child)); + } + } + } + } + } + }); + } + + if ((style & SWT.MULTI) == SWT.MULTI) { + treeViewer = new RemoteTreeViewer(mainComp, SWT.MULTI | SWT.BORDER); + } else { + treeViewer = new RemoteTreeViewer(mainComp, SWT.SINGLE | SWT.BORDER); + } + gd = new GridData(GridData.FILL_BOTH); + gd.horizontalSpan = 4; + // see bug 158380 + gd.heightHint = Math.max(parent.getSize().y, heightHint); + treeViewer.getTree().setLayoutData(gd); + // treeViewer.setUseHashlookup(true); + treeViewer.setComparer(new DeferredFileStoreComparer()); + treeViewer.setComparator(new RemoteResourceComparator()); + treeViewer.setContentProvider(new RemoteContentProvider()); + treeViewer.setLabelProvider(new WorkbenchLabelProvider()); + treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + ISelection selection = event.getSelection(); + if (!selection.isEmpty() && selection instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection) selection; + remotePaths.clear(); + for (Object currentSelection : ss.toArray()) { + if (currentSelection instanceof DeferredFileStore) { + String path = ((DeferredFileStore) currentSelection).getFileStore().toURI().getPath(); + remotePaths.add(path); + } + } + if (remotePaths.size() > 0) { + remotePathText.setText(remotePaths.get(0)); + } + updateEnablement(); + } + } + }); + treeViewer.addDoubleClickListener(new IDoubleClickListener() { + @Override + public void doubleClick(DoubleClickEvent event) { + IStructuredSelection s = (IStructuredSelection) event.getSelection(); + Object o = s.getFirstElement(); + if (treeViewer.isExpandable(o)) { + treeViewer.setExpandedState(o, !treeViewer.getExpandedState(o)); + } + } + + }); + if ((optionFlags & DIRECTORY_BROWSER) != 0) { + treeViewer.addFilter(new ViewerFilter() { + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + if ((element instanceof DeferredFileStore)) { + return ((DeferredFileStore) element).isContainer(); + } + return element instanceof PendingUpdateAdapter; + } + }); + } + + if ((optionFlags & SHOW_HIDDEN_CHECKBOX) != 0) { + final Button showHiddenButton = new Button(mainComp, SWT.CHECK); + showHiddenButton.setText(Messages.RemoteResourceBrowser_Show_hidden_files); + showHiddenButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + showHidden = showHiddenButton.getSelection(); + setRoot(fRootPath.toString()); + } + }); + } + + updateEnablement(); + } + + /** + * Add a listener that will be notified when the directory path is modified. + * + * @param listener + * listener to add + */ + public void addModifyListener(ModifyListener listener) { + fModifyListeners.add(listener); + } + + /** + * Change the viewers input. Called when a new connection is selected. + * + * @param conn + * new connection + * @return true if input successfully changed + */ + private boolean changeInput(final IRemoteConnection conn) { + if (conn == null) { + return false; + } + IRemoteUIConnectionManager uiMgr = RemoteUIServices.getRemoteUIServices(conn.getRemoteServices()).getUIConnectionManager(); + if (uiMgr != null) { + uiMgr.openConnectionWithProgress(getShell(), null, conn); + } + if (!conn.isOpen()) { + return false; + } + + fFileMgr = conn.getFileManager(); + if (fFileMgr != null) { + /* + * Note: the call to findInitialPath must happen before the + * treeViewer input is set or the treeViewer fails. No idea why this + * is. + */ + String cwd = conn.getWorkingDirectory(); + IPath initial = findInitialPath(cwd, fInitialPath); + + // TODO: not platform independent - needs IRemotePath + setRoot(initial.toString()); + + fConnection = conn; + return true; + } + + return false; + } + + public void setConnection(IRemoteConnection connection) { + changeInput(connection); + updateEnablement(); + } + + /** + * When a new connection is selected, make sure it is open before using it. + */ + private void connectionSelected() { + /* + * Make sure the connection is open before we try and read from the + * connection. + */ + final IRemoteConnection conn = fRemoteConnectionWidget.getConnection(); + if (!changeInput(conn)) { + /* + * Reset combo back to the previous selection + */ + fRemoteConnectionWidget.setConnection(fConnection); + } + } + + /** + * @return + */ + private String createNewFolder(final String parent) { + final String[] name = new String[1]; + name[0] = null; + try { + PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() { + @Override + public void run(IProgressMonitor monitor) { + SubMonitor progress = SubMonitor.convert(monitor, 10); + String baseName = "newfolder"; //$NON-NLS-1$ + IFileStore path = fConnection.getFileManager().getResource(parent); + IFileStore child = path.getChild(baseName); + int count = 1; + try { + while (!progress.isCanceled() && child.fetchInfo(EFS.NONE, progress.newChild(1)).exists()) { + progress.setWorkRemaining(10); + child = path.getChild(baseName + " (" + count++ + ")"); //$NON-NLS-1$//$NON-NLS-2$ + } + if (!progress.isCanceled()) { + child.mkdir(EFS.SHALLOW, progress.newChild(10)); + name[0] = child.getName(); + } + } catch (CoreException e) { + ErrorDialog.openError(getShell(), Messages.RemoteResourceBrowserWidget_New_Folder, + Messages.RemoteResourceBrowserWidget_Unable_to_create_new_folder, e.getStatus()); + } + } + }); + } catch (InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return name[0]; + } + + /** + * Determine the initial path for the browser. If the initial path is not + * supplied or does not exist on the remote machine, then the initial path + * will be the cwd. + * + * @param cwd + * @param initialPath + * @return initial path + */ + private IPath findInitialPath(String cwd, String initialPath) { + if (initialPath != null) { + IPath path = new Path(initialPath); + if (!path.isAbsolute()) { + path = new Path(cwd).append(path); + } + if (fFileMgr.getResource(path.toString()).fetchInfo().exists()) { + return path; + } + } + return new Path(cwd); + } + + /** + * Get the connection that was selected + * + * @return selected connection + */ + public IRemoteConnection getConnection() { + return fConnection; + } + + /** + * Get the paths that were selected. + * + * @return selected paths + */ + public List getPaths() { + return remotePaths; + } + + private void notifyListeners(ModifyEvent e) { + for (Object listener : fModifyListeners.getListeners()) { + ((ModifyListener) listener).modifyText(e); + } + } + + /** + * Remove a listener that will be notified when the directory path is + * modified. + * + * @param listener + * listener to remove + */ + public void removeModifyListener(ModifyListener listener) { + fModifyListeners.remove(listener); + } + + /** + * Set the initial path to start browsing. This will be set in the browser + * text field, and in a future version should expand the browser to this + * location if it exists. + * + * @param path + */ + public void setInitialPath(String path) { + fInitialPath = path; + updateEnablement(); + } + + /** + * Set the root directory for the browser. This will also update the text + * field with the path. + * + * @param path + * path of root directory + */ + private void setRoot(String path) { + if (fFileMgr != null) { + IFileStore root = fFileMgr.getResource(path); + treeViewer.setInput(new DeferredFileStore(root, !showHidden)); + remotePathText.setText(path); + remotePathText.setSelection(remotePathText.getText().length()); + fRootPath = new Path(path); + } + } + + /** + * Set the dialogTitle of the dialog. + * + * @param title + */ + public void setTitle(String title) { + dialogTitle = title; + if (dialogTitle == null) { + dialogTitle = ""; //$NON-NLS-1$ + } + Shell shell = getShell(); + if ((shell != null) && !shell.isDisposed()) { + shell.setText(dialogTitle); + } + } + + /** + * Set the type of browser. Can be either a file browser (allows selection + * of files) or a directory browser (allows selection of directories), or + * both. + */ + public void setType() { + if ((optionFlags & FILE_BROWSER) == FILE_BROWSER) { + dialogLabel = Messages.RemoteResourceBrowser_fileLabel; + setTitle(Messages.RemoteResourceBrowser_fileTitle); + } else { + dialogLabel = Messages.RemoteResourceBrowser_directoryLabel; + setTitle(Messages.RemoteResourceBrowser_directoryTitle); + } + } + + private void updateEnablement() { + boolean upEnabled = false; + boolean newFolderEnabled = false; + + if (fConnection != null && fConnection.isOpen()) { + if (remotePaths.size() == 1) { + String pathText = remotePaths.get(0); + if (!pathText.equals(EMPTY_STRING)) { + if (fConnection.getFileManager().getResource(pathText).fetchInfo().isDirectory()) { + newFolderEnabled = true; + } + IPath path = new Path(pathText); + if (!path.isRoot()) { + upEnabled = true; + } + } + } + } + + if (upButton != null) { + upButton.setEnabled(upEnabled); + } + if (newFolderButton != null) { + newFolderButton.setEnabled(newFolderEnabled); + } + } +}