diff options
author | Michael Valenta | 2004-09-02 20:31:38 +0000 |
---|---|---|
committer | Michael Valenta | 2004-09-02 20:31:38 +0000 |
commit | 4d75684251589b8d7d2ff3ad4081a20a419d9256 (patch) | |
tree | 8e765a9dac16135360d315238a97a4f0912c8786 | |
parent | c02c796dcf8c9a29cf2a210152b03a78742cfd07 (diff) | |
download | eclipse.platform.team-4d75684251589b8d7d2ff3ad4081a20a419d9256.tar.gz eclipse.platform.team-4d75684251589b8d7d2ff3ad4081a20a419d9256.tar.xz eclipse.platform.team-4d75684251589b8d7d2ff3ad4081a20a419d9256.zip |
Starting outgoing commit set work on a branch
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); + } +} |