Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Valenta2004-09-02 20:31:38 +0000
committerMichael Valenta2004-09-02 20:31:38 +0000
commit4d75684251589b8d7d2ff3ad4081a20a419d9256 (patch)
tree8e765a9dac16135360d315238a97a4f0912c8786
parentc02c796dcf8c9a29cf2a210152b03a78742cfd07 (diff)
downloadeclipse.platform.team-4d75684251589b8d7d2ff3ad4081a20a419d9256.tar.gz
eclipse.platform.team-4d75684251589b8d7d2ff3ad4081a20a419d9256.tar.xz
eclipse.platform.team-4d75684251589b8d7d2ff3ad4081a20a419d9256.zip
Starting outgoing commit set work on a branch
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java4
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CommitCommentArea.java41
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/IHelpContextIds.java1
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ReleaseCommentDialog.java8
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CommitOperation.java2
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/repo/RepositoryManager.java5
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelManager.java2
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelProvider.java499
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelSorter.java13
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSet.java297
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetDialog.java220
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetDiffNode.java68
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetManager.java456
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ICommitSetChangeListener.java50
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/WorkspaceCommitOperation.java26
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/CommitSetTests.java208
16 files changed, 1823 insertions, 77 deletions
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
index 896f26988..defa11578 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
@@ -38,6 +38,7 @@ import org.eclipse.team.internal.ccvs.ui.console.CVSOutputConsole;
import org.eclipse.team.internal.ccvs.ui.model.CVSAdapterFactory;
import org.eclipse.team.internal.ccvs.ui.repo.RepositoryManager;
import org.eclipse.team.internal.ccvs.ui.repo.RepositoryRoot;
+import org.eclipse.team.internal.ccvs.ui.subscriber.CommitSetManager;
import org.eclipse.team.internal.ccvs.ui.subscriber.WorkspaceSynchronizeParticipant;
import org.eclipse.team.internal.ui.TeamUIPlugin;
import org.eclipse.team.internal.ui.Utils;
@@ -664,6 +665,8 @@ public class CVSUIPlugin extends AbstractUIPlugin {
Platform.getAdapterManager().registerAdapters(factory, RepositoryRoot.class);
console = new CVSOutputConsole();
+
+ CommitSetManager.getInstance();
IPreferenceStore store = getPreferenceStore();
if (store.getBoolean(ICVSUIConstants.PREF_FIRST_STARTUP)) {
@@ -703,6 +706,7 @@ public class CVSUIPlugin extends AbstractUIPlugin {
}
console.shutdown();
+ CommitSetManager.getInstance().shutdown();
} finally {
super.stop(context);
}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CommitCommentArea.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CommitCommentArea.java
index c890dadf2..524512625 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CommitCommentArea.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CommitCommentArea.java
@@ -37,6 +37,8 @@ public class CommitCommentArea extends DialogArea {
private String comment = ""; //$NON-NLS-1$
public static final String OK_REQUESTED = "OkRequested";//$NON-NLS-1$
+ public static final String COMMENT_MODIFIED = "CommentModified";//$NON-NLS-1$
+ private String proposedComment;
/**
* Constructor for CommitCommentArea.
@@ -77,7 +79,9 @@ public class CommitCommentArea extends DialogArea {
});
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
+ String oldComment = comment;
comment = text.getText();
+ CommitCommentArea.this.signalCommentModified(oldComment, comment);
}
});
@@ -105,7 +109,7 @@ public class CommitCommentArea extends DialogArea {
});
}
- /**
+ /**
* Method initializeValues.
*/
private void initializeValues() {
@@ -120,12 +124,13 @@ public class CommitCommentArea extends DialogArea {
previousCommentsCombo.setText(""); //$NON-NLS-1$
// determine the initial comment text
- String initialComment;
- try {
- initialComment = getCommitTemplate();
- } catch (CVSException e) {
- CVSUIPlugin.log(e);
- initialComment = null;
+ String initialComment = proposedComment;
+ if (initialComment == null) {
+ try {
+ initialComment = getCommitTemplate();
+ } catch (CVSException e) {
+ CVSUIPlugin.log(e);
+ }
}
if (initialComment != null && initialComment.length() != 0) {
text.setText(initialComment);
@@ -138,6 +143,10 @@ public class CommitCommentArea extends DialogArea {
private void signalCtrlEnter() {
firePropertyChangeChange(OK_REQUESTED, null, null);
}
+
+ protected void signalCommentModified(String oldValue, String comment) {
+ firePropertyChangeChange(COMMENT_MODIFIED, oldValue, comment);
+ }
private String getCommitTemplate() throws CVSException {
CVSTeamProvider provider = getProvider();
@@ -205,4 +214,22 @@ public class CommitCommentArea extends DialogArea {
text.setFocus();
}
}
+
+ public void setProposedComment(String proposedComment) {
+ this.proposedComment = proposedComment;
+ }
+
+ public boolean hasCommitTemplate() {
+ try {
+ return getCommitTemplate() != null;
+ } catch (CVSException e) {
+ CVSUIPlugin.log(e);
+ return false;
+ }
+ }
+
+ public void setEnabled(boolean b) {
+ text.setEnabled(b);
+ previousCommentsCombo.setEnabled(b);
+ }
}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/IHelpContextIds.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/IHelpContextIds.java
index 2d764910b..5088907cd 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/IHelpContextIds.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/IHelpContextIds.java
@@ -31,6 +31,7 @@ public interface IHelpContextIds {
public static final String HISTORY_FILTER_DIALOG = PREFIX + "history_filter_dialog_context"; //$NON-NLS-1$
public static final String DATE_TAG_DIALOG = PREFIX + "date_tag_dialog_context"; //$NON-NLS-1$
public static final String KEYBOARD_INTERACTIVE_DIALOG = PREFIX + "keyboard_interactive_dialog_context"; //$NON-NLS-1$
+ public static final String COMMIT_SET_DIALOG = PREFIX + "commit_set_dialog_context"; //$NON-NLS-1$
// Different uses of the TagSelectionDialog
public static final String REPLACE_TAG_SELECTION_DIALOG = PREFIX + "replace_tag_selection_dialog_context"; //$NON-NLS-1$
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ReleaseCommentDialog.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ReleaseCommentDialog.java
index 72eb65826..e98487b86 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ReleaseCommentDialog.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ReleaseCommentDialog.java
@@ -32,8 +32,6 @@ public class ReleaseCommentDialog extends DetailsDialog {
CommitCommentArea commitCommentArea;
// dialogs settings that are persistent between workbench sessions
private IDialogSettings settings;
- private IResource[] resourcesToCommit;
- private int depth;
private static final String HEIGHT_KEY = "width-key"; //$NON-NLS-1$
private static final String WIDTH_KEY = "height-key"; //$NON-NLS-1$
@@ -41,17 +39,17 @@ public class ReleaseCommentDialog extends DetailsDialog {
* ReleaseCommentDialog constructor.
*
* @param parentShell the parent of this dialog
+ * @param proposedComment
*/
- public ReleaseCommentDialog(Shell parentShell, IResource[] resourcesToCommit, int depth) {
+ public ReleaseCommentDialog(Shell parentShell, IResource[] resourcesToCommit, String proposedComment, int depth) {
super(parentShell, Policy.bind("ReleaseCommentDialog.title")); //$NON-NLS-1$
- this.resourcesToCommit = resourcesToCommit;
- this.depth = depth;
int shellStyle = getShellStyle();
setShellStyle(shellStyle | SWT.RESIZE | SWT.MAX);
commitCommentArea = new CommitCommentArea(this, null);
// Get a project from which the commit template can be obtained
if (resourcesToCommit.length > 0)
commitCommentArea.setProject(resourcesToCommit[0].getProject());
+ commitCommentArea.setProposedComment(proposedComment);
IDialogSettings workbenchSettings = CVSUIPlugin.getPlugin().getDialogSettings();
this.settings = workbenchSettings.getSection("ReleaseCommentDialog");//$NON-NLS-1$
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CommitOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CommitOperation.java
index 55ec9da42..ad6792e98 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CommitOperation.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CommitOperation.java
@@ -164,7 +164,7 @@ public class CommitOperation extends SingleCommandOperation {
protected String promptForComment(IResource[] resourcesToCommit) {
RepositoryManager manager = CVSUIPlugin.getPlugin().getRepositoryManager();
- return manager.promptForComment(getShell(), resourcesToCommit);
+ return manager.promptForComment(getShell(), resourcesToCommit, null);
}
/*
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/repo/RepositoryManager.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/repo/RepositoryManager.java
index 0fd57afaf..18ef67c0d 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/repo/RepositoryManager.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/repo/RepositoryManager.java
@@ -533,10 +533,11 @@ public class RepositoryManager {
/**
* Return the entered comment or null if canceled.
+ * @param proposedComment
*/
- public String promptForComment(final Shell shell, IResource[] resourcesToCommit) {
+ public String promptForComment(final Shell shell, IResource[] resourcesToCommit, String proposedComment) {
final int[] result = new int[1];
- final ReleaseCommentDialog dialog = new ReleaseCommentDialog(shell, resourcesToCommit, IResource.DEPTH_INFINITE);
+ final ReleaseCommentDialog dialog = new ReleaseCommentDialog(shell, resourcesToCommit, proposedComment, IResource.DEPTH_INFINITE);
shell.getDisplay().syncExec(new Runnable() {
public void run() {
result[0] = dialog.open();
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelManager.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelManager.java
index 9d86bf6a8..7fb4e9198 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelManager.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelManager.java
@@ -73,7 +73,7 @@ public class ChangeLogModelManager extends SynchronizeModelManager implements IP
} else if(id.endsWith(HierarchicalModelProvider.HierarchicalModelProviderDescriptor.ID)) {
return new HierarchicalModelProvider(getConfiguration(), getSyncInfoSet());
} else {
- return new ChangeLogModelProvider(getConfiguration(), getSyncInfoSet(), tag1, tag2);
+ return new ChangeLogModelProvider(getConfiguration(), getSyncInfoSet(), tag1, tag2);
}
}
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 cc2cf451a..81f45a05d 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
@@ -10,21 +10,27 @@
*******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.subscriber;
+import java.lang.reflect.InvocationTargetException;
import java.util.*;
-import org.eclipse.compare.structuremergeviewer.IDiffContainer;
-import org.eclipse.compare.structuremergeviewer.IDiffElement;
+
+import org.eclipse.compare.structuremergeviewer.*;
+import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.jface.action.Action;
-import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.resource.ImageDescriptor;
+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.TeamException;
import org.eclipse.team.core.subscribers.Subscriber;
import org.eclipse.team.core.synchronize.*;
+import org.eclipse.team.core.synchronize.FastSyncInfoFilter.AndSyncInfoFilter;
+import org.eclipse.team.core.synchronize.FastSyncInfoFilter.SyncInfoDirectionFilter;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.resources.*;
@@ -36,6 +42,7 @@ import org.eclipse.team.internal.ui.TeamUIPlugin;
import org.eclipse.team.internal.ui.Utils;
import org.eclipse.team.internal.ui.synchronize.*;
import org.eclipse.team.ui.synchronize.*;
+import org.eclipse.ui.actions.BaseSelectionListenerAction;
import org.eclipse.ui.progress.UIJob;
/**
@@ -50,7 +57,7 @@ import org.eclipse.ui.progress.UIJob;
*
* @since 3.0
*/
-public class ChangeLogModelProvider extends SynchronizeModelProvider {
+public class ChangeLogModelProvider extends SynchronizeModelProvider implements ICommitSetChangeListener {
// Log operation that is used to fetch revision histories from the server. It also
// provides caching so we keep it around.
private RemoteLogOperation logOperation;
@@ -70,9 +77,19 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
// Constants for persisting sorting options
private final static String SORT_ORDER_GROUP = "changelog_sort"; //$NON-NLS-1$
+ private final static String COMMIT_SET_GROUP = "commit_set"; //$NON-NLS-1$
private static final String P_LAST_COMMENTSORT = TeamUIPlugin.ID + ".P_LAST_COMMENT_SORT"; //$NON-NLS-1$
private static final String P_LAST_RESOURCESORT = TeamUIPlugin.ID + ".P_LAST_RESOURCE_SORT"; //$NON-NLS-1$
+ public static final AndSyncInfoFilter OUTGOING_FILE_FILTER = new AndSyncInfoFilter(new FastSyncInfoFilter[] {
+ new FastSyncInfoFilter() {
+ public boolean select(SyncInfo info) {
+ return info.getLocal().getType() == IResource.FILE;
+ }
+ },
+ new SyncInfoDirectionFilter(new int[] { SyncInfo.OUTGOING, SyncInfo.CONFLICTING })
+ });
+
/* *****************************************************************************
* Action that allows changing the model providers sort order.
*/
@@ -129,6 +146,141 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
return sorter.getResourceCriteria();
}
}
+
+ private class CreateCommitSetAction extends SynchronizeModelAction {
+
+ public CreateCommitSetAction(ISynchronizePageConfiguration configuration) {
+ super("&New Commit Set...", configuration);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.SynchronizeModelAction#getSyncInfoFilter()
+ */
+ protected FastSyncInfoFilter getSyncInfoFilter() {
+ return OUTGOING_FILE_FILTER;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.SynchronizeModelAction#getSubscriberOperation(org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration, org.eclipse.compare.structuremergeviewer.IDiffElement[])
+ */
+ protected SynchronizeModelOperation getSubscriberOperation(ISynchronizePageConfiguration configuration, IDiffElement[] elements) {
+ return new SynchronizeModelOperation(configuration, elements) {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ syncExec(new Runnable() {
+ public void run() {
+ try {
+ IResource[] resources = Utils.getResources(getSelectedDiffElements());
+ CommitSet set = CommitSetManager.getInstance().createCommitSet("New Set", null);
+ CommitSetDialog dialog = new CommitSetDialog(getConfiguration().getSite().getShell(), set, resources,
+ "New Commit Set", "Enter the name and comment for the new commit set");
+ dialog.open();
+ if (dialog.getReturnCode() != InputDialog.OK) return;
+ set.addFiles(resources);
+ CommitSetManager.getInstance().add(set);
+ } catch (CVSException e) {
+ CVSUIPlugin.openError(getConfiguration().getSite().getShell(),
+ "Could not create set", "A problem occurred while trying to create the commit set", e);
+ }
+ }
+ });
+ }
+ };
+ }
+ }
+
+ private abstract class CommitSetAction extends BaseSelectionListenerAction {
+ private final ISynchronizePageConfiguration configuration;
+
+ public CommitSetAction(String title, ISynchronizePageConfiguration configuration) {
+ super(title);
+ this.configuration = configuration;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.actions.BaseSelectionListenerAction#updateSelection(org.eclipse.jface.viewers.IStructuredSelection)
+ */
+ protected boolean updateSelection(IStructuredSelection selection) {
+ return getSelectedSet() != null;
+ }
+
+ protected CommitSet getSelectedSet() {
+ IStructuredSelection selection = getStructuredSelection();
+ if (selection.size() == 1) {
+ Object first = selection.getFirstElement();
+ if (first instanceof CommitSetDiffNode) {
+ return ((CommitSetDiffNode)first).getSet();
+ }
+ }
+ return null;
+ }
+ }
+
+ private class EditCommitSetAction extends CommitSetAction {
+
+ public EditCommitSetAction(ISynchronizePageConfiguration configuration) {
+ super("Ed&it Comment...", configuration);
+ }
+
+ public void run() {
+ CommitSet set = getSelectedSet();
+ if (set == null) return;
+ CommitSetDialog dialog = new CommitSetDialog(getConfiguration().getSite().getShell(), set, set.getFiles(),
+ "Edit Commit Set Comment", "Edit the name and comment for the new commit set");
+ dialog.open();
+ if (dialog.getReturnCode() != InputDialog.OK) return;
+ // Nothing to do here as the set was updated by the dialog
+ }
+
+ }
+
+ private class MakeDefaultCommitSetAction extends CommitSetAction {
+
+ public MakeDefaultCommitSetAction(ISynchronizePageConfiguration configuration) {
+ super("Make De&fault", configuration);
+ }
+
+ public void run() {
+ CommitSet set = getSelectedSet();
+ if (set == null) return;
+ CommitSetManager.getInstance().makeDefault(set);
+ }
+
+ }
+
+ private class AddToCommitSetAction extends SynchronizeModelAction {
+
+ private final CommitSet set;
+
+ public AddToCommitSetAction(ISynchronizePageConfiguration configuration, CommitSet set, ISelection selection) {
+ super(set.getTitle(), configuration);
+ this.set = set;
+ selectionChanged(selection);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.SynchronizeModelAction#getSyncInfoFilter()
+ */
+ protected FastSyncInfoFilter getSyncInfoFilter() {
+ return OUTGOING_FILE_FILTER;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.SynchronizeModelAction#getSubscriberOperation(org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration, org.eclipse.compare.structuremergeviewer.IDiffElement[])
+ */
+ protected SynchronizeModelOperation getSubscriberOperation(ISynchronizePageConfiguration configuration, IDiffElement[] elements) {
+ return new SynchronizeModelOperation(configuration, elements) {
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+ try {
+ set.addFiles(Utils.getResources(getSelectedDiffElements()));
+ } catch (CVSException e) {
+ CVSUIPlugin.openError(getConfiguration().getSite().getShell(),
+ "Could not add files", "A problem occurred while rying to add the files to the commit set", e);
+ }
+ }
+ };
+ }
+ }
/* *****************************************************************************
* Action group for this layout. It is added and removed for this layout only.
@@ -136,10 +288,26 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
public class ChangeLogActionGroup extends SynchronizePageActionGroup {
private MenuManager sortByComment;
private MenuManager sortByResource;
+ private CreateCommitSetAction createCommitSet;
+ private MenuManager addToCommitSet;
+ private EditCommitSetAction editCommitSet;
+ private MakeDefaultCommitSetAction makeDefault;
public void initialize(ISynchronizePageConfiguration configuration) {
super.initialize(configuration);
sortByComment = new MenuManager(Policy.bind("ChangeLogModelProvider.0")); //$NON-NLS-1$
sortByResource = new MenuManager(Policy.bind("ChangeLogModelProvider.6")); //$NON-NLS-1$
+ addToCommitSet = new MenuManager("Add &To");
+ addToCommitSet.setRemoveAllWhenShown(true);
+ addToCommitSet.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ addCommitSets(manager);
+ }
+ });
+ createCommitSet = new CreateCommitSetAction(configuration);
+ addToCommitSet.add(createCommitSet);
+ addToCommitSet.add(new Separator());
+ editCommitSet = new EditCommitSetAction(configuration);
+ makeDefault = new MakeDefaultCommitSetAction(configuration);
appendToGroup(
ISynchronizePageConfiguration.P_CONTEXT_MENU,
@@ -149,6 +317,18 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
ISynchronizePageConfiguration.P_CONTEXT_MENU,
SORT_ORDER_GROUP,
sortByResource);
+ appendToGroup(
+ ISynchronizePageConfiguration.P_CONTEXT_MENU,
+ COMMIT_SET_GROUP,
+ addToCommitSet);
+ appendToGroup(
+ ISynchronizePageConfiguration.P_CONTEXT_MENU,
+ COMMIT_SET_GROUP,
+ editCommitSet);
+ appendToGroup(
+ ISynchronizePageConfiguration.P_CONTEXT_MENU,
+ COMMIT_SET_GROUP,
+ makeDefault);
ChangeLogModelSorter sorter = (ChangeLogModelSorter)getViewerSorter();
@@ -161,16 +341,38 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
sortByResource.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.9"), ChangeLogModelSorter.PARENT_NAME, ToggleSortOrderAction.RESOURCE_NAME, sorter.getResourceCriteria())); //$NON-NLS-1$
}
- /* (non-Javadoc)
+ protected void addCommitSets(IMenuManager manager) {
+ CommitSet[] sets = CommitSetManager.getInstance().getSets();
+ ISelection selection = getContext().getSelection();
+ createCommitSet.selectionChanged(selection);
+ addToCommitSet.add(createCommitSet);
+ addToCommitSet.add(new Separator());
+ for (int i = 0; i < sets.length; i++) {
+ CommitSet set = sets[i];
+ AddToCommitSetAction action = new AddToCommitSetAction(getConfiguration(), set, selection);
+ manager.add(action);
+ }
+ }
+
+ /* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.SynchronizePageActionGroup#dispose()
*/
public void dispose() {
sortByComment.dispose();
sortByResource.dispose();
+ addToCommitSet.dispose();
sortByComment.removeAll();
sortByResource.removeAll();
+ addToCommitSet.removeAll();
super.dispose();
}
+
+
+ public void updateActionBars() {
+ editCommitSet.selectionChanged((IStructuredSelection)getContext().getSelection());
+ makeDefault.selectionChanged((IStructuredSelection)getContext().getSelection());
+ super.updateActionBars();
+ }
}
/* *****************************************************************************
@@ -265,8 +467,12 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
this.tag1 = tag1;
this.tag2 = tag2;
configuration.addMenuGroup(ISynchronizePageConfiguration.P_CONTEXT_MENU, SORT_ORDER_GROUP);
+ configuration.addMenuGroup(ISynchronizePageConfiguration.P_CONTEXT_MENU, COMMIT_SET_GROUP);
this.sortGroup = new ChangeLogActionGroup();
configuration.addActionContribution(sortGroup);
+ if (configuration.getComparisonType() == ISynchronizePageConfiguration.THREE_WAY) {
+ CommitSetManager.getInstance().addListener(this);
+ }
}
/* (non-Javadoc)
@@ -329,28 +535,21 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
monitor.beginTask(null, 100);
// Decide which nodes we have to fetch log histories
SyncInfo[] infos = set.getSyncInfos();
- ArrayList commentNodes = new ArrayList();
- ArrayList resourceNodes = new ArrayList();
+ ArrayList remoteChanges = new ArrayList();
+ ArrayList localChanges = new ArrayList();
for (int i = 0; i < infos.length; i++) {
SyncInfo info = infos[i];
- if(isInterestingChange(info)) {
- commentNodes.add(info);
- } else {
- resourceNodes.add(info);
+ boolean handled = false;
+ if(isRemoteChange(info)) {
+ remoteChanges.add(info);
+ handled = true;
+ }
+ if (isLocalChange(info) || !handled) {
+ localChanges.add(info);
}
}
- // Show elements that don't need their log histories retreived
- for (Iterator it = resourceNodes.iterator(); it.hasNext();) {
- SyncInfo info = (SyncInfo) it.next();
- addNewElementFor(info, null, null);
- }
- if(! resourceNodes.isEmpty())
- refreshViewer();
-
- // Fetch log histories then add elements
- SyncInfo[] commentInfos = (SyncInfo[]) commentNodes.toArray(new SyncInfo[commentNodes.size()]);
- RemoteLogOperation logs = getSyncInfoComment(commentInfos, Policy.subMonitorFor(monitor, 80));
- addLogEntries(commentInfos, logs, Policy.subMonitorFor(monitor, 20));
+ handleLocalChanges((SyncInfo[]) localChanges.toArray(new SyncInfo[localChanges.size()]));
+ handleRemoteChanges((SyncInfo[]) remoteChanges.toArray(new SyncInfo[remoteChanges.size()]), monitor);
} catch (CVSException e) {
Utils.handle(e);
} catch (InterruptedException e) {
@@ -360,7 +559,31 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
}
/**
- * Add the followinf sync info elements to the viewer. It is assumed that these elements have associated
+ * Fetch the log histories for the remote changes and use this information
+ * to add each resource to an appropriate commit set.
+ */
+ private void handleRemoteChanges(SyncInfo[] infos, IProgressMonitor monitor) throws CVSException, InterruptedException {
+ RemoteLogOperation logs = getSyncInfoComment(infos, Policy.subMonitorFor(monitor, 80));
+ addLogEntries(infos, logs, Policy.subMonitorFor(monitor, 20));
+ }
+
+ /**
+ * Use the commit set manager to determine the commit set that each local
+ * change belongs to.
+ */
+ private void handleLocalChanges(SyncInfo[] infos) {
+ if (infos.length != 0) {
+ // Show elements that don't need their log histories retreived
+ for (int i = 0; i < infos.length; i++) {
+ SyncInfo info = infos[i];
+ addLocalChange(info);
+ }
+ refreshViewer();
+ }
+ }
+
+ /**
+ * Add the following sync info elements to the viewer. It is assumed that these elements have associated
* log entries cached in the log operation.
*/
private void addLogEntries(SyncInfo[] commentInfos, RemoteLogOperation logs, IProgressMonitor monitor) {
@@ -408,11 +631,11 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
if(logEntries == null || logEntries.length == 0) {
// If for some reason we don't have a log entry, try the latest
// remote.
- addNewElementFor(info, null, null);
+ addRemoteChange(info, null, null);
} else {
for (int i = 0; i < logEntries.length; i++) {
ILogEntry entry = logEntries[i];
- addNewElementFor(info, remoteResource, entry);
+ addRemoteChange(info, remoteResource, entry);
}
}
}
@@ -443,7 +666,7 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
} catch (TeamException e) {
// continue and skip deletion checks
}
- addNewElementFor(info, remoteResource, logEntry);
+ addRemoteChange(info, remoteResource, logEntry);
}
private boolean isDeletedRemotely(SyncInfo info) {
@@ -453,33 +676,51 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
return false;
}
- private void addNewElementFor(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
- ISynchronizeModelElement element;
- // If the element has a comment then group with common comment
- if(remoteResource != null && logEntry != null && isInterestingChange(info)) {
- ChangeLogDiffNode changeRoot = getChangeLogDiffNodeFor(logEntry);
- if (changeRoot == null) {
- changeRoot = new ChangeLogDiffNode(getModelRoot(), logEntry);
- addToViewer(changeRoot);
- }
- if(requiresCustomSyncInfo(info, remoteResource, logEntry)) {
- info = new CVSUpdatableSyncInfo(info.getKind(), info.getLocal(), info.getBase(), (RemoteResource)logEntry.getRemoteFile(), ((CVSSyncInfo)info).getSubscriber());
- try {
- info.init();
- } catch (TeamException e) {
- // this shouldn't happen, we've provided our own calculate kind
- }
- }
- element = new FullPathSyncInfoElement(changeRoot, info);
- } else {
- // For nodes without comments, simply parent with the root. These will be outgoing
- // additions.
- element = new FullPathSyncInfoElement(getModelRoot(), info);
- }
- addToViewer(element);
- }
-
- private boolean requiresCustomSyncInfo(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
+ /*
+ * Add the local change to the appropriate outgoing commit set
+ */
+ private void addLocalChange(SyncInfo info) {
+ CommitSet set = getCommitSetFor(info);
+ if (set == null) {
+ // TODO: What to do about local mods that are not in a set
+ addToViewer(new FullPathSyncInfoElement(getModelRoot(), info));
+ } else {
+ CommitSetDiffNode node = getDiffNodeFor(set);
+ if (node == null) {
+ node = new CommitSetDiffNode(getModelRoot(), set);
+ addToViewer(node);
+ }
+ addToViewer(new FullPathSyncInfoElement(node, info));
+ }
+ }
+
+ /*
+ * Add the remote change to an incoming commit set
+ */
+ private void addRemoteChange(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
+ if(remoteResource != null && logEntry != null && isRemoteChange(info)) {
+ ChangeLogDiffNode changeRoot = getChangeLogDiffNodeFor(logEntry);
+ if (changeRoot == null) {
+ changeRoot = new ChangeLogDiffNode(getModelRoot(), logEntry);
+ addToViewer(changeRoot);
+ }
+ if(requiresCustomSyncInfo(info, remoteResource, logEntry)) {
+ info = new CVSUpdatableSyncInfo(info.getKind(), info.getLocal(), info.getBase(), (RemoteResource)logEntry.getRemoteFile(), ((CVSSyncInfo)info).getSubscriber());
+ try {
+ info.init();
+ } catch (TeamException e) {
+ // this shouldn't happen, we've provided our own calculate kind
+ }
+ }
+ addToViewer(new FullPathSyncInfoElement(changeRoot, info));
+ } else {
+ // The info was not retrieved for the remote change for some reason.
+ // Add the node to the root
+ addToViewer(new FullPathSyncInfoElement(getModelRoot(), info));
+ }
+ }
+
+ private boolean requiresCustomSyncInfo(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
// Only interested in non-deletions
if (logEntry.isDeletion() || !(info instanceof CVSSyncInfo)) return false;
// Only require a custom sync info if the remote of the sync info
@@ -509,9 +750,47 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
}
/*
- * Return if this sync info should be considered as part of a commit set.
+ * Find an existing comment set
+ * TODO: we could do better than a linear lookup?
+ */
+ private CommitSetDiffNode getDiffNodeFor(CommitSet set) {
+ if (set == null) return null;
+ IDiffElement[] elements = getModelRoot().getChildren();
+ for (int i = 0; i < elements.length; i++) {
+ IDiffElement element = elements[i];
+ if(element instanceof CommitSetDiffNode) {
+ CommitSetDiffNode node = (CommitSetDiffNode)element;
+ if(node.getSet() == set) {
+ return node;
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Find an existing comment set
+ * TODO: we could do better than a linear lookup?
+ * TODO: can a file be in multiple sets?
+ */
+ private CommitSet getCommitSetFor(SyncInfo info) {
+ CommitSet[] sets = CommitSetManager.getInstance().getSets();
+ for (int i = 0; i < sets.length; i++) {
+ CommitSet set = sets[i];
+ if (set.contains(info.getLocal())) {
+ return set;
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Return if this sync info should be considered as part of a remote change
+ * meaning that it can be placed inside an incoming commit set (i.e. the
+ * set is determined using the comments from the log entry of the file).
+ *
*/
- private boolean isInterestingChange(SyncInfo info) {
+ private boolean isRemoteChange(SyncInfo info) {
int kind = info.getKind();
if(info.getLocal().getType() != IResource.FILE) return false;
if(info.getComparator().isThreeWay()) {
@@ -519,6 +798,15 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
}
return true;
}
+
+ /*
+ * Return if this sync info is an outgoing change.
+ */
+ private boolean isLocalChange(SyncInfo info) {
+ return (info.getLocal().getType() == IResource.FILE
+ && info.getComparator().isThreeWay()
+ && (info.getKind() & SyncInfo.DIRECTION_MASK) != SyncInfo.INCOMING);
+ }
/**
* How do we tell which revision has the interesting log message? Use the later
@@ -592,6 +880,7 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
fetchLogEntriesJob.cancel();
}
sortGroup.dispose();
+ CommitSetManager.getInstance().removeListener(this);
super.dispose();
}
@@ -645,6 +934,7 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
for (int i = 0; i < infos.length; i++) {
SyncInfo info = infos[i];
IResource local = info.getLocal();
+ // TODO: This will cause the log to be refetched, even if it was a local change
removeFromViewer(local);
}
startUpdateJob(new SyncInfoSet(event.getChangedResources()));
@@ -717,4 +1007,99 @@ public class ChangeLogModelProvider extends SynchronizeModelProvider {
// The super class will do all the interesting work.
super.addToViewer(node);
}
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.subscriber.ICommitSetChangeListener#setAdded(org.eclipse.team.internal.ccvs.ui.subscriber.CommitSet)
+ */
+ public void setAdded(CommitSet set) {
+ refresh(set.getFiles(), true /* we may not be in the UI thread */);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.subscriber.ICommitSetChangeListener#setRemoved(org.eclipse.team.internal.ccvs.ui.subscriber.CommitSet)
+ */
+ public void setRemoved(CommitSet set) {
+ refresh(set.getFiles(), true /* we may not be in the UI thread */);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.subscriber.ICommitSetChangeListener#titleChanged(org.eclipse.team.internal.ccvs.ui.subscriber.CommitSet)
+ */
+ public void titleChanged(CommitSet set) {
+ refreshNode(getDiffNodeFor(set));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.subscriber.ICommitSetChangeListener#filesChanged(org.eclipse.team.internal.ccvs.ui.subscriber.CommitSet, org.eclipse.core.resources.IFile[])
+ */
+ public void filesChanged(CommitSet set, IFile[] files) {
+ refresh(files, true /* we may not be in the UI thread */);
+ }
+
+ private void refresh(final IResource[] resources, boolean performSyncExec) {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ List infos = new ArrayList();
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ SyncInfo info = getSyncInfo(resource);
+ if (info != null) {
+ infos.add(info);
+// TODO: This will cause the log to be refetched, even if it was a local change
+ removeFromViewer(resource);
+ }
+ }
+ startUpdateJob(new SyncInfoSet((SyncInfo[]) infos.toArray(new SyncInfo[infos.size()])));
+ }
+ };
+ if (performSyncExec) {
+ syncExec(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ protected SyncInfo getSyncInfo(IResource resource) {
+ Object o = getMapping(resource);
+ if (o instanceof IAdaptable) {
+ return (SyncInfo)((IAdaptable)o).getAdapter(SyncInfo.class);
+ }
+ return null;
+ }
+
+ private void syncExec(final Runnable runnable) {
+ final Control ctrl = getViewer().getControl();
+ if (ctrl != null && !ctrl.isDisposed()) {
+ ctrl.getDisplay().syncExec(new Runnable() {
+ public void run() {
+ if (!ctrl.isDisposed()) {
+ runnable.run();
+ }
+ }
+ });
+ }
+ }
+
+ private void refreshNode(final DiffNode node) {
+ if (node != null) {
+ syncExec(new Runnable() {
+ public void run() {
+ getViewer().refresh(node);
+ }
+ });
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(CommitSetManager.DEFAULT_SET)) {
+ CommitSet oldValue = (CommitSet)event.getOldValue();
+ refreshNode(getDiffNodeFor(oldValue));
+ CommitSet newValue = (CommitSet)event.getNewValue();
+ refreshNode(getDiffNodeFor(newValue));
+ }
+
+ }
}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelSorter.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelSorter.java
index 3b270462b..2f64b5aaf 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelSorter.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ChangeLogModelSorter.java
@@ -43,10 +43,13 @@ public class ChangeLogModelSorter extends ViewerSorter {
}
protected int classComparison(Object element) {
- if (element instanceof ChangeLogDiffNode) {
+ if (element instanceof CommitSetDiffNode) {
return 0;
}
- return 1;
+ if (element instanceof ChangeLogDiffNode) {
+ return 1;
+ }
+ return 2;
}
protected int compareClass(Object element1, Object element2) {
@@ -64,6 +67,12 @@ public class ChangeLogModelSorter extends ViewerSorter {
//have to deal with non-resources in navigator
//if one or both objects are not resources, returned a comparison
//based on class.
+ if (o1 instanceof CommitSetDiffNode && o2 instanceof CommitSetDiffNode) {
+ CommitSet s1 = ((CommitSetDiffNode) o1).getSet();
+ CommitSet s2 = ((CommitSetDiffNode) o2).getSet();
+ return compareNames(s1.getTitle(), s2.getTitle());
+ }
+
if (o1 instanceof ChangeLogDiffNode && o2 instanceof ChangeLogDiffNode) {
ILogEntry r1 = ((ChangeLogDiffNode) o1).getComment();
ILogEntry r2 = ((ChangeLogDiffNode) o2).getComment();
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSet.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSet.java
new file mode 100644
index 000000000..02e5dbee8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSet.java
@@ -0,0 +1,297 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.subscriber;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.client.Command;
+import org.eclipse.team.internal.ccvs.core.client.Commit;
+import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.operations.CommitOperation;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * A commit set is used to associate a comment with a set of outgoing
+ * file modifications. If the comment of the set is <code>null</code>,
+ * the title of the commit set will be used as the comment when committing
+ */
+public class CommitSet {
+
+ private static final String CTX_REVISION = "revision"; //$NON-NLS-1$
+ private static final String CTX_PATH = "path"; //$NON-NLS-1$
+ private static final String CTX_FILES = "files"; //$NON-NLS-1$
+ private static final String CTX_TITLE = "title"; //$NON-NLS-1$
+ private static final String CTX_COMMENT = "comment"; //$NON-NLS-1$
+
+ private String title;
+ private String comment;
+ private Map dirtyFiles; // Maps IFile->String(revision)
+
+
+ /**
+ * Restore a commit set from the given memento
+ * @param memento the memento to which the set was saved
+ * @return the restored set
+ */
+ public static CommitSet from(IMemento memento) {
+ CommitSet set = new CommitSet();
+ set.init(memento);
+ return set;
+ }
+
+ private CommitSet() {
+ dirtyFiles = new HashMap();
+ }
+
+ /**
+ * Create a commit set with the given title.
+ * @param title the title for the commit set
+ */
+ /* package */ CommitSet(String title) {
+ this();
+ this.title = title;
+ }
+
+ /**
+ * Get the title of the commit set. The title is used
+ * as the comment when the set is committed if no comment
+ * has been explicitly set using <code>setComment</code>.
+ * @return the title of the set
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Set the title of the set. The title is used
+ * as the comment when the set is committed if no comment
+ * has been explicitly set using <code>setComment</code>.
+ * @param title the title of the set
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ CommitSetManager.getInstance().titleChanged(this);
+ }
+
+ /**
+ * Get the comment of this commit set. If the comment
+ * as never been set, the title is returned as the comment
+ * @return the comment to be used when the set is committed
+ */
+ public String getComment() {
+ if (comment == null) {
+ return getTitle();
+ }
+ return comment;
+ }
+
+ /**
+ * Set the comment to be used when the commit set is committed.
+ * If <code>null</code> is passed, the title of the set
+ * will be used as the comment.
+ * @param comment the comment for the set or <code>null</code>
+ * if the title should be the comment
+ */
+ public void setComment(String comment) {
+ if (comment != null && comment.equals(getTitle())) {
+ this.comment = null;
+ } else {
+ this.comment = comment;
+ }
+ }
+
+ /**
+ * Add the dirty files in the given array to the commit set.
+ * The list of files that were added is returned.
+ * @param files the files to be added to the set
+ * @return the files that were added because they were dirty
+ * @throws CVSException if the dirty state or revision of one of the files could not be determined
+ */
+ public IFile[] addFiles(IResource[] files) throws CVSException {
+ List addedFiles = new ArrayList();
+ for (int i = 0; i < files.length; i++) {
+ IResource file = files[i];
+ if (file.getType() == IResource.FILE && addFile((IFile)file)) {
+ addedFiles.add(file);
+ }
+ }
+ IFile[] fileArray = (IFile[]) addedFiles.toArray(new IFile[addedFiles.size()]);
+ if (fileArray.length > 0) {
+ CommitSetManager.getInstance().filesAdded(this, fileArray);
+ }
+ return fileArray;
+ }
+
+ /**
+ * Remove the given files from this set.
+ * @param files the files to be removed
+ */
+ public void removeFiles(IFile[] files) {
+ List removed = new ArrayList();
+ for (int i = 0; i < files.length; i++) {
+ IFile file = files[i];
+ if (dirtyFiles.remove(file) != null) {
+ removed.add(file);
+ }
+ }
+ if (!removed.isEmpty()) {
+ CommitSetManager.getInstance().filesChanged(this, (IFile[]) removed.toArray(new IFile[removed.size()]));
+ }
+ }
+
+ private boolean addFile(IFile file) throws CVSException {
+ ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(file);
+ if (!cvsFile.isModified(null)) {
+ return false;
+ }
+ byte[] syncBytes = cvsFile.getSyncBytes();
+ String revision;
+ if (syncBytes == null) {
+ revision = ResourceSyncInfo.ADDED_REVISION;
+ } else {
+ revision = ResourceSyncInfo.getRevision(syncBytes);
+ }
+ addFile(file, revision);
+ return true;
+ }
+
+ private boolean isModified(IResource resource) {
+ try {
+ ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
+ return cvsResource.isModified(null);
+ } catch (CVSException e) {
+ CVSUIPlugin.log(e);
+ return true;
+ }
+ }
+
+ private void addFile(IFile file, String revision) {
+ dirtyFiles.put(file, revision);
+ }
+
+ private String getRevision(IFile file) {
+ return (String)dirtyFiles.get(file);
+ }
+
+ public void save(IMemento memento) {
+ memento.putString(CTX_TITLE, getTitle());
+ if (comment != null) {
+ memento.putString(CTX_COMMENT, comment);
+ }
+ for (Iterator iter = dirtyFiles.keySet().iterator(); iter.hasNext();) {
+ IFile file = (IFile) iter.next();
+ IMemento child = memento.createChild(CTX_FILES);
+ child.putString(CTX_PATH, file.getFullPath().toString());
+ child.putString(CTX_REVISION, getRevision(file));
+ }
+ }
+
+ public void init(IMemento memento) {
+ title = memento.getString(CTX_TITLE);
+ comment = memento.getString(CTX_COMMENT);
+ IMemento[] children = memento.getChildren(CTX_FILES);
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ for (int i = 0; i < children.length; i++) {
+ IMemento child = children[i];
+ String path = child.getString(CTX_PATH);
+ String revision = child.getString(CTX_REVISION);
+ IFile file = root.getFile(new Path(path));
+ addFile(file, revision);
+ }
+ }
+
+ /**
+ * The given project is no longer under CVS control.
+ * Remoev any files that may have been included in the
+ * commit set.
+ */
+ /* package*/ void projectRemoved(IProject project) {
+ List filesToRemove = new ArrayList();
+ for (Iterator iter = dirtyFiles.keySet().iterator(); iter.hasNext();) {
+ IFile file = (IFile) iter.next();
+ if (file.getProject().equals(project)) {
+ filesToRemove.add(file);
+ }
+ }
+ removeFiles((IFile[]) filesToRemove.toArray(new IFile[filesToRemove.size()]));
+ }
+
+ /**
+ * The sync state of the given resources has changed. If any of them are in this
+ * set, adjust the state accordingly.
+ * @param changedResources the resources whose sync state has changed
+ */
+ /* package*/ void resourceSyncInfoChanged(IResource[] changedResources) {
+ List filesToRemove = new ArrayList();
+ for (int i = 0; i < changedResources.length; i++) {
+ IResource resource = changedResources[i];
+ if (dirtyFiles.containsKey(resource) && !isModified(resource)) {
+ filesToRemove.add(resource);
+ }
+ }
+ removeFiles((IFile[]) filesToRemove.toArray(new IFile[filesToRemove.size()]));
+ }
+
+ /**
+ * Commit the files in this commit set to the repository.
+ * @param monitor a progress monitor
+ * @throws InterruptedException
+ * @throws InvocationTargetException
+ */
+ public void commit(IWorkbenchPart part, IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ IFile[] files = getFiles();
+ new CommitOperation(part, files, new Command.LocalOption[] { Commit.makeArgumentOption(Command.MESSAGE_OPTION, getComment()) })
+ .run(monitor);
+ // TODO: Handle set archival
+ }
+
+ /**
+ * Return the dirty files contained in this set.
+ * @return the dirty files contained in this set
+ */
+ public IFile[] getFiles() {
+ return (IFile[]) dirtyFiles.keySet().toArray(new IFile[dirtyFiles.size()]);
+ }
+
+ /**
+ * Return whether the set contains any files.
+ * @return whether the set contains any files
+ */
+ public boolean isEmpty() {
+ return dirtyFiles.isEmpty();
+ }
+
+ /**
+ * Return true if the given file is included in this set.
+ * @param local a ocal file
+ * @return true if the given file is included in this set
+ */
+ public boolean contains(IResource local) {
+ return dirtyFiles.containsKey(local);
+ }
+
+ /**
+ * Return whether the set has a comment that differs from the title.
+ * @return whether the set has a comment that differs from the title
+ */
+ public boolean hasComment() {
+ return comment != null;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetDialog.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetDialog.java
new file mode 100644
index 000000000..e4570a771
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetDialog.java
@@ -0,0 +1,220 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.subscriber;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.team.internal.ccvs.ui.*;
+import org.eclipse.team.internal.ccvs.ui.CommitCommentArea;
+import org.eclipse.ui.help.WorkbenchHelp;
+
+/**
+ * Dialog for creating and editing commit set
+ * title and comment
+ */
+public class CommitSetDialog extends Dialog {
+
+ private final CommitSet set;
+ private CommitCommentArea commitCommentArea;
+ private Text nameText;
+ private Button useTitleButton;
+ private Button enterCommentButton;
+ private final String title;
+ private final String description;
+ private String comment;
+
+ public CommitSetDialog(Shell parentShell, CommitSet set, IResource[] files, String title, String description) {
+ super(parentShell);
+ this.set = set;
+ this.title = title;
+ this.description = description;
+ if (files == null) {
+ files = set.getFiles();
+ }
+ int shellStyle = getShellStyle();
+ setShellStyle(shellStyle | SWT.RESIZE | SWT.MAX);
+ commitCommentArea = new CommitCommentArea(this, null);
+ // Get a project from which the commit template can be obtained
+ if (files.length > 0)
+ commitCommentArea.setProject(files[0].getProject());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ protected Control createDialogArea(Composite parent) {
+ getShell().setText(title);
+ Composite composite = (Composite)super.createDialogArea(parent);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ createWrappingLabel(composite, description);
+ createNameArea(composite);
+
+ if (hasCommitTemplate()) {
+ if (set.hasComment()) {
+ // Only set the comment if the set has a custom comment.
+ // Otherwise, the template should be used
+ commitCommentArea.setProposedComment(set.getComment());
+ }
+ } else {
+ comment = set.getComment();
+ commitCommentArea.setProposedComment(comment);
+ createOptionsArea(composite);
+ }
+
+ commitCommentArea.createArea(composite);
+ commitCommentArea.addPropertyChangeListener(new IPropertyChangeListener() {
+
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty() == CommitCommentArea.OK_REQUESTED) {
+ okPressed();
+ } else if (event.getProperty() == CommitCommentArea.COMMENT_MODIFIED) {
+ comment = (String)event.getNewValue();
+ updateEnablements();
+ }
+ }
+ });
+
+ initializeValues();
+ updateEnablements();
+
+ // set F1 help
+ WorkbenchHelp.setHelp(composite, IHelpContextIds.COMMIT_SET_DIALOG);
+ Dialog.applyDialogFont(parent);
+ return composite;
+ }
+
+ private void createNameArea(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+ layout.marginWidth = 0;
+ layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+ layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
+ layout.numColumns = 2;
+ composite.setLayout(layout);
+ composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ composite.setFont(parent.getFont());
+
+ Label label = new Label(composite, SWT.NONE);
+ label.setText("&Name:");
+ label.setLayoutData(new GridData(GridData.BEGINNING));
+
+ nameText = new Text(composite, SWT.BORDER);
+ nameText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ nameText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ updateEnablements();
+ }
+ });
+ }
+
+ private void initializeValues() {
+ String initialText = set.getTitle();
+ if (initialText == null) initialText = "";
+ nameText.setText(initialText);
+
+ if (useTitleButton != null) {
+ useTitleButton.setEnabled(!set.hasComment());
+ enterCommentButton.setEnabled(set.hasComment());
+ }
+ }
+
+ private void createOptionsArea(Composite composite) {
+ useTitleButton = createRadioButton(composite, "Use the &title as the commit comment");
+ enterCommentButton = createRadioButton(composite, "Enter a commit &comment");
+ SelectionAdapter listener = new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ updateEnablements();
+ }
+ };
+ useTitleButton.addSelectionListener(listener);
+ enterCommentButton.addSelectionListener(listener);
+
+ }
+
+ private Button createRadioButton(Composite parent, String label) {
+ Button button = new Button(parent, SWT.RADIO);
+ button.setText(label);
+ button.setLayoutData(new GridData());
+ return button;
+ }
+
+ private void updateEnablements() {
+ commitCommentArea.setEnabled(isUseCustomComment());
+ String name = nameText.getText();
+ if (name == null && name.length() == 0) {
+ setPageComplete(false);
+ return;
+ }
+ if (isUseCustomComment()) {
+ if (comment == null || comment.length() == 0) {
+ setPageComplete(false);
+ return;
+ }
+ }
+ setPageComplete(true);
+ }
+
+ final protected void setPageComplete(boolean complete) {
+ Button okButton = getOKButton();
+ if(okButton != null ) {
+ okButton.setEnabled(complete);
+ }
+ }
+
+ private boolean hasCommitTemplate() {
+ return commitCommentArea.hasCommitTemplate();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#okPressed()
+ */
+ protected void okPressed() {
+ set.setTitle(nameText.getText());
+ if (isUseCustomComment()) {
+ // Call getComment so the comment gets saved
+ set.setComment(commitCommentArea.getComment());
+ } else {
+ set.setComment(null);
+ }
+ super.okPressed();
+ }
+
+ private boolean isUseCustomComment() {
+ return enterCommentButton == null || enterCommentButton.getSelection();
+ }
+
+ protected Label createWrappingLabel(Composite parent, String text) {
+ Label label = new Label(parent, SWT.LEFT | SWT.WRAP);
+ label.setText(text);
+ GridData data = new GridData();
+ data.horizontalSpan = 1;
+ data.horizontalAlignment = GridData.FILL;
+ data.horizontalIndent = 0;
+ data.grabExcessHorizontalSpace = true;
+ data.widthHint = 200;
+ label.setLayoutData(data);
+ return label;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetDiffNode.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetDiffNode.java
new file mode 100644
index 000000000..70a9c5d03
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetDiffNode.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.subscriber;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants;
+import org.eclipse.team.internal.ui.synchronize.SynchronizeModelElement;
+import org.eclipse.team.ui.synchronize.ISynchronizeModelElement;
+
+/**
+ * This diff node represents an outgoing commit set when it appears in the
+ * synchronize view.
+ */
+public class CommitSetDiffNode extends SynchronizeModelElement {
+
+ private final CommitSet set;
+
+ public CommitSetDiffNode(ISynchronizeModelElement parent, CommitSet set) {
+ super(parent);
+ this.set = set;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.ISynchronizeModelElement#getResource()
+ */
+ public IResource getResource() {
+ return null;
+ }
+
+ public CommitSet getSet() {
+ return set;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)
+ */
+ public ImageDescriptor getImageDescriptor(Object object) {
+ return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_CHANGELOG); // TODO: Custom image needed
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.compare.structuremergeviewer.DiffNode#getName()
+ */
+ public String getName() {
+ String name = set.getTitle();
+ if (CommitSetManager.getInstance().isDefault(set)) {
+ name = name + " (default)";
+ }
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.SyncInfoModelElement#toString()
+ */
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetManager.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetManager.java
new file mode 100644
index 000000000..ed54b1e5a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/CommitSetManager.java
@@ -0,0 +1,456 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.subscriber;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.util.ListenerList;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.team.core.synchronize.SyncInfoSet;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+import org.eclipse.team.internal.ccvs.core.util.ResourceStateChangeListeners;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ui.TeamUIPlugin;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.XMLMemento;
+
+/**
+ * Thsi class keeps the active commit sets up-to-date.
+ */
+public class CommitSetManager extends Object implements IResourceChangeListener, IResourceStateChangeListener {
+
+ private static final String FILENAME = "commitSets.xml"; //$NON-NLS-1$
+ private static final String CTX_COMMIT_SETS = "commitSets"; //$NON-NLS-1$
+ private static final String CTX_COMMIT_SET = "commitSet"; //$NON-NLS-1$
+ private static final String CTX_DEFAULT_SET = "defaultSet"; //$NON-NLS-1$
+
+ private List activeSets;
+ private static CommitSetManager instance;
+ private static ListenerList listeners = new ListenerList();
+ private CommitSet defaultSet;
+
+ /*
+ * Property change event for the default set
+ */
+ public static final String DEFAULT_SET = "DefaultSet"; //$NON-NLS-1$
+
+ public synchronized static CommitSetManager getInstance() {
+ if (instance == null) {
+ instance = new CommitSetManager();
+ }
+ return instance;
+ }
+
+ private CommitSetManager() {
+ try {
+ load();
+ } catch (CoreException e) {
+ // The saved commit sets could not be restored
+ CVSUIPlugin.log(e);
+ }
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
+ ResourceStateChangeListeners.getListener().addResourceStateChangeListener(this);
+ }
+
+ public void shutdown() {
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ ResourceStateChangeListeners.getListener().removeResourceStateChangeListener(this);
+ save();
+ }
+
+ private void load() throws CoreException {
+ activeSets = new ArrayList();
+ File file = getStateFile();
+ Reader reader;
+ try {
+ reader = new BufferedReader(new FileReader(file));
+ } catch (FileNotFoundException e) {
+ return;
+ }
+ IMemento memento = XMLMemento.createReadRoot(reader);
+ String defaultSetTitle = memento.getString(CTX_DEFAULT_SET);
+ IMemento[] sets = memento.getChildren(CTX_COMMIT_SET);
+ for (int i = 0; i < sets.length; i++) {
+ IMemento child = sets[i];
+ CommitSet set = CommitSet.from(child);
+ if (defaultSet == null && defaultSetTitle != null && set.getTitle().equals(defaultSetTitle)) {
+ defaultSet = set;
+ }
+ activeSets.add(set);
+ }
+ }
+
+ private void save() {
+ XMLMemento xmlMemento = XMLMemento.createWriteRoot(CTX_COMMIT_SETS);
+ for (Iterator it = activeSets.iterator(); it.hasNext(); ) {
+ CommitSet set = (CommitSet) it.next();
+ if (!set.isEmpty()) {
+ IMemento child = xmlMemento.createChild(CTX_COMMIT_SET);
+ set.save(child);
+ }
+ }
+ if (defaultSet != null) {
+ xmlMemento.putString(CTX_DEFAULT_SET, defaultSet.getTitle());
+ }
+ try {
+ Writer writer = new BufferedWriter(new FileWriter(getStateFile()));
+ try {
+ xmlMemento.save(writer);
+ } finally {
+ writer.close();
+ }
+ } catch (IOException e) {
+ TeamUIPlugin.log(new Status(IStatus.ERROR, TeamUIPlugin.ID, 1, "An error occurred saving the commit sets to disk.", e)); //$NON-NLS-1$
+ }
+ }
+
+ private File getStateFile() {
+ IPath pluginStateLocation = CVSUIPlugin.getPlugin().getStateLocation();
+ return pluginStateLocation.append(FILENAME).toFile(); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
+ */
+ public void resourceChanged(IResourceChangeEvent event) {
+ // File modifications should be handled by the resourceModified method.
+ // Therefore, we are only concerned with project deletions and close
+ processDelta(event.getDelta());
+
+ }
+
+ private void processDelta(IResourceDelta delta) {
+ IResource resource = delta.getResource();
+ int kind = delta.getKind();
+
+ if (resource.getType() == IResource.ROOT) {
+ IResourceDelta[] affectedChildren = delta.getAffectedChildren(IResourceDelta.CHANGED | IResourceDelta.REMOVED | IResourceDelta.ADDED);
+ for (int i = 0; i < affectedChildren.length; i++) {
+ processDelta(affectedChildren[i]);
+ }
+ return;
+ }
+
+ if (resource.getType() == IResource.PROJECT) {
+ // Handle a deleted project
+ if (((kind & IResourceDelta.REMOVED) != 0)) {
+ projectDeconfigured((IProject)resource);
+ return;
+ }
+ // Handle a closed project
+ if ((delta.getFlags() & IResourceDelta.OPEN) != 0 && !((IProject) resource).isOpen()) {
+ projectDeconfigured((IProject)resource);
+ return;
+ }
+ }
+ }
+
+ private Object[] getListeners() {
+ return listeners.getListeners();
+ }
+
+ /**
+ * The title of the given set has changed. Notify any listeners.
+ */
+ /* package */ void titleChanged(final CommitSet set) {
+ if (activeSets.contains(set)) {
+ Object[] listeners = getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ final ICommitSetChangeListener listener = (ICommitSetChangeListener)listeners[i];
+ Platform.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ // Exceptions are logged by the platform
+ }
+ public void run() throws Exception {
+ listener.titleChanged(set);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * The state of files in the set have changed.
+ */
+ /* package */void filesAdded(final CommitSet set, final IFile[] files) {
+ filesChanged(set, files);
+ // Remove the added files from any other set that contains them
+ for (Iterator iter = activeSets.iterator(); iter.hasNext();) {
+ CommitSet otherSet = (CommitSet) iter.next();
+ if (otherSet != set) {
+ otherSet.removeFiles(files);
+ }
+ }
+ }
+
+ /**
+ * The state of files in the set have changed.
+ */
+ /* package */void filesChanged(final CommitSet set, final IFile[] files) {
+ if (activeSets.contains(set)) {
+ Object[] listeners = getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ final ICommitSetChangeListener listener = (ICommitSetChangeListener) listeners[i];
+ Platform.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ // Exceptions are logged by the platform
+ }
+ public void run() throws Exception {
+ listener.filesChanged(set, files);
+ }
+ });
+ }
+ }
+ }
+
+ private void firePropertyChange(String property, CommitSet oldDefault, CommitSet newDefault) {
+ final PropertyChangeEvent event = new PropertyChangeEvent(this, property, oldDefault, newDefault);
+ Object[] listeners = getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ final ICommitSetChangeListener listener = (ICommitSetChangeListener) listeners[i];
+ Platform.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ // Exceptions are logged by the platform
+ }
+ public void run() throws Exception {
+ listener.propertyChange(event);
+ }
+ });
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#resourceSyncInfoChanged(org.eclipse.core.resources.IResource[])
+ */
+ public void resourceSyncInfoChanged(IResource[] changedResources) {
+ for (Iterator iter = activeSets.iterator(); iter.hasNext();) {
+ CommitSet set = (CommitSet) iter.next();
+ if (!set.isEmpty()) {
+ // Don't forward the event if the set is empty
+ set.resourceSyncInfoChanged(changedResources);
+ if (set.isEmpty()) {
+ remove(set);
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#externalSyncInfoChange(org.eclipse.core.resources.IResource[])
+ */
+ public void externalSyncInfoChange(IResource[] changedResources) {
+ resourceSyncInfoChanged(changedResources);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#resourceModified(org.eclipse.core.resources.IResource[])
+ */
+ public void resourceModified(IResource[] changedResources) {
+ // First, remove any clean files from active commit sets
+ resourceSyncInfoChanged(changedResources);
+ // Next, add any dirty files that are not in an active set to the default set
+ if (defaultSet != null) {
+ considerForDefaultSet(changedResources);
+ }
+ }
+
+ private void considerForDefaultSet(IResource[] changedResources) {
+ List filesToAdd = new ArrayList();
+ for (int i = 0; i < changedResources.length; i++) {
+ IResource resource = changedResources[i];
+ if (isDirtyFile(resource) && !isInActiveSet(resource)) {
+ filesToAdd.add(resource);
+ }
+ }
+ if (!filesToAdd.isEmpty()) {
+ try {
+ defaultSet.addFiles((IFile[]) filesToAdd.toArray(new IFile[filesToAdd.size()]));
+ } catch (CVSException e) {
+ CVSUIPlugin.log(e);
+ }
+ }
+ }
+
+ private boolean isInActiveSet(IResource resource) {
+ for (Iterator iter = activeSets.iterator(); iter.hasNext();) {
+ CommitSet set = (CommitSet) iter.next();
+ if (set.contains(resource)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isDirtyFile(IResource resource) {
+ try {
+ if (resource.getType() != IResource.FILE) return false;
+ ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor((IFile)resource);
+ return cvsFile.isModified(null);
+ } catch (CVSException e) {
+ CVSUIPlugin.log(e);
+ return true;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#projectConfigured(org.eclipse.core.resources.IProject)
+ */
+ public void projectConfigured(IProject project) {
+ if (defaultSet != null) {
+ // Need to scan for outgoing changes and add them to the default set
+ CVSWorkspaceSubscriber subscriber = CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber();
+ // TODO: Should be done in a background job
+ SyncInfoSet syncSet = new SyncInfoSet();
+ subscriber.collectOutOfSync(new IResource[] { project }, IResource.DEPTH_INFINITE, syncSet, new NullProgressMonitor());
+ syncSet.selectNodes(ChangeLogModelProvider.OUTGOING_FILE_FILTER);
+ considerForDefaultSet(syncSet.getResources());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#projectDeconfigured(org.eclipse.core.resources.IProject)
+ */
+ public void projectDeconfigured(IProject project) {
+ for (Iterator iter = activeSets.iterator(); iter.hasNext();) {
+ CommitSet set = (CommitSet) iter.next();
+ set.projectRemoved(project);
+ }
+ }
+
+ /**
+ * Create a commit set with the given title and files. The created
+ * set is not added to the control of the commit set manager
+ * so no events are fired. The set can be added using the
+ * <code>add</code> method.
+ * @param title the title of the commit set
+ * @param files the files contained in the set
+ * @return the created set
+ * @throws CVSException
+ */
+ public CommitSet createCommitSet(String title, IResource[] files) throws CVSException {
+ CommitSet commitSet = new CommitSet(title);
+ if (files != null && files.length > 0)
+ commitSet.addFiles(files);
+ return commitSet;
+ }
+
+ /**
+ * Add the set to the list of active sets.
+ * @param set the set to be added
+ */
+ public void add(final CommitSet set) {
+ if (!contains(set)) {
+ activeSets.add(set);
+ Object[] listeners = getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ final ICommitSetChangeListener listener = (ICommitSetChangeListener)listeners[i];
+ Platform.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ // Exceptions are logged by the platform
+ }
+ public void run() throws Exception {
+ listener.setAdded(set);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Remove the set from the list of active sets.
+ * @param set the set to be removed
+ */
+ public void remove(final CommitSet set) {
+ if (contains(set)) {
+ activeSets.remove(set);
+ Object[] listeners = getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ final ICommitSetChangeListener listener = (ICommitSetChangeListener)listeners[i];
+ Platform.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ // Exceptions are logged by the platform
+ }
+ public void run() throws Exception {
+ listener.setRemoved(set);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Return whether the manager contains the given commit set
+ * @param set the commit set being tested
+ * @return whether the set is contained in the manager's list of active sets
+ */
+ public boolean contains(CommitSet set) {
+ return activeSets.contains(set);
+ }
+
+ /**
+ * Add the listener to the set of registered listeners.
+ * @param listener the listener to be added
+ */
+ public void addListener(ICommitSetChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Remove the listener from the set of registered listeners.
+ * @param listener the listener to remove
+ */
+ public void removeListener(ICommitSetChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Return the list of active commit sets.
+ * @return the list of active commit sets
+ */
+ public CommitSet[] getSets() {
+ return (CommitSet[]) activeSets.toArray(new CommitSet[activeSets.size()]);
+ }
+
+ /**
+ * Make the given set the default set into which all new modifications
+ * that ae not already in another set go.
+ * @param set the set which is to become the default set
+ */
+ public void makeDefault(CommitSet set) {
+ CommitSet oldDefault = defaultSet;
+ defaultSet = set;
+ firePropertyChange(DEFAULT_SET, oldDefault, defaultSet);
+ }
+
+ /**
+ * Retrn the set which is currently the default or
+ * <code>null</code> if there is no default set.
+ * @return
+ */
+ public CommitSet getDefaultCommitSet() {
+ return defaultSet;
+ }
+ /**
+ * Return whether the given set is the default set into which all
+ * new modifications will be placed.
+ * @param set the set to test
+ * @return whether the set is the default set
+ */
+ public boolean isDefault(CommitSet set) {
+ return set == defaultSet;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ICommitSetChangeListener.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ICommitSetChangeListener.java
new file mode 100644
index 000000000..061369abb
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/ICommitSetChangeListener.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.subscriber;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+/**
+ * Interface for registering commit set change listeners with
+ * the commit set manager.
+ */
+public interface ICommitSetChangeListener extends IPropertyChangeListener {
+
+ /**
+ * The given set has been added to the set manager.
+ * @param set the added set
+ */
+ void setAdded(CommitSet set);
+
+ /**
+ * The given set has been removed from the set manager.
+ * @param set the removed set
+ */
+ void setRemoved(CommitSet set);
+
+ /**
+ * The title of the given set has changed.
+ * @param set the set whose title changed
+ */
+ void titleChanged(CommitSet set);
+
+ /**
+ * The state of the given files have change with respect to the
+ * given set. This means that the files have either been added
+ * or removed from the set. Callers can use the files contained
+ * in the set to determine if each file is an addition or removal.
+ * @param set the commit set that has changed
+ * @param files the files whose containment state has changed w.r.t the set
+ */
+ void filesChanged(CommitSet set, IFile[] files);
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/WorkspaceCommitOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/WorkspaceCommitOperation.java
index 682cc50a2..cecbf2f6a 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/WorkspaceCommitOperation.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/WorkspaceCommitOperation.java
@@ -262,10 +262,32 @@ public class WorkspaceCommitOperation extends CVSSubscriberOperation {
* @return the comment, or null to cancel
*/
protected String promptForComment(RepositoryManager manager, IResource[] resourcesToCommit) {
- return manager.promptForComment(getShell(), resourcesToCommit);
+ String proposedComment = getProposedComment(resourcesToCommit);
+ return manager.promptForComment(getShell(), resourcesToCommit, proposedComment);
}
- protected IResource[] promptForResourcesToBeAdded(RepositoryManager manager, IResource[] unadded) {
+ private String getProposedComment(IResource[] resourcesToCommit) {
+ CommitSet[] sets = CommitSetManager.getInstance().getSets();
+ for (int i = 0; i < sets.length; i++) {
+ CommitSet set = sets[i];
+ if (containsAll(set, resourcesToCommit)) {
+ return set.getComment();
+ }
+ }
+ return null;
+ }
+
+ private boolean containsAll(CommitSet set, IResource[] resourcesToCommit) {
+ for (int j = 0; j < resourcesToCommit.length; j++) {
+ IResource resource = resourcesToCommit[j];
+ if (!set.contains(resource)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected IResource[] promptForResourcesToBeAdded(RepositoryManager manager, IResource[] unadded) {
return manager.promptForResourcesToBeAdded(getShell(), unadded);
}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/CommitSetTests.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/CommitSetTests.java
new file mode 100644
index 000000000..ee2249201
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/CommitSetTests.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.tests.ccvs.ui;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.ui.subscriber.*;
+import org.eclipse.team.internal.ccvs.ui.subscriber.CommitSet;
+import org.eclipse.team.internal.ccvs.ui.subscriber.CommitSetManager;
+import org.eclipse.team.tests.ccvs.core.EclipseTest;
+
+/**
+ * Tests for CVS commit sets
+ */
+public class CommitSetTests extends EclipseTest {
+
+ private List addedSets = new ArrayList();
+ private List removedSets = new ArrayList();
+ private ICommitSetChangeListener listener = new ICommitSetChangeListener() {
+ public void setAdded(CommitSet set) {
+ addedSets.add(set);
+ }
+ public void setRemoved(CommitSet set) {
+ removedSets.add(set);
+ }
+ public void titleChanged(CommitSet set) {
+ // TODO Auto-generated method stub
+
+ }
+ public void filesChanged(CommitSet set, IFile[] files) {
+ // TODO Auto-generated method stub
+
+ }
+ public void propertyChange(PropertyChangeEvent event) {
+ // TODO Auto-generated method stub
+
+ }
+ };
+
+ public static Test suite() {
+ return suite(CommitSetTests.class);
+ }
+
+ public CommitSetTests() {
+ super();
+ }
+
+ public CommitSetTests(String name) {
+ super(name);
+ }
+
+ /**
+ * Create a commit set and verify that it was created and contains the supplied files
+ * @param title the title of the new set
+ * @param files the files for the new set
+ * @return the newly create commit set
+ * @throws TeamException
+ */
+ protected CommitSet createCommitSet(String title, IFile[] files, boolean manageSet) throws TeamException {
+ assertIsModified(getName(), files);
+ CommitSetManager manager = CommitSetManager.getInstance();
+ CommitSet set = manager.createCommitSet(title, files);
+ assertEquals("Not all files were asdded to the set", files.length, set.getFiles().length);
+ if (manageSet) {
+ manager.add(set);
+ waitForSetAddedEvent(set);
+ }
+ return set;
+ }
+
+
+ /**
+ * Commit the files in the given set to the repository
+ * and ensure that the files are in the proper state
+ * @param set the commit set
+ * @throws CVSException
+ */
+ protected void commit(CommitSet set) throws CVSException {
+ boolean isManaged = setIsManaged(set);
+ try {
+ set.commit(null /* no workbench part */, DEFAULT_MONITOR);
+ } catch (InvocationTargetException e) {
+ throw CVSException.wrapException(e);
+ } catch (InterruptedException e) {
+ fail("The commit was interupted");
+ }
+ if (isManaged) {
+ // Committing the set should remove it from the manager
+ waitForSetRemovedEvent(set);
+ }
+ }
+
+ private boolean setIsManaged(CommitSet set) {
+ return CommitSetManager.getInstance().contains(set);
+ }
+
+ private void waitForSetAddedEvent(CommitSet set) {
+ int count = 0;
+ while (count < 5) {
+ if (addedSets.contains(set)) {
+ addedSets.remove(set);
+ return;
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // keep going
+ }
+ count++;
+ }
+ fail("Did not receive expected set added event");
+ }
+
+ private void waitForSetRemovedEvent(CommitSet set) {
+ int count = 0;
+ while (count < 5) {
+ if (removedSets.contains(set)) {
+ removedSets.remove(set);
+ return;
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // keep going
+ }
+ count++;
+ }
+ fail("Did not receive expected set removed event");
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.tests.harness.EclipseWorkspaceTest#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ CommitSetManager.getInstance().addListener(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.tests.ccvs.core.EclipseTest#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ CommitSetManager.getInstance().removeListener(listener);
+ }
+
+ /**
+ * Test a simple commit of a commit set. The set being committed is not managed.
+ * @throws CoreException
+ * @throws IOException
+ * @throws TeamException
+ */
+ public void testSimpleCommit() throws TeamException, CoreException, IOException {
+ IProject project = createProject(new String[] { "changed.txt", "deleted.txt", "folder1/", "folder1/a.txt" });
+ setContentsAndEnsureModified(project.getFile("changed.txt"));
+ deleteResources(project, new String[] { "deleted.txt" }, false /* don't commit */);
+ addResources(project, new String[] { "added.txt" }, false /* don't commit */);
+
+ IFile[] files = new IFile[] {
+ project.getFile("changed.txt"),
+ project.getFile("deleted.txt"),
+ project.getFile("added.txt")};
+ CommitSet set = createCommitSet("testSimpleCommit", files, false /* do not manage the set */);
+ commit(set);
+ assertLocalStateEqualsRemote(project);
+ }
+
+ /**
+ * Test a managed commit of a commit set. The set being committed is managed
+ * and should be removed once the commit succeeds.
+ * @throws CoreException
+ * @throws IOException
+ * @throws TeamException
+ */
+ public void testManagedCommit() throws TeamException, CoreException, IOException {
+ IProject project = createProject(new String[] { "changed.txt", "deleted.txt", "folder1/", "folder1/a.txt" });
+ setContentsAndEnsureModified(project.getFile("changed.txt"));
+ deleteResources(project, new String[] { "deleted.txt" }, false /* don't commit */);
+ addResources(project, new String[] { "added.txt" }, false /* don't commit */);
+
+ IFile[] files = new IFile[] {
+ project.getFile("changed.txt"),
+ project.getFile("deleted.txt"),
+ project.getFile("added.txt")};
+ CommitSet set = createCommitSet("testSimpleCommit", files, true /* manage the set */);
+ commit(set);
+ assertLocalStateEqualsRemote(project);
+ }
+}

Back to the top