diff options
14 files changed, 924 insertions, 806 deletions
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSSynchronizeViewPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSSynchronizeViewPage.java index 5d62ed9dd..4c272d51a 100644 --- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSSynchronizeViewPage.java +++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CVSSynchronizeViewPage.java @@ -88,6 +88,7 @@ public class CVSSynchronizeViewPage extends SubscriberParticipantPage implements super.setActionBars(actionBars); IMenuManager mgr = actionBars.getMenuManager(); mgr.add(new Separator()); + //mgr.add(groupByComment); } /* @@ -132,9 +133,7 @@ public class CVSSynchronizeViewPage extends SubscriberParticipantPage implements private SyncInfoTree getSyncInfoSet() { return getParticipant().getSubscriberSyncInfoCollector().getSyncInfoTree(); } - - - + /* (non-Javadoc) * @see org.eclipse.team.ui.synchronize.SubscriberParticipantPage#createSyncInfoSetCompareConfiguration() */ diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogDiffNode.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogDiffNode.java index 896f1f7fa..90d327647 100644 --- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogDiffNode.java +++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogDiffNode.java @@ -13,20 +13,19 @@ package org.eclipse.team.internal.ccvs.ui.subscriber; import java.text.DateFormat; import org.eclipse.compare.structuremergeviewer.DiffNode; +import org.eclipse.core.resources.IResource; import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.team.core.synchronize.SyncInfo; import org.eclipse.team.internal.ccvs.core.ILogEntry; import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin; import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants; -import org.eclipse.team.ui.synchronize.viewers.SyncInfoModelElement; +import org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement; -public class ChangeLogDiffNode extends SyncInfoModelElement { +public class ChangeLogDiffNode extends SynchronizeModelElement { private ILogEntry logEntry; public ChangeLogDiffNode(DiffNode parent, ILogEntry logEntry) { - //super(parent, new SyncInfoTree(), ResourcesPlugin.getWorkspace().getRoot()); - super(null, null); + super(parent); this.logEntry = logEntry; } @@ -54,10 +53,6 @@ public class ChangeLogDiffNode extends SyncInfoModelElement { String date = DateFormat.getDateTimeInstance().format(logEntry.getDate()); return date + ": " + logEntry.getComment() + " (" + logEntry.getAuthor() +")"; } - - public void add(SyncInfo info) { - //((SubscriberSyncInfoSet)getSyncInfoTree()).add(info); - } /* (non-Javadoc) * @see org.eclipse.team.ui.synchronize.SyncInfoModelElement#toString() @@ -65,4 +60,12 @@ public class ChangeLogDiffNode extends SyncInfoModelElement { public String toString() { return getLabel(null); } + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement#getResource() + */ + public IResource getResource() { + // TODO Auto-generated method stub + return null; + } } diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelProvider.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelProvider.java index 26cea8ed0..ae1924d85 100644 --- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelProvider.java +++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelProvider.java @@ -12,12 +12,12 @@ package org.eclipse.team.internal.ccvs.ui.subscriber; import java.util.*; +import org.eclipse.compare.structuremergeviewer.IDiffContainer; import org.eclipse.compare.structuremergeviewer.IDiffElement; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.*; import org.eclipse.team.core.synchronize.*; import org.eclipse.team.internal.ccvs.core.*; import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot; @@ -25,7 +25,6 @@ import org.eclipse.team.internal.ccvs.core.resources.RemoteFile; import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo; import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin; import org.eclipse.team.ui.synchronize.viewers.*; -import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.ui.progress.UIJob; /** @@ -40,10 +39,9 @@ import org.eclipse.ui.progress.UIJob; * * {date/time, comment, user} -> {*files} */ -public class ChangeLogModelProvider extends HierarchicalModelProvider { +public class ChangeLogModelProvider extends SynchronizeModelProvider { private Map commentRoots = new HashMap(); - private PendingUpdateAdapter pendingItem; private boolean shutdown = false; private FetchLogEntriesJob fetchLogEntriesJob; @@ -85,54 +83,12 @@ public class ChangeLogModelProvider extends HierarchicalModelProvider { } } - /** - * The PendingUpdateAdapter is a convenience object that can be used - * by a BaseWorkbenchContentProvider that wants to show a pending update. - */ - public static class PendingUpdateAdapter implements IWorkbenchAdapter, IAdaptable { - - /** - * Create a new instance of the receiver. - */ - public PendingUpdateAdapter() { - //No initial behavior - } - - /* (non-Javadoc) - * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) - */ - 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) - */ - public Object[] getChildren(Object o) { - return new Object[0]; - } - - /* (non-Javadoc) - * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object) - */ - public ImageDescriptor getImageDescriptor(Object object) { - return null; - } - - /* (non-Javadoc) - * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object) - */ - public String getLabel(Object o) { - return "Fetching logs from server. Please wait..."; + public static class FullPathSyncInfoElement extends SyncInfoModelElement { + public FullPathSyncInfoElement(IDiffContainer parent, SyncInfo info) { + super(parent, info); } - - /* (non-Javadoc) - * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object) - */ - public Object getParent(Object o) { - return null; + public String getName() { + return getResource().getFullPath().toString(); } } @@ -146,17 +102,11 @@ public class ChangeLogModelProvider extends HierarchicalModelProvider { } public IStatus run(IProgressMonitor monitor) { if (set != null && !shutdown) { - final SyncInfoModelElement[] nodes = calculateRoots(getSyncInfoTree(), monitor); + final SynchronizeModelElement[] nodes = calculateRoots(getSyncInfoSet(), monitor); UIJob updateUI = new UIJob("updating change log viewers") { public IStatus runInUIThread(IProgressMonitor monitor) { - AbstractTreeViewer tree = getTreeViewer(); - if(pendingItem != null && tree != null && !tree.getControl().isDisposed()) { - tree.remove(pendingItem); - } - for (int i = 0; i < nodes.length; i++) { - addToViewer(nodes[i]); - buildModelObjects(nodes[i]); - } + StructuredViewer tree = getViewer(); + tree.refresh(); return Status.OK_STATUS; } }; @@ -167,7 +117,7 @@ public class ChangeLogModelProvider extends HierarchicalModelProvider { } }; - public ChangeLogModelProvider(SyncInfoTree set) { + public ChangeLogModelProvider(SyncInfoSet set) { super(set); } @@ -176,25 +126,7 @@ public class ChangeLogModelProvider extends HierarchicalModelProvider { * @see org.eclipse.team.ui.synchronize.viewers.HierarchicalModelProvider#buildModelObjects(org.eclipse.compare.structuremergeviewer.DiffNode) */ protected IDiffElement[] buildModelObjects(SynchronizeModelElement node) { - /*if(node == this) { - UIJob job = new UIJob("") { - public IStatus runInUIThread(IProgressMonitor monitor) { - AbstractTreeViewer tree = getTreeViewer(); - if (tree != null && !tree.getControl().isDisposed()) { - if(pendingItem == null) { - pendingItem = new PendingUpdateAdapter(); - } - IDiffElement[] elements = getChildren(); - for (int i = 0; i < elements.length; i++) { - tree.remove(elements[i]); - } - tree.add(ChangeLogViewerInput.this, pendingItem); - } - return Status.OK_STATUS; - } - }; - job.schedule(); - + if(node == getModelRoot()) { if(fetchLogEntriesJob == null) { fetchLogEntriesJob = new FetchLogEntriesJob(); } @@ -205,17 +137,15 @@ public class ChangeLogModelProvider extends HierarchicalModelProvider { } catch (InterruptedException e) { } } - fetchLogEntriesJob.setSyncInfoSet(getSyncInfoTree()); + fetchLogEntriesJob.setSyncInfoSet(getSyncInfoSet()); fetchLogEntriesJob.schedule(); - } else { - return super.buildModelObjects(node); - }*/ + } return new IDiffElement[0]; } - private SyncInfoModelElement[] calculateRoots(SyncInfoSet set, IProgressMonitor monitor) { + private SynchronizeModelElement[] calculateRoots(SyncInfoSet set, IProgressMonitor monitor) { commentRoots.clear(); - /*SyncInfo[] infos = set.getSyncInfos(); + SyncInfo[] infos = set.getSyncInfos(); monitor.beginTask("fetching from server", set.size() * 100); for (int i = 0; i < infos.length; i++) { if(monitor.isCanceled()) { @@ -226,13 +156,15 @@ public class ChangeLogModelProvider extends HierarchicalModelProvider { DateComment dateComment = new DateComment(logEntry.getDate(), logEntry.getComment(), logEntry.getAuthor()); ChangeLogDiffNode changeRoot = (ChangeLogDiffNode) commentRoots.get(dateComment); if (changeRoot == null) { - changeRoot = new ChangeLogDiffNode(this, logEntry); + changeRoot = new ChangeLogDiffNode(getModelRoot(), logEntry); commentRoots.put(dateComment, changeRoot); } - changeRoot.add(infos[i]); + SynchronizeModelElement element = new FullPathSyncInfoElement(changeRoot, infos[i]); + associateDiffNode(element); + changeRoot.add(element); } monitor.worked(100); - }*/ + } return (ChangeLogDiffNode[]) commentRoots.values().toArray(new ChangeLogDiffNode[commentRoots.size()]); } @@ -253,6 +185,7 @@ public class ChangeLogModelProvider extends HierarchicalModelProvider { String baseRevision = getRevisionString(base); String remoteRevision = getRevisionString(remote); String localRevision = getRevisionString(local); + // TODO: handle new files where there is no local or remote boolean useRemote = true; if(local != null && remote != null) { @@ -280,13 +213,6 @@ public class ChangeLogModelProvider extends HierarchicalModelProvider { } /* (non-Javadoc) - * @see org.eclipse.team.ui.synchronize.views.HierarchicalModelProvider#syncSetChanged(org.eclipse.team.core.subscribers.ISyncInfoSetChangeEvent) - */ - protected void syncSetChanged(ISyncInfoSetChangeEvent event) { - reset(); - } - - /* (non-Javadoc) * @see org.eclipse.team.ui.synchronize.views.HierarchicalModelProvider#dispose() */ public void dispose() { @@ -296,4 +222,48 @@ public class ChangeLogModelProvider extends HierarchicalModelProvider { } super.dispose(); } + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#getViewerSorter() + */ + public ViewerSorter getViewerSorter() { + return new SynchronizeModelElementSorter(); + } + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#doAdd(org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement, org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement) + */ + protected void doAdd(SynchronizeModelElement parent, SynchronizeModelElement element) { + AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer(); + viewer.add(parent, element); + } + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#doRemove(org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement) + */ + protected void doRemove(SynchronizeModelElement element) { + AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer(); + viewer.remove(element); + } + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#handleResourceAdditions(org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent) + */ + protected void handleResourceAdditions(ISyncInfoTreeChangeEvent event) { + reset(); + } + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#handleResourceChanges(org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent) + */ + protected void handleResourceChanges(ISyncInfoTreeChangeEvent event) { + reset(); + } + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#handleResourceRemovals(org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent) + */ + protected void handleResourceRemovals(ISyncInfoTreeChangeEvent event) { + reset(); + } } diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CompareParticipantPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CompareParticipantPage.java index 30ef265d1..8c1f9e5d8 100644 --- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CompareParticipantPage.java +++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CompareParticipantPage.java @@ -13,19 +13,44 @@ package org.eclipse.team.internal.ccvs.ui.subscriber; import org.eclipse.jface.action.*; import org.eclipse.team.internal.ui.synchronize.actions.RemoveSynchronizeParticipantAction; import org.eclipse.team.ui.synchronize.ISynchronizeView; -import org.eclipse.team.ui.synchronize.subscriber.DirectionFilterActionGroup; import org.eclipse.team.ui.synchronize.subscriber.SubscriberParticipant; +import org.eclipse.team.ui.synchronize.subscriber.SynchronizeViewerAdvisor; +import org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider; import org.eclipse.ui.IActionBars; public class CompareParticipantPage extends CVSSynchronizeViewPage { private RemoveSynchronizeParticipantAction removeAction; - private DirectionFilterActionGroup modes; - private Action updateAdapter; + private Action groupByCommentAction; + private boolean groupByComment = true; + private class CompareAdvisor extends CVSSynchronizeViewerAdvisor { + public CompareAdvisor(ISynchronizeView view, SubscriberParticipant participant) { + super(view, participant); + } + + protected SynchronizeModelProvider getModelProvider() { + if (groupByComment) { + return new ChangeLogModelProvider(getSyncInfoSet()); + } + return super.getModelProvider(); + } + + public void refreshModel() { + setInput(getViewer()); + } + } + public CompareParticipantPage(SubscriberParticipant participant, ISynchronizeView view) { super(participant, view); removeAction = new RemoveSynchronizeParticipantAction(getParticipant()); + groupByCommentAction = new Action("Show as Change Log", Action.AS_CHECK_BOX) { //$NON-NLS-1$ + public void run() { + groupByComment = ! groupByComment; + setChecked(groupByComment); + ((CompareAdvisor)CompareParticipantPage.this.getViewerConfiguration()).refreshModel(); + } + }; } /* (non-Javadoc) @@ -37,6 +62,16 @@ public class CompareParticipantPage extends CVSSynchronizeViewPage { IToolBarManager toolbar = actionBars.getToolBarManager(); toolbar.add(new Separator()); toolbar.add(removeAction); + IMenuManager mgr = actionBars.getMenuManager(); + mgr.add(new Separator()); + mgr.add(groupByCommentAction); } } + + /* (non-Javadoc) + * @see org.eclipse.team.internal.ccvs.ui.subscriber.CVSSynchronizeViewPage#createSynchronizeViewerAdvisor() + */ + protected SynchronizeViewerAdvisor createSynchronizeViewerAdvisor() { + return new CompareAdvisor(getSynchronizeView(), getParticipant()); + } } diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/ISynchronizeView.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/ISynchronizeView.java index 0fdf85d87..387158033 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/ISynchronizeView.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/ISynchronizeView.java @@ -21,7 +21,7 @@ import org.eclipse.ui.IViewPart; * <p> * Clients should not add viewActions to this view because they will be global * to all participants. Instead, add participant specific actions as described - * in {@link TreeViewerAdvisor}. + * in {@link StructuredViewerAdvisor}. * </p> * <p> * Clients are not intended to implement this interface. diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/subscriber/SynchronizeViewerAdvisor.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/subscriber/SynchronizeViewerAdvisor.java index 364a98827..31a3d86ba 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/subscriber/SynchronizeViewerAdvisor.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/subscriber/SynchronizeViewerAdvisor.java @@ -80,13 +80,13 @@ public class SynchronizeViewerAdvisor extends TreeViewerAdvisor { protected void initializeListeners(StructuredViewer viewer) { viewer.addSelectionChangedListener(new ISelectionChangedListener() { - public void selectionChanged(SelectionChangedEvent event) { + public void selectionChanged(SelectionChangedEvent event) { updateStatusLine((IStructuredSelection) event.getSelection()); } }); viewer.addOpenListener(new IOpenListener() { - public void open(OpenEvent event) { + public void open(OpenEvent event) { handleOpen(); } }); diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/CompressedFoldersModelProvider.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/CompressedFoldersModelProvider.java index 2480c5f61..0a02d8a62 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/CompressedFoldersModelProvider.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/CompressedFoldersModelProvider.java @@ -92,7 +92,7 @@ public class CompressedFoldersModelProvider extends HierarchicalModelProvider { */ protected IDiffElement[] createModelObjects(SynchronizeModelElement container) { IResource resource = null; - if (container == getRoot()) { + if (container == getModelRoot()) { resource = ResourcesPlugin.getWorkspace().getRoot(); } else { resource = container.getResource(); @@ -193,7 +193,7 @@ public class CompressedFoldersModelProvider extends HierarchicalModelProvider { if (parentNode == null) { SynchronizeModelElement projectNode = getModelObject(local.getProject()); if (projectNode == null) { - projectNode = createModelObject(getRoot(), local.getProject()); + projectNode = createModelObject(getModelRoot(), local.getProject()); } if (local.getParent().getType() == IResource.PROJECT) { parentNode = projectNode; @@ -205,7 +205,7 @@ public class CompressedFoldersModelProvider extends HierarchicalModelProvider { } else { SynchronizeModelElement projectNode = getModelObject(local.getProject()); if (projectNode == null) { - projectNode = createModelObject(getRoot(), local.getProject()); + projectNode = createModelObject(getModelRoot(), local.getProject()); } createModelObject(projectNode, local); } diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/HierarchicalModelProvider.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/HierarchicalModelProvider.java index 873abbbae..a537e8da4 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/HierarchicalModelProvider.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/HierarchicalModelProvider.java @@ -10,26 +10,12 @@ *******************************************************************************/ package org.eclipse.team.ui.synchronize.viewers; -import java.util.*; - -import org.eclipse.compare.structuremergeviewer.IDiffContainer; import org.eclipse.compare.structuremergeviewer.IDiffElement; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.*; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.jface.util.IPropertyChangeListener; -import org.eclipse.jface.util.PropertyChangeEvent; -import org.eclipse.jface.viewers.*; -import org.eclipse.swt.custom.BusyIndicator; -import org.eclipse.swt.widgets.Control; -import org.eclipse.team.core.ITeamStatus; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.team.core.synchronize.*; -import org.eclipse.team.internal.core.Assert; -import org.eclipse.team.internal.ui.TeamUIPlugin; -import org.eclipse.ui.progress.UIJob; -import org.eclipse.team.internal.ui.Policy; /** * An input that can be used with both {@link } and @@ -47,72 +33,8 @@ import org.eclipse.team.internal.ui.Policy; * NOT ON DEMAND - model is created then maintained! * @since 3.0 */ -public class HierarchicalModelProvider extends SynchronizeModelProvider implements ISyncInfoSetChangeListener { - - // Map from resources to model objects. This allows effecient lookup - // of model objects based on changes occuring to resources. - private Map resourceMap = Collections.synchronizedMap(new HashMap()); - - // The viewer this input is being displayed in - private AbstractTreeViewer viewer; - - // Flasg to indicate if tree control should be updated while - // building the model. - private boolean refreshViewer; - - private RootDiffNode root; - - private SyncInfoTree set; - - private Set pendingLabelUpdates = new HashSet(); - - private LabelUpdateJob labelUpdater = new LabelUpdateJob(); - - class LabelUpdateJob extends UIJob { - public static final int BATCH_WAIT_INCREMENT = 100; - Set nodes = new HashSet(); - public LabelUpdateJob() { - super(Policy.bind("HierarchicalModelProvider.0")); //$NON-NLS-1$ - setSystem(true); - } - public IStatus runInUIThread(IProgressMonitor monitor) { - Object[] updates; - synchronized(nodes) { - updates = nodes.toArray(new Object[nodes.size()]); - nodes.clear(); - } - if (canUpdateViewer()) { - AbstractTreeViewer tree = getTreeViewer(); - tree.update(updates, null); - } - schedule(BATCH_WAIT_INCREMENT); - return Status.OK_STATUS; - } - public void add(Object node, boolean isBusy) { - synchronized(nodes) { - nodes.add(node); - } - if (isBusy) { - schedule(BATCH_WAIT_INCREMENT); - } else { - // Wait when unbusying to give the events a chance to propogate through - // the collector - schedule(BATCH_WAIT_INCREMENT * 10); - } - } - public boolean shouldRun() { - return !nodes.isEmpty(); - } - } - - private IPropertyChangeListener listener = new IPropertyChangeListener() { - public void propertyChange(final PropertyChangeEvent event) { - if (event.getProperty() == SynchronizeModelElement.BUSY_PROPERTY) { - labelUpdater.add(event.getSource(), ((Boolean)event.getNewValue()).booleanValue()); - } - } - }; - +public class HierarchicalModelProvider extends SynchronizeModelProvider { + /** * Create an input based on the provide sync set. The input is not initialized * until <code>prepareInput</code> is called. @@ -120,98 +42,15 @@ public class HierarchicalModelProvider extends SynchronizeModelProvider implemen * @param set the sync set used as the basis for the model created by this input. */ public HierarchicalModelProvider(SyncInfoTree set) { - Assert.isNotNull(set); - this.root = new RootDiffNode(); - this.set = set; - } - - /** - * Return the model object (i.e. an instance of <code>SyncInfoModelElement</code> - * or one of its subclasses) for the given IResource. - * @param resource - * the resource - * @return the <code>SyncInfoModelElement</code> for the given resource - */ - protected SynchronizeModelElement getModelObject(IResource resource) { - return (SynchronizeModelElement) resourceMap.get(resource); - } - - /** - * Return the <code>AbstractTreeViewer</code> asociated with this content - * provider or <code>null</code> if the viewer is not of the proper type. - * @return - */ - public AbstractTreeViewer getTreeViewer() { - return viewer; - } - - public void setViewer(StructuredViewer viewer) { - Assert.isTrue(viewer instanceof AbstractTreeViewer); - this.viewer = (AbstractTreeViewer)viewer; + super(set); } public ViewerSorter getViewerSorter() { return new SynchronizeModelElementSorter(); } - /** - * Builds the viewer model based on the contents of the sync set. - */ - public SynchronizeModelElement prepareInput(IProgressMonitor monitor) { - // Connect to the sync set which will register us as a listener and give us a reset event - // in a background thread - getSyncInfoTree().connect(this, monitor); - return getRoot(); - } - - /** - * Dispose of the builder - */ - public void dispose() { - resourceMap.clear(); - getSyncInfoTree().removeSyncSetChangedListener(this); - } - - /* - * (non-Javadoc) - * @see org.eclipse.team.ccvs.syncviews.views.ISyncSetChangedListener#syncSetChanged() - */ - public void syncInfoChanged(final ISyncInfoSetChangeEvent event, IProgressMonitor monitor) { - if (! (event instanceof ISyncInfoTreeChangeEvent)) { - reset(); - } else { - final Control ctrl = viewer.getControl(); - if (ctrl != null && !ctrl.isDisposed()) { - ctrl.getDisplay().syncExec(new Runnable() { - public void run() { - if (!ctrl.isDisposed()) { - BusyIndicator.showWhile(ctrl.getDisplay(), new Runnable() { - public void run() { - handleChanges((ISyncInfoTreeChangeEvent)event); - getRoot().fireChanges(); - } - }); - } - } - }); - } - } - } - - /** - * For each node create children based on the contents of - * @param node - * @return - */ - protected IDiffElement[] buildModelObjects(SynchronizeModelElement node) { - IDiffElement[] children = createModelObjects(node); - for (int i = 0; i < children.length; i++) { - IDiffElement element = children[i]; - if (element instanceof SynchronizeModelElement) { - buildModelObjects((SynchronizeModelElement) element); - } - } - return children; + protected SyncInfoTree getSyncInfoTree() { + return (SyncInfoTree)getSyncInfoSet(); } /** @@ -223,7 +62,7 @@ public class HierarchicalModelProvider extends SynchronizeModelProvider implemen */ protected IDiffElement[] createModelObjects(SynchronizeModelElement container) { IResource resource = null; - if (container == getRoot()) { + if (container == getModelRoot()) { resource = ResourcesPlugin.getWorkspace().getRoot(); } else { resource = container.getResource(); @@ -253,32 +92,6 @@ public class HierarchicalModelProvider extends SynchronizeModelProvider implemen } /** - * Clear the model objects from the diff tree, cleaning up any cached state - * (such as resource to model object map). This method recurses deeply on - * the tree to allow the cleanup of any cached state for the children as - * well. - * @param node - * the root node - */ - protected void clearModelObjects(SynchronizeModelElement node) { - IDiffElement[] children = node.getChildren(); - for (int i = 0; i < children.length; i++) { - IDiffElement element = children[i]; - if (element instanceof SynchronizeModelElement) { - clearModelObjects((SynchronizeModelElement) element); - } - } - IResource resource = node.getResource(); - if (resource != null) { - unassociateDiffNode(resource); - } - IDiffContainer parent = node.getParent(); - if (parent != null) { - parent.removeToRoot(node); - } - } - - /** * Invokes <code>getModelObject(Object)</code> on an array of resources. * @param resources * the resources @@ -292,70 +105,6 @@ public class HierarchicalModelProvider extends SynchronizeModelProvider implemen return result; } - protected void associateDiffNode(SynchronizeModelElement node) { - IResource resource = node.getResource(); - if(resource != null) { - resourceMap.put(resource, node); - } - } - - protected void unassociateDiffNode(IResource resource) { - resourceMap.remove(resource); - } - - /** - * Handle the changes made to the viewer's <code>SyncInfoSet</code>. - * This method delegates the changes to the three methods <code>handleResourceChanges(ISyncInfoSetChangeEvent)</code>, - * <code>handleResourceRemovals(ISyncInfoSetChangeEvent)</code> and - * <code>handleResourceAdditions(ISyncInfoSetChangeEvent)</code>. - * @param event - * the event containing the changed resourcses. - */ - protected void handleChanges(ISyncInfoTreeChangeEvent event) { - try { - viewer.getControl().setRedraw(false); - handleResourceChanges(event); - handleResourceRemovals(event); - handleResourceAdditions(event); - firePendingLabelUpdates(); - } finally { - viewer.getControl().setRedraw(true); - } - } - - /** - * Update the viewer for the sync set additions in the provided event. This - * method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>. - * Subclasses may override. - * @param event - */ - protected void handleResourceAdditions(ISyncInfoTreeChangeEvent event) { - IResource[] added = event.getAddedSubtreeRoots(); - addResources(added); - } - - /** - * Update the viewer for the sync set changes in the provided event. This - * method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>. - * Subclasses may override. - * @param event - */ - protected void handleResourceChanges(ISyncInfoTreeChangeEvent event) { - // Refresh the viewer for each changed resource - SyncInfo[] infos = event.getChangedResources(); - for (int i = 0; i < infos.length; i++) { - SyncInfo info = infos[i]; - IResource local = info.getLocal(); - SynchronizeModelElement diffNode = getModelObject(local); - // If a sync info diff node already exists then just update - // it, otherwise remove the old diff node and create a new - // sub-tree. - if (diffNode != null) { - handleChange(diffNode, info); - } - } - } - /** * Handle the change for the existing diff node. The diff node * should be changed to have the given sync info @@ -383,107 +132,6 @@ public class HierarchicalModelProvider extends SynchronizeModelProvider implemen // TODO: set any additional sync info bits } - protected boolean isConflicting(SynchronizeModelElement diffNode) { - return (diffNode.getKind() & SyncInfo.DIRECTION_MASK) == SyncInfo.CONFLICTING; - } - - /** - * Update the viewer for the sync set removals in the provided event. This - * method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>. - * Subclasses may override. - * @param event - */ - protected void handleResourceRemovals(ISyncInfoTreeChangeEvent event) { - // Remove the removed subtrees - IResource[] removedRoots = event.getRemovedSubtreeRoots(); - for (int i = 0; i < removedRoots.length; i++) { - removeFromViewer(removedRoots[i]); - } - // We have to look for folders that may no longer be in the set - // (i.e. are in-sync) but still have descendants in the set - IResource[] removedResources = event.getRemovedResources(); - for (int i = 0; i < removedResources.length; i++) { - IResource resource = removedResources[i]; - if (resource.getType() != IResource.FILE) { - SynchronizeModelElement node = getModelObject(resource); - if (node != null) { - removeFromViewer(resource); - addResources(new IResource[] {resource}); - } - } - } - } - - protected void reset() { - try { - refreshViewer = false; - - // Clear existing model, but keep the root node - resourceMap.clear(); - clearModelObjects(getRoot()); - // remove all from tree viewer - IDiffElement[] elements = getRoot().getChildren(); - for (int i = 0; i < elements.length; i++) { - viewer.remove(elements[i]); - } - - // Rebuild the model - associateDiffNode(getRoot()); - buildModelObjects(getRoot()); - - // Notify listeners that model has changed - getRoot().fireChanges(); - } finally { - refreshViewer = true; - } - TeamUIPlugin.getStandardDisplay().asyncExec(new Runnable() { - public void run() { - if (viewer != null && !viewer.getControl().isDisposed()) { - viewer.refresh(); - } - } - }); - } - - protected RootDiffNode getRoot() { - return root; - } - - protected SyncInfoTree getSyncInfoTree() { - return set; - } - - /** - * Remove any traces of the resource and any of it's descendants in the - * hiearchy defined by the content provider from the content provider and - * the viewer it is associated with. - * @param resource - */ - protected void removeFromViewer(IResource resource) { - SynchronizeModelElement node = getModelObject(resource); - if (node == null) return; - if (isConflicting(node)) { - setParentConflict(node, false); - } - clearModelObjects(node); - if (canUpdateViewer()) { - AbstractTreeViewer tree = getTreeViewer(); - tree.remove(node); - } - } - - protected void addToViewer(SynchronizeModelElement node) { - associateDiffNode(node); - node.addPropertyChangeListener(listener); - if (isConflicting(node)) { - setParentConflict(node, true); - } - if (canUpdateViewer()) { - AbstractTreeViewer tree = getTreeViewer(); - tree.add(node.getParent(), node); - } - } - protected void addResources(IResource[] added) { for (int i = 0; i < added.length; i++) { IResource resource = added[i]; @@ -501,70 +149,82 @@ public class HierarchicalModelProvider extends SynchronizeModelProvider implemen } } } - - /** - * @param tree - * @return - */ - private boolean canUpdateViewer() { - return refreshViewer && getTreeViewer() != null; - } /* (non-Javadoc) - * @see org.eclipse.team.core.subscribers.ISyncInfoSetChangeListener#syncInfoSetReset(org.eclipse.team.core.subscribers.SyncInfoSet, org.eclipse.core.runtime.IProgressMonitor) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#buildModelObjects(org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement) */ - public void syncInfoSetReset(SyncInfoSet set, IProgressMonitor monitor) { - reset(); + protected IDiffElement[] buildModelObjects(SynchronizeModelElement node) { + IDiffElement[] children = createModelObjects(node); + for (int i = 0; i < children.length; i++) { + IDiffElement element = children[i]; + if (element instanceof SynchronizeModelElement) { + buildModelObjects((SynchronizeModelElement) element); + } + } + return children; } /* (non-Javadoc) - * @see org.eclipse.team.core.subscribers.ISyncInfoSetChangeListener#syncInfoSetError(org.eclipse.team.core.subscribers.SyncInfoSet, org.eclipse.team.core.ITeamStatus[], org.eclipse.core.runtime.IProgressMonitor) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#doAdd(org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement, org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement) */ - public void syncInfoSetErrors(SyncInfoSet set, ITeamStatus[] errors, IProgressMonitor monitor) { - // TODO Auto-generated method stub + protected void doAdd(SynchronizeModelElement parent, SynchronizeModelElement element) { + AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer(); + viewer.add(parent, element); } /* (non-Javadoc) - * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#getInput() + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#doRemove(org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement) */ - public SynchronizeModelElement getInput() { - return getRoot(); + protected void doRemove(SynchronizeModelElement element) { + AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer(); + viewer.remove(element); } - - /** - * Update the label of the given diff node. Diff nodes - * are accumulated and updated in a single call. - * @param diffNode the diff node to be updated + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#handleResourceAdditions(org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent) */ - protected void updateLabel(SynchronizeModelElement diffNode) { - pendingLabelUpdates.add(diffNode); + protected void handleResourceAdditions(ISyncInfoTreeChangeEvent event) { + IResource[] added = event.getAddedSubtreeRoots(); + addResources(added); } - /** - * Forces the viewer to update the labels for parents whose children have - * changed during this round of sync set changes. + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#handleResourceChanges(org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent) */ - protected void firePendingLabelUpdates() { - try { - if (canUpdateViewer()) { - AbstractTreeViewer tree = getTreeViewer(); - tree.update(pendingLabelUpdates.toArray(new Object[pendingLabelUpdates.size()]), null); + protected void handleResourceChanges(ISyncInfoTreeChangeEvent event) { + // Refresh the viewer for each changed resource + SyncInfo[] infos = event.getChangedResources(); + for (int i = 0; i < infos.length; i++) { + SyncInfo info = infos[i]; + IResource local = info.getLocal(); + SynchronizeModelElement diffNode = getModelObject(local); + if (diffNode != null) { + handleChange(diffNode, info); } - } finally { - pendingLabelUpdates.clear(); - } + } } - protected void setParentConflict(SynchronizeModelElement diffNode, boolean value) { - diffNode.setPropertyToRoot(SynchronizeModelElement.PROPAGATED_CONFLICT_PROPERTY, value); - updateParentLabels(diffNode); - } - - private void updateParentLabels(SynchronizeModelElement diffNode) { - updateLabel(diffNode); - while (diffNode.getParent() != null) { - diffNode = (SynchronizeModelElement)diffNode.getParent(); - updateLabel(diffNode); + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#handleResourceRemovals(org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent) + */ + protected void handleResourceRemovals(ISyncInfoTreeChangeEvent event) { + // Remove the removed subtrees + IResource[] removedRoots = event.getRemovedSubtreeRoots(); + for (int i = 0; i < removedRoots.length; i++) { + removeFromViewer(removedRoots[i]); + } + // We have to look for folders that may no longer be in the set + // (i.e. are in-sync) but still have descendants in the set + IResource[] removedResources = event.getRemovedResources(); + for (int i = 0; i < removedResources.length; i++) { + IResource resource = removedResources[i]; + if (resource.getType() != IResource.FILE) { + SynchronizeModelElement node = getModelObject(resource); + if (node != null) { + removeFromViewer(resource); + addResources(new IResource[] {resource}); + } + } } } } diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/StructuredViewerAdvisor.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/StructuredViewerAdvisor.java index 12511b87a..fa5b10140 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/StructuredViewerAdvisor.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/StructuredViewerAdvisor.java @@ -37,9 +37,12 @@ import org.eclipse.ui.internal.PluginAction; import org.eclipse.ui.model.BaseWorkbenchContentProvider; /** - * A <code>StructuredViewerAdvisor</code> object controls various UI - * aspects of viewers that show {@link SyncInfoSet} like the context menu, toolbar, content - * provider, label provider, and model provider. + * A <code>StructuredViewerAdvisor</code> controls various UI + * aspects of viewers that show {@link SyncInfoSet} like the context menu, toolbar, + * content provider, label provider, navigation, and model provider. The + * advisor allows decoupling viewer behavior from the viewers presentation. This + * allows viewers that aren't in the same class hierarchy to re-use basic + * behavior. * <p> * This advisor allows viewer contributions made in a plug-in manifest to * be scoped to a particular unique id. As a result the context menu for the @@ -61,40 +64,55 @@ import org.eclipse.ui.model.BaseWorkbenchContentProvider; * <li>Create a configuration instance with a <code>menuID</code> that * matches the targetID in the viewer contribution. * </ul> - * <p> - * Clients may use this class as is, or subclass to add new state and behavior. - * The default behavior is to show sync info in a tree + * </p><p> + * Clients may subclass to add behavior for concrete structured viewers. * </p> + * + * @see TreeViewerAdvisor * @since 3.0 */ public abstract class StructuredViewerAdvisor { + private SynchronizeModelProvider modelProvider; private ListenerList listeners; - private String menuId; + private String targetID; private SyncInfoSet set; private StructuredViewer viewer; - public StructuredViewerAdvisor(String menuId, SyncInfoSet set) { + /** + * Create an advisor that will allow viewer contributions with the given <code>targetID</code>. This + * advisor will provide a presentation model based on the given sync info set. Note that it's important + * to call {@link #dispose()} when finished with an advisor. + * + * @param targetID the targetID defined in the viewer contributions in a plugin.xml file. + * @param set the set of <code>SyncInfo</code> objects that are to be shown to the user. + */ + public StructuredViewerAdvisor(String targetID, SyncInfoSet set) { this.set = set; - this.menuId = menuId; + this.targetID = targetID; } + /** + * Create an advisor that will provide a presentation model based on the given sync info set. + * Note that it's important to call {@link #dispose()} when finished with an advisor. + * + * @param set the set of <code>SyncInfo</code> objects that are to be shown to the user. + */ public StructuredViewerAdvisor(SyncInfoSet set) { this(null, set); } /** - * Initialize the viewer with the elements of this configuration, including - * content and label providers, sorter, input and menus. This method is - * invoked from the constructor of <code>SyncInfoDiffTreeViewer</code> to - * initialize the viewers. A configuration instance may only be used with - * one viewer. + * Install a viewer to be configured with this advisor. An advisor can only be installed with + * one viewer at a time. When this method completes the viewer is considered initialized and + * can be shown to the user. - * @param viewer the viewer being initialized + * @param viewer the viewer being installed */ - public void initializeViewer(StructuredViewer viewer) { + public final void initializeViewer(StructuredViewer viewer) { Assert.isTrue(this.viewer == null, "Can only be initialized once."); //$NON-NLS-1$ + Assert.isTrue(validateViewer(viewer)); this.viewer = viewer; initializeListeners(viewer); @@ -112,6 +130,12 @@ public abstract class StructuredViewerAdvisor { setInput(viewer); } + /** + * This is called to add a listener to the model shown in the viewer. The listener is + * called when the model is changed or updated. + * + * @param listener the listener to add + */ public void addInputChangedListener(ISynchronizeModelChangeListener listener) { if (listeners == null) listeners= new ListenerList(); @@ -119,7 +143,20 @@ public abstract class StructuredViewerAdvisor { } /** - * Cleanup listeners + * Remove a model listener. + * + * @param listener the listener to remove. + */ + public void removeInputChangedListener(ISynchronizeModelChangeListener listener) { + if (listeners != null) { + listeners.remove(listener); + if (listeners.isEmpty()) + listeners= null; + } + } + + /** + * Must be called when an advisor is no longer needed. */ public void dispose() { if(modelProvider != null) { @@ -128,31 +165,45 @@ public abstract class StructuredViewerAdvisor { } /** - * Return the menu id that is used to obtain context menu items from the - * workbench. - * @return the menuId. + * Return the targetID that is used to obtain context menu items from the workbench. When + * a context menu is added to the viewer, this ID is registered with the workbench to allow + * viewer contributions. + * + * @return the targetID or <code>null</code> if this advisor doesn't allow contributions. */ - public String getMenuId() { - return menuId; + public String getTargetID() { + return targetID; } /** - * Return the <code>SyncInfoSet</code> being shown by the viewer - * associated with this configuration. - * @return a <code>SyncInfoSet</code> + * Return the <code>SyncInfoSet</code> used to create the model shown by this advisor. + * + * @return the <code>SyncInfoSet</code> used to create the model shown by this advisor. */ public SyncInfoSet getSyncInfoSet() { return set; } + /** + * Subclasses must implement to allow navigation of their viewers. + * + * @param next if <code>true</code> then navigate forwards, otherwise navigate + * backwards. + * @return <code>true</code> if the end is reached, and <code>false</code> otherwise. + */ public abstract boolean navigate(boolean next); /** - * Creates the input for this view and initializes it. At the time this method - * is called the viewer may not of been created yet. - * - * @param monitor shows progress while preparing the input - * @return the input that can be shown in a viewer + * Creates the model that will be shown in the viewers. This can be called before the + * viewer has been created. + * <p> + * The result of this method can be shown used as the input to a viewer. However, the + * prefered method of initializing a viewer is to call {@link #initializeViewer(StructuredViewer)} + * directly. This method only exists when the model must be created before the + * viewer. + * </p> + * @param monitor shows progress while preparing the model + * @return the model that can be shown in a viewer */ public Object prepareInput(IProgressMonitor monitor) throws TeamException { if(modelProvider != null) { @@ -162,42 +213,16 @@ public abstract class StructuredViewerAdvisor { return modelProvider.prepareInput(monitor); } - public void removeInputChangedListener(ISynchronizeModelChangeListener listener) { - if (listeners != null) { - listeners.remove(listener); - if (listeners.isEmpty()) - listeners= null; - } - } - - /** - * Method invoked from <code>initializeViewer(Composite, StructuredViewer)</code> - * in order to initialize any listeners for the viewer. - * @param viewer - * the viewer being initialize - */ - protected abstract void initializeListeners(final StructuredViewer viewer); - - /** - * Get the input that will be assigned to the viewer initialized by this - * configuration. Subclass may override. - * @return the viewer input - */ - protected abstract SynchronizeModelProvider getModelProvider(); - /** * Callback that is invoked when a context menu is about to be shown in the * viewer. Subsclasses must implement to contribute menus. Also, menus can * contributed by creating a viewer contribution with a <code>targetID</code> * that groups sets of actions that are related. * - * @param viewer - * the viewer - * @param manager - * the menu manager + * @param viewer the viewer in which the context menu is being shown. + * @param manager the menu manager to which actions can be added. */ protected void fillContextMenu(final StructuredViewer viewer, IMenuManager manager) { - // subclasses will add actions } /** @@ -209,20 +234,46 @@ public abstract class StructuredViewerAdvisor { * <p> * The default behavior is to add the up and down navigation nuttons to the * toolbar. Subclasses can override. - * @param viewer - * the viewer being initialize + * </p> + * @param viewer the viewer being initialize */ protected void initializeActions(StructuredViewer viewer) { } /** + * Method invoked from <code>initializeViewer(Composite, StructuredViewer)</code> + * in order to initialize any listeners for the viewer. + * + * @param viewer the viewer being initialize + */ + protected void initializeListeners(final StructuredViewer viewer) { + } + + /** + * Get the input that will be assigned to the viewer initialized by this + * configuration. Subclass may override. + * @return the viewer input + */ + protected abstract SynchronizeModelProvider getModelProvider(); + + + /** + * Subclasses can validate that the viewer being initialized with this advisor + * is of the correct type. + * + * @param viewer the viewer to validate + * @return <code>true</code> if the viewer is valid, <code>false</code> otherwise. + */ + protected abstract boolean validateViewer(StructuredViewer viewer); + + /** * Returns whether workbench menu items whould be included in the context * menu. By default, this returns <code>true</code> if there is a menu id * and <code>false</code> otherwise * @return whether to include workbench context menu items */ protected boolean allowParticipantMenuContributions() { - return getMenuId() != null; + return getTargetID() != null; } /** @@ -236,14 +287,19 @@ public abstract class StructuredViewerAdvisor { } } - protected void fireChanges() { + private void fireChanges() { if (listeners != null) { Object[] l= listeners.getListeners(); for (int i= 0; i < l.length; i++) - ((ISynchronizeModelChangeListener) l[i]).modelChanged(modelProvider.getInput()); + ((ISynchronizeModelChangeListener) l[i]).modelChanged(modelProvider.getModelRoot()); } } + /** + * Returns the content provider for the viewer. + * + * @return the content provider for the viewer. + */ protected IStructuredContentProvider getContentProvider() { return new BaseWorkbenchContentProvider(); } @@ -264,20 +320,25 @@ public abstract class StructuredViewerAdvisor { return new SynchronizeModelElementLabelProvider(); } + /** + * Returns the viewer configured by this advisor. + * + * @return the viewer configured by this advisor. + */ protected StructuredViewer getViewer() { return viewer; } /** - * Method invoked from <code>initializeViewer(Composite, StructuredViewer)</code> + * Method invoked from <code>initializeViewer(StructuredViewer)</code> * in order to configure the viewer to call <code>fillContextMenu(StructuredViewer, IMenuManager)</code> - * when a context menu is being displayed in the diff tree viewer. - * @param viewer - * the viewer being initialized + * when a context menu is being displayed in viewer. + * + * @param viewer the viewer being initialized * @see fillContextMenu(StructuredViewer, IMenuManager) */ protected final void hookContextMenu(final StructuredViewer viewer) { - final MenuManager menuMgr = new MenuManager(getMenuId()); //$NON-NLS-1$ + final MenuManager menuMgr = new MenuManager(getTargetID()); //$NON-NLS-1$ menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { @@ -315,23 +376,26 @@ public abstract class StructuredViewerAdvisor { site = Utils.findSite(); } if (site != null) { - site.registerContextMenu(getMenuId(), menuMgr, viewer); + site.registerContextMenu(getTargetID(), menuMgr, viewer); } } } /** - * @param viewer + * Called to set the input to a viewer. The input to a viewer is always the model created + * by the model provider. + * + * @param viewer the viewer to set the input. */ protected final void setInput(StructuredViewer viewer) { modelProvider.setViewer(viewer); viewer.setSorter(modelProvider.getViewerSorter()); - DiffNode input = modelProvider.getInput(); + DiffNode input = modelProvider.getModelRoot(); input.addCompareInputChangeListener(new ICompareInputChangeListener() { public void compareInputChanged(ICompareInput source) { fireChanges(); } }); - viewer.setInput(modelProvider.getInput()); + viewer.setInput(modelProvider.getModelRoot()); } -} +}
\ No newline at end of file diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelElement.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelElement.java index 62921579a..0c05f4ace 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelElement.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelElement.java @@ -20,7 +20,8 @@ import org.eclipse.team.internal.ui.TeamUIPlugin; import org.eclipse.ui.model.IWorkbenchAdapter; /** - * A node that represents synchronization state between elements. + * A model element that can be shown in viewers. + * * @since 3.0 */ public abstract class SynchronizeModelElement extends DiffNode implements IAdaptable { @@ -102,6 +103,9 @@ public abstract class SynchronizeModelElement extends DiffNode implements IAdapt } } + public void fireChanges() { + fireChange(); + } public ImageDescriptor getImageDescriptor(Object object) { IResource resource = getResource(); diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelElementLabelProvider.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelElementLabelProvider.java index 094edbd44..ca14ae936 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelElementLabelProvider.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelElementLabelProvider.java @@ -25,6 +25,9 @@ import org.eclipse.ui.internal.WorkbenchColors; import org.eclipse.ui.model.WorkbenchLabelProvider; /** + * A label provider that decorates viewers showing + * {@link org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement}. + * * @since 3.0 */ public class SynchronizeModelElementLabelProvider extends LabelProvider implements IColorProvider { diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelProvider.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelProvider.java index dbda6b191..8633c6ecf 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelProvider.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/SynchronizeModelProvider.java @@ -10,53 +10,417 @@ *******************************************************************************/ package org.eclipse.team.ui.synchronize.viewers; +import java.util.*; + +import org.eclipse.compare.structuremergeviewer.IDiffContainer; +import org.eclipse.compare.structuremergeviewer.IDiffElement; +import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jface.viewers.StructuredViewer; -import org.eclipse.jface.viewers.ViewerSorter; +import org.eclipse.core.runtime.*; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.*; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Control; +import org.eclipse.team.core.ITeamStatus; +import org.eclipse.team.core.synchronize.*; +import org.eclipse.team.internal.core.Assert; +import org.eclipse.team.internal.ui.Policy; +import org.eclipse.team.internal.ui.TeamUIPlugin; +import org.eclipse.ui.progress.UIJob; /** - * This class is reponsible for creating and maintaining model of - * DiffNodes that can be shown in a viewer. + * This class is reponsible for creating and maintaining a presentation model of + * {@link SynchronizeModelElement} elements that can be shown in a viewer. The model + * is based on the synchronization information contained in the provided {@link SyncInfoSet}. + * <p> + * label updates (property propagation to parent nodes) + * sync change listener (changes, additions, removals, reset) + * batching busy updates + * </p> * + * @see HierarchicalModelProvider + * @see CompressedFoldersModelProvider * @since 3.0 */ -public abstract class SynchronizeModelProvider { +public abstract class SynchronizeModelProvider implements ISyncInfoSetChangeListener { - protected class RootDiffNode extends UnchangedResourceModelElement { - public RootDiffNode() { - super(null, ResourcesPlugin.getWorkspace().getRoot()); + // Flasg to indicate if tree control should be updated while + // building the model. + private boolean refreshViewer; + + protected Map resourceMap = Collections.synchronizedMap(new HashMap()); + + protected SynchronizeModelElement root; + + // The viewer this input is being displayed in + private StructuredViewer viewer; + + private Set pendingLabelUpdates = new HashSet(); + + private LabelUpdateJob labelUpdater = new LabelUpdateJob(); + + private IPropertyChangeListener listener = new IPropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent event) { + if (event.getProperty() == SynchronizeModelElement.BUSY_PROPERTY) { + labelUpdater.add(event.getSource(), ((Boolean)event.getNewValue()).booleanValue()); + } + } + }; + + class LabelUpdateJob extends UIJob { + public static final int BATCH_WAIT_INCREMENT = 100; + Set nodes = new HashSet(); + public LabelUpdateJob() { + super(Policy.bind("HierarchicalModelProvider.0")); //$NON-NLS-1$ + setSystem(true); + } + public IStatus runInUIThread(IProgressMonitor monitor) { + Object[] updates; + synchronized(nodes) { + updates = nodes.toArray(new Object[nodes.size()]); + nodes.clear(); + } + if (canUpdateViewer()) { + StructuredViewer tree = getViewer(); + tree.update(updates, null); + } + schedule(BATCH_WAIT_INCREMENT); + return Status.OK_STATUS; } - public void fireChanges() { - fireChange(); + public void add(Object node, boolean isBusy) { + synchronized(nodes) { + nodes.add(node); + } + if (isBusy) { + schedule(BATCH_WAIT_INCREMENT); + } else { + // Wait when unbusying to give the events a chance to propogate through + // the collector + schedule(BATCH_WAIT_INCREMENT * 10); + } } - public boolean hasChildren() { - // This is required to allow the sync framework to be used in wizards - // where the input is not populated until after the compare input is - // created - // (i.e. the compare input will only create the diff viewer if the - // input has children - return true; + public boolean shouldRun() { + return !nodes.isEmpty(); } } + private SyncInfoSet set; + /** - * Called to initialize this controller and returns the input created by this controller. - * @param monitor + * Create an input based on the provide sync set. The input is not + * initialized until <code>prepareInput</code> is called. + * @param set + * the sync set used as the basis for the model created by this + * input. + */ + public SynchronizeModelProvider(SyncInfoSet set) { + this(new UnchangedResourceModelElement(null, ResourcesPlugin.getWorkspace().getRoot()), set); + } + + public SynchronizeModelProvider(SynchronizeModelElement parent, SyncInfoSet set) { + Assert.isNotNull(set); + Assert.isNotNull(parent); + this.root = parent; + this.set = set; + } + + public SyncInfoSet getSyncInfoSet() { + return set; + } + + /** + * Return the <code>AbstractTreeViewer</code> asociated with this content + * provider or <code>null</code> if the viewer is not of the proper type. * @return */ - public abstract SynchronizeModelElement prepareInput(IProgressMonitor monitor); + public StructuredViewer getViewer() { + return viewer; + } + + public void setViewer(StructuredViewer viewer) { + Assert.isTrue(viewer instanceof AbstractTreeViewer); + this.viewer = (AbstractTreeViewer) viewer; + } + + /** + * Builds the viewer model based on the contents of the sync set. + */ + public SynchronizeModelElement prepareInput(IProgressMonitor monitor) { + // Connect to the sync set which will register us as a listener and give us a reset event + // in a background thread + getSyncInfoSet().connect(this, monitor); + return getModelRoot(); + } + + /** + * Dispose of the builder + */ + public void dispose() { + resourceMap.clear(); + getSyncInfoSet().removeSyncSetChangedListener(this); + } /** * Returns the input created by this controller or <code>null</code> if * {@link #prepareInput(IProgressMonitor)} hasn't been called on this object yet. * @return */ - public abstract SynchronizeModelElement getInput(); - - public abstract void setViewer(StructuredViewer viewer); + public SynchronizeModelElement getModelRoot() { + return root; + } public abstract ViewerSorter getViewerSorter(); - public abstract void dispose(); + /** + * Return the model object (i.e. an instance of <code>SyncInfoModelElement</code> + * or one of its subclasses) for the given IResource. + * @param resource + * the resource + * @return the <code>SyncInfoModelElement</code> for the given resource + */ + protected SynchronizeModelElement getModelObject(IResource resource) { + return (SynchronizeModelElement) resourceMap.get(resource); + } + + public void syncInfoChanged(final ISyncInfoSetChangeEvent event, IProgressMonitor monitor) { + if (! (event instanceof ISyncInfoTreeChangeEvent)) { + reset(); + } else { + final Control ctrl = getViewer().getControl(); + if (ctrl != null && !ctrl.isDisposed()) { + ctrl.getDisplay().syncExec(new Runnable() { + public void run() { + if (!ctrl.isDisposed()) { + BusyIndicator.showWhile(ctrl.getDisplay(), new Runnable() { + public void run() { + handleChanges((ISyncInfoTreeChangeEvent)event); + getModelRoot().fireChanges(); + } + }); + } + } + }); + } + } + } + + /** + * For each node create children based on the contents of + * @param node + * @return + */ + protected abstract IDiffElement[] buildModelObjects(SynchronizeModelElement node); + + protected abstract void doAdd(SynchronizeModelElement parent, SynchronizeModelElement element); + + protected abstract void doRemove(SynchronizeModelElement element); + + protected void associateDiffNode(SynchronizeModelElement node) { + IResource resource = node.getResource(); + if(resource != null) { + resourceMap.put(resource, node); + } + } + + protected void unassociateDiffNode(IResource resource) { + resourceMap.remove(resource); + } + + /** + * Handle the changes made to the viewer's <code>SyncInfoSet</code>. + * This method delegates the changes to the three methods <code>handleResourceChanges(ISyncInfoSetChangeEvent)</code>, + * <code>handleResourceRemovals(ISyncInfoSetChangeEvent)</code> and + * <code>handleResourceAdditions(ISyncInfoSetChangeEvent)</code>. + * @param event + * the event containing the changed resourcses. + */ + protected void handleChanges(ISyncInfoTreeChangeEvent event) { + StructuredViewer viewer = getViewer(); + try { + viewer.getControl().setRedraw(false); + handleResourceChanges(event); + handleResourceRemovals(event); + handleResourceAdditions(event); + firePendingLabelUpdates(); + } finally { + viewer.getControl().setRedraw(true); + } + } + + /** + * Update the viewer for the sync set additions in the provided event. This + * method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>. + * Subclasses may override. + * @param event + */ + protected abstract void handleResourceAdditions(ISyncInfoTreeChangeEvent event); + + /** + * Update the viewer for the sync set changes in the provided event. This + * method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>. + * Subclasses may override. + * @param event + */ + protected abstract void handleResourceChanges(ISyncInfoTreeChangeEvent event); + + protected boolean isConflicting(SynchronizeModelElement diffNode) { + return (diffNode.getKind() & SyncInfo.DIRECTION_MASK) == SyncInfo.CONFLICTING; + } + + /** + * Update the viewer for the sync set removals in the provided event. This + * method is invoked by <code>handleChanges(ISyncInfoSetChangeEvent)</code>. + * Subclasses may override. + * @param event + */ + protected abstract void handleResourceRemovals(ISyncInfoTreeChangeEvent event); + + protected void reset() { + try { + refreshViewer = false; + + // Clear existing model, but keep the root node + resourceMap.clear(); + clearModelObjects(getModelRoot()); + // remove all from tree viewer + IDiffElement[] elements = getModelRoot().getChildren(); + for (int i = 0; i < elements.length; i++) { + doRemove((SynchronizeModelElement)elements[i]); + } + + // Rebuild the model + associateDiffNode(getModelRoot()); + buildModelObjects(getModelRoot()); + + // Notify listeners that model has changed + getModelRoot().fireChanges(); + } finally { + refreshViewer = true; + } + TeamUIPlugin.getStandardDisplay().asyncExec(new Runnable() { + public void run() { + StructuredViewer viewer = getViewer(); + if (viewer != null && !viewer.getControl().isDisposed()) { + viewer.refresh(); + } + } + }); + } + + /** + * Remove any traces of the resource and any of it's descendants in the + * hiearchy defined by the content provider from the content provider and + * the viewer it is associated with. + * @param resource + */ + protected void removeFromViewer(IResource resource) { + SynchronizeModelElement node = getModelObject(resource); + if (node == null) return; + if (isConflicting(node)) { + setParentConflict(node, false); + } + clearModelObjects(node); + if (canUpdateViewer()) { + doRemove(node); + } + } + + /** + * Clear the model objects from the diff tree, cleaning up any cached state + * (such as resource to model object map). This method recurses deeply on + * the tree to allow the cleanup of any cached state for the children as + * well. + * @param node + * the root node + */ + protected void clearModelObjects(SynchronizeModelElement node) { + IDiffElement[] children = node.getChildren(); + for (int i = 0; i < children.length; i++) { + IDiffElement element = children[i]; + if (element instanceof SynchronizeModelElement) { + clearModelObjects((SynchronizeModelElement) element); + } + } + IResource resource = node.getResource(); + if (resource != null) { + unassociateDiffNode(resource); + } + IDiffContainer parent = node.getParent(); + if (parent != null) { + parent.removeToRoot(node); + } + } + + protected void addToViewer(SynchronizeModelElement node) { + associateDiffNode(node); + node.addPropertyChangeListener(listener); + if (isConflicting(node)) { + setParentConflict(node, true); + } + if (canUpdateViewer()) { + doAdd((SynchronizeModelElement)node.getParent(), node); + } + } + + /* (non-Javadoc) + * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoSetReset(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.core.runtime.IProgressMonitor) + */ + public void syncInfoSetReset(SyncInfoSet set, IProgressMonitor monitor) { + reset(); + } + + /* (non-Javadoc) + * @see org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener#syncInfoSetErrors(org.eclipse.team.core.synchronize.SyncInfoSet, org.eclipse.team.core.ITeamStatus[], org.eclipse.core.runtime.IProgressMonitor) + */ + public void syncInfoSetErrors(SyncInfoSet set, ITeamStatus[] errors, IProgressMonitor monitor) { + // When errors occur we currently don't process them. It may be possible to decorate + // elements in the model with errors, but currently we prefer to let ignore and except + // another listener to display them. + } + + /** + * Update the label of the given diff node. Diff nodes + * are accumulated and updated in a single call. + * @param diffNode the diff node to be updated + */ + protected void updateLabel(SynchronizeModelElement diffNode) { + pendingLabelUpdates.add(diffNode); + } + + /** + * @param tree + * @return + */ + private boolean canUpdateViewer() { + return refreshViewer && getViewer() != null; + } + + /** + * Forces the viewer to update the labels for parents whose children have + * changed during this round of sync set changes. + */ + protected void firePendingLabelUpdates() { + try { + if (canUpdateViewer()) { + StructuredViewer tree = getViewer(); + tree.update(pendingLabelUpdates.toArray(new Object[pendingLabelUpdates.size()]), null); + } + } finally { + pendingLabelUpdates.clear(); + } + } + + protected void setParentConflict(SynchronizeModelElement diffNode, boolean value) { + diffNode.setPropertyToRoot(SynchronizeModelElement.PROPAGATED_CONFLICT_PROPERTY, value); + updateParentLabels(diffNode); + } + + private void updateParentLabels(SynchronizeModelElement diffNode) { + updateLabel(diffNode); + while (diffNode.getParent() != null) { + diffNode = (SynchronizeModelElement)diffNode.getParent(); + updateLabel(diffNode); + } + } }
\ No newline at end of file diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/TreeViewerAdvisor.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/TreeViewerAdvisor.java index 39b059c18..4e688fc6e 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/TreeViewerAdvisor.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/viewers/TreeViewerAdvisor.java @@ -14,123 +14,117 @@ import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; -import org.eclipse.jface.viewers.*; -import org.eclipse.swt.widgets.*; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.OpenEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; import org.eclipse.team.core.TeamException; -import org.eclipse.team.core.synchronize.*; -import org.eclipse.team.internal.core.Assert; -import org.eclipse.team.internal.ui.*; +import org.eclipse.team.core.synchronize.SyncInfoTree; +import org.eclipse.team.internal.ui.IPreferenceIds; +import org.eclipse.team.internal.ui.TeamUIPlugin; +import org.eclipse.team.internal.ui.Utils; import org.eclipse.team.internal.ui.synchronize.actions.ExpandAllAction; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.internal.dialogs.ContainerCheckedTreeViewer; /** - * A <code>TreeViewerAdvisor</code> object controls various UI - * aspects of sync info viewers like the context menu, toolbar, content - * provider, and label provider. A configuration is created to display - * {@link SyncInfo} objects contained in the provided {@link SyncInfoSet}. + * A <code>TreeViewerAdvisor</code> that works with TreeViewers. Two default + * tree viewers are provided that support navigation: <code>NavigableTreeViewer</code> + * and <code>NavigableCheckboxTreeViewer</code>. * <p> - * This configuration allows viewer contributions made in a plug-in manifest to - * be scoped to a particular unique id. As a result the context menu for the - * viewer can be configured to show object contributions for random id schemes. - * To enable declarative action contributions for a configuration there are two - * steps required: - * <ul> - * <li>Create a viewer contribution with a <code>targetID</code> that groups - * sets of actions that are related. A common pratice for synchronize view - * configurations is to use the participant id as the targetID. - * - * <pre> - * <viewerContribution - * id="org.eclipse.team.ccvs.ui.CVSCompareSubscriberContributions" - * targetID="org.eclipse.team.cvs.ui.compare-participant"> - * ... - * </pre> - * - * <li>Create a configuration instance with a <code>menuID</code> that - * matches the targetID in the viewer contribution. - * </ul> + * Note that this advisor can be used with any tree viewer. By default it provides an + * expand all action, double click behavior on containers, and navigation support for + * tree viewers. + * </p><p> + * By default this advisor supports hierarchical models and honour the compressed + * folder Team preference for showing the sync set as compressed folders. Subclasses + * can provide their own presentation models. * <p> - * Clients may use this class as is, or subclass to add new state and behavior. - * The default behavior is to show sync info in a tree - * </p> * @since 3.0 */ public class TreeViewerAdvisor extends StructuredViewerAdvisor implements IPropertyChangeListener { - - private ExpandAllAction expandAllAction; + /** + * Interface used to implement navigation for tree viewers. This interface is used by + * {@link TreeViewerAdvisor#navigate(TreeViewer, boolean, boolean, boolean) to open + * selections and navigate. + */ public interface ITreeViewerAccessor { - public void openSelection(); public void createChildren(TreeItem item); + public void openSelection(); } - public static class NavigableTreeViewer extends TreeViewer implements ITreeViewerAccessor { - public NavigableTreeViewer(Composite parent, int style) { + /** + * A navigable checkboxec tree viewer that will work with the <code>navigate</code> method of + * this advisor. + */ + public static class NavigableCheckboxTreeViewer extends ContainerCheckedTreeViewer implements ITreeViewerAccessor { + public NavigableCheckboxTreeViewer(Composite parent, int style) { super(parent, style); } - public void openSelection() { - fireOpen(new OpenEvent(this, getSelection())); - } - public void createChildren(TreeItem item) { super.createChildren(item); } - } - - public static class NavigableCheckboxTreeViewer extends ContainerCheckedTreeViewer implements ITreeViewerAccessor { - public NavigableCheckboxTreeViewer(Composite parent, int style) { - super(parent, style); - } public void openSelection() { fireOpen(new OpenEvent(this, getSelection())); } + } + + /** + * A navigable tree viewer that will work with the <code>navigate</code> method of + * this advisor. + */ + public static class NavigableTreeViewer extends TreeViewer implements ITreeViewerAccessor { + public NavigableTreeViewer(Composite parent, int style) { + super(parent, style); + } public void createChildren(TreeItem item) { super.createChildren(item); } + + public void openSelection() { + fireOpen(new OpenEvent(this, getSelection())); + } } - /** - * Create a <code>SyncInfoSetCompareConfiguration</code> for the given - * sync set. - * @param set - * the <code>SyncInfoSet</code> to be displayed in the - * resulting diff viewer. - */ - public TreeViewerAdvisor(SyncInfoTree set) { - this(null, set); - } + private ExpandAllAction expandAllAction; /** - * Create a <code>SyncInfoSetCompareConfiguration</code> for the given - * sync set and menuId. If the menuId is <code>null</code>, then no - * contributed menus will be shown in the diff viewer created from this - * configuration. - * @param menuId - * the id of <code>targetID</code> specified in <code>viewerContribution</code> - * extension points. - * @param set - * the <code>SyncInfoSet</code> to be displayed in the - * resulting diff viewer + * Create an advisor that will allow viewer contributions with the given <code>targetID</code>. This + * advisor will provide a presentation model based on the given sync info set. Note that it's important + * to call {@link #dispose()} when finished with an advisor. + * + * @param targetID the targetID defined in the viewer contributions in a plugin.xml file. + * @param set the set of <code>SyncInfo</code> objects that are to be shown to the user. */ public TreeViewerAdvisor(String menuId, SyncInfoTree set) { super(menuId, set); TeamUIPlugin.getPlugin().getPreferenceStore().addPropertyChangeListener(this); } - /* (non-Javadoc) - * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#initializeViewer(org.eclipse.jface.viewers.StructuredViewer) + /** + * Create a tree viewer advisor that will provide a presentation model based on the given + * sync info set. Note that it's important to call {@link #dispose()} when finished with + * an advisor. + * + * @param set the set of <code>SyncInfo</code> objects that are to be shown to the user. */ - public void initializeViewer(StructuredViewer viewer) { - super.initializeViewer(viewer); - Assert.isTrue(viewer instanceof AbstractTreeViewer); + public TreeViewerAdvisor(SyncInfoTree set) { + this(null, set); } - /* (non-Javadoc) * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#dispose() */ @@ -140,61 +134,13 @@ public class TreeViewerAdvisor extends StructuredViewerAdvisor implements IPrope } /* (non-Javadoc) - * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#initializeListeners(org.eclipse.jface.viewers.StructuredViewer) - */ - protected void initializeListeners(StructuredViewer viewer) { - viewer.addDoubleClickListener(new IDoubleClickListener() { - public void doubleClick(DoubleClickEvent event) { - handleDoubleClick(getViewer(), event); - } - }); - } - - - /* (non-Javadoc) - * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#initializeActions(org.eclipse.jface.viewers.StructuredViewer) + * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#navigate(boolean) */ - protected void initializeActions(StructuredViewer viewer) { - super.initializeActions(viewer); - expandAllAction = new ExpandAllAction((AbstractTreeViewer) viewer); - Utils.initAction(expandAllAction, "action.expandAll."); //$NON-NLS-1$ + public boolean navigate(boolean next) { + return TreeViewerAdvisor.navigate((TreeViewer)getViewer(), next, true, false); } - /** - * Handles a double-click event from the viewer. Expands or collapses a - * folder when double-clicked. - * @param viewer - * the viewer - * @param event - * the double-click event - */ - protected void handleDoubleClick(StructuredViewer viewer, DoubleClickEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - Object element = selection.getFirstElement(); - AbstractTreeViewer treeViewer = (AbstractTreeViewer) getViewer(); - if (treeViewer.getExpandedState(element)) { - treeViewer.collapseToLevel(element, AbstractTreeViewer.ALL_LEVELS); - } else { - TreeViewerAdvisor.navigate((TreeViewer)getViewer(), true /* next */, false /* no-open */, true /* only-expand */); - } - } - /* (non-Javadoc) - * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#getDiffNodeController() - */ - protected SynchronizeModelProvider getModelProvider() { - if(getShowCompressedFolders()) { - return new CompressedFoldersModelProvider((SyncInfoTree)getSyncInfoSet()); - } - return new HierarchicalModelProvider((SyncInfoTree)getSyncInfoSet()); - } - - private boolean getShowCompressedFolders() { - return TeamUIPlugin.getPlugin().getPreferenceStore().getBoolean(IPreferenceIds.SYNCVIEW_COMPRESS_FOLDERS); - } - - /* - * (non-Javadoc) * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent event) { @@ -208,7 +154,13 @@ public class TreeViewerAdvisor extends StructuredViewerAdvisor implements IPrope } } - + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#initializeViewer(org.eclipse.jface.viewers.StructuredViewer) + */ + public boolean validateViewer(StructuredViewer viewer) { + return viewer instanceof AbstractTreeViewer; + } + /* (non-Javadoc) * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#fillContextMenu(org.eclipse.jface.viewers.StructuredViewer, org.eclipse.jface.action.IMenuManager) */ @@ -216,73 +168,62 @@ public class TreeViewerAdvisor extends StructuredViewerAdvisor implements IPrope manager.add(expandAllAction); manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } - + + /* (non-Javadoc) - * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#navigate(boolean) + * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#getDiffNodeController() */ - public boolean navigate(boolean next) { - return TreeViewerAdvisor.navigate((TreeViewer)getViewer(), next, true, false); + protected SynchronizeModelProvider getModelProvider() { + if(getShowCompressedFolders()) { + return new CompressedFoldersModelProvider((SyncInfoTree)getSyncInfoSet()); + } + return new HierarchicalModelProvider((SyncInfoTree)getSyncInfoSet()); } + /** - * Selects the next (or previous) node of the current selection. - * If there is no current selection the first (last) node in the tree is selected. - * Wraps around at end or beginning. - * Clients may override. - * - * @param next if <code>true</code> the next node is selected, otherwise the previous node - * @return <code>true</code> if at end (or beginning) + * Handles a double-click event from the viewer. Expands or collapses a folder when double-clicked. + * + * @param viewer the viewer + * @param event the double-click event */ - public static boolean navigate(TreeViewer viewer, boolean next, boolean fireOpen, boolean expandOnly) { - Tree tree = viewer.getTree(); - if (tree == null) - return false; - TreeItem item = null; - TreeItem children[] = tree.getSelection(); - if (children != null && children.length > 0) - item = children[0]; - if (item == null) { - children = tree.getItems(); - if (children != null && children.length > 0) { - item = children[0]; - if (item != null && item.getItemCount() <= 0) { - setSelection(viewer, item, fireOpen, expandOnly); // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106 - return false; - } - } - } - while (true) { - item = findNextPrev(viewer, item, next); - if (item == null) - break; - if (item.getItemCount() <= 0) - break; - } - if (item != null) { - setSelection(viewer, item, fireOpen, expandOnly); // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106 - return false; + protected void handleDoubleClick(StructuredViewer viewer, DoubleClickEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + Object element = selection.getFirstElement(); + AbstractTreeViewer treeViewer = (AbstractTreeViewer) getViewer(); + if (treeViewer.getExpandedState(element)) { + treeViewer.collapseToLevel(element, AbstractTreeViewer.ALL_LEVELS); + } else { + TreeViewerAdvisor.navigate((TreeViewer)getViewer(), true /* next */, false /* no-open */, true /* only-expand */); } - return true; } - - private static void setSelection(TreeViewer viewer, TreeItem ti, boolean fireOpen, boolean expandOnly) { - if (ti != null) { - Object data= ti.getData(); - if (data != null) { - // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106 - ISelection selection = new StructuredSelection(data); - if (expandOnly) { - viewer.expandToLevel(data, 0); - } else { - viewer.setSelection(selection, true); - ISelection currentSelection = viewer.getSelection(); - if (fireOpen && currentSelection != null && selection.equals(currentSelection)) { - if (viewer instanceof ITreeViewerAccessor) { - ((ITreeViewerAccessor) viewer).openSelection(); - } - } - } + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#initializeActions(org.eclipse.jface.viewers.StructuredViewer) + */ + protected void initializeActions(StructuredViewer viewer) { + super.initializeActions(viewer); + expandAllAction = new ExpandAllAction((AbstractTreeViewer) viewer); + Utils.initAction(expandAllAction, "action.expandAll."); //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.team.ui.synchronize.viewers.StructuredViewerAdvisor#initializeListeners(org.eclipse.jface.viewers.StructuredViewer) + */ + protected void initializeListeners(StructuredViewer viewer) { + viewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(DoubleClickEvent event) { + handleDoubleClick(getViewer(), event); } - } + }); + } + + /** + * Return the state of the compressed folder setting. + * + * @return the state of the compressed folder setting. + */ + private boolean getShowCompressedFolders() { + return TeamUIPlugin.getPlugin().getPreferenceStore().getBoolean(IPreferenceIds.SYNCVIEW_COMPRESS_FOLDERS); } private static TreeItem findNextPrev(TreeViewer viewer, TreeItem item, boolean next) { @@ -350,4 +291,66 @@ public class TreeViewerAdvisor extends StructuredViewerAdvisor implements IPrope } return item; } + + private static void setSelection(TreeViewer viewer, TreeItem ti, boolean fireOpen, boolean expandOnly) { + if (ti != null) { + Object data= ti.getData(); + if (data != null) { + // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106 + ISelection selection = new StructuredSelection(data); + if (expandOnly) { + viewer.expandToLevel(data, 0); + } else { + viewer.setSelection(selection, true); + ISelection currentSelection = viewer.getSelection(); + if (fireOpen && currentSelection != null && selection.equals(currentSelection)) { + if (viewer instanceof ITreeViewerAccessor) { + ((ITreeViewerAccessor) viewer).openSelection(); + } + } + } + } + } + } + + /** + * Selects the next (or previous) node of the current selection. + * If there is no current selection the first (last) node in the tree is selected. + * Wraps around at end or beginning. + * Clients may not override. + * + * @param next if <code>true</code> the next node is selected, otherwise the previous node + * @return <code>true</code> if at end (or beginning) + */ + public static boolean navigate(TreeViewer viewer, boolean next, boolean fireOpen, boolean expandOnly) { + Tree tree = viewer.getTree(); + if (tree == null) + return false; + TreeItem item = null; + TreeItem children[] = tree.getSelection(); + if (children != null && children.length > 0) + item = children[0]; + if (item == null) { + children = tree.getItems(); + if (children != null && children.length > 0) { + item = children[0]; + if (item != null && item.getItemCount() <= 0) { + setSelection(viewer, item, fireOpen, expandOnly); // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106 + return false; + } + } + } + while (true) { + item = findNextPrev(viewer, item, next); + if (item == null) + break; + if (item.getItemCount() <= 0) + break; + } + if (item != null) { + setSelection(viewer, item, fireOpen, expandOnly); // Fix for http://dev.eclipse.org/bugs/show_bug.cgi?id=20106 + return false; + } + return true; + } }
\ No newline at end of file diff --git a/tests/org.eclipse.team.tests.core/plugin.xml b/tests/org.eclipse.team.tests.core/plugin.xml index 332327000..6b1b468a1 100644 --- a/tests/org.eclipse.team.tests.core/plugin.xml +++ b/tests/org.eclipse.team.tests.core/plugin.xml @@ -91,6 +91,19 @@ id="org.eclipse.team.tests.core.linking"> </repository> </extension> + <extension + point="org.eclipse.ui.views"> + <category + name="Team Test Views" + id="org.eclipse.team.tests.core"> + </category> + <view + name="Content Provider Test View" + category="org.eclipse.team.tests.core" + class="org.eclipse.team.tests.ui.views.ContentProviderTestView" + id="org.eclipse.team.tests.ui.views.ContentProviderTestView"> + </view> + </extension> </plugin> |