IDEA-27603 (Indicate which branch is being worked on)

This commit is contained in:
irengrig
2010-02-03 11:59:12 +03:00
parent 04678a38df
commit 3f995476ba
19 changed files with 177 additions and 35 deletions
@@ -237,6 +237,7 @@ changes.nodetitle.locked.folders=Locked working copy folders
changes.nodetitle.locked.folders.tooltip=Some folders are locked; VCS operations are not allowed for them. Cleanup should be performed.
changes.nodetitle.logicallt.locked.folders=Explicitly locked files
changes.nodetitle.switched.files=Switched Files
changes.nodetitle.switched.roots=Roots Switches
changes.nodetitle.updating=(updating...)
changes.nodetitle.have.outdated.files=Some files are changed on server
change.nodetitle.change.is.outdated=File is changed on server
@@ -67,6 +67,9 @@ public class MockChangelistBuilder implements ChangelistBuilder {
public void processSwitchedFile(VirtualFile file, String branch, final boolean recursive) {
}
public void processRootSwitch(VirtualFile file, String branch) {
}
public boolean isUpdatingUnversionedFiles() {
return true;
}
@@ -103,6 +103,8 @@ public interface ChangelistBuilder {
*/
void processSwitchedFile(VirtualFile file, String branch, final boolean recursive);
void processRootSwitch(VirtualFile file, String branch);
boolean isUpdatingUnversionedFiles();
boolean reportChangesOutsideProject();
@@ -58,6 +58,9 @@ public class EmptyChangelistBuilder implements ChangelistBuilder {
public void processSwitchedFile(final VirtualFile file, final String branch, final boolean recursive) {
}
public void processRootSwitch(VirtualFile file, String branch) {
}
public boolean isUpdatingUnversionedFiles() {
return true;
}
@@ -492,6 +492,13 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
}
}
@Nullable
Map<VirtualFile, String> getSwitchedRoots() {
synchronized (myDataLock) {
return ((SwitchedFileHolder) myComposite.get(FileHolder.HolderType.ROOT_SWITCH)).getFilesMapCopy();
}
}
public VcsException getUpdateException() {
return myUpdateException;
}
@@ -55,8 +55,8 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.Collection;
public class ChangesViewManager extends AbstractProjectComponent implements JDOMExternalizable {
@@ -221,6 +221,7 @@ public class ChangesViewManager extends AbstractProjectComponent implements JDOM
changeListManager.getDeletedFiles(),
changeListManager.getModifiedWithoutEditing(),
changeListManager.getSwitchedFilesMap(),
changeListManager.getSwitchedRoots(),
SHOW_IGNORED_MODE ? changeListManager.getIgnoredFiles() : null, changeListManager.getLockedFolders(),
changeListManager.getLogicallyLockedFolders());
}
@@ -28,6 +28,7 @@ interface FileHolder {
MODIFIED_WITHOUT_EDITING,
IGNORED,
LOCKED,
LOGICALLY_LOCKED
LOGICALLY_LOCKED,
ROOT_SWITCH
}
}
@@ -27,7 +27,7 @@ public class FileHolderComposite implements FileHolder {
public FileHolderComposite(final Project project) {
myHolders = new HashMap<HolderType, FileHolder>();
myHolders.put(FileHolder.HolderType.UNVERSIONED, new VirtualFileHolder(project, FileHolder.HolderType.UNVERSIONED));
//myHolders.put(FileHolder.HolderType.SWITCHED, new SwitchedFileHolder(project, HolderType.SWITCHED));
myHolders.put(FileHolder.HolderType.ROOT_SWITCH, new SwitchedFileHolder(project, HolderType.ROOT_SWITCH));
myHolders.put(FileHolder.HolderType.MODIFIED_WITHOUT_EDITING, new VirtualFileHolder(project, FileHolder.HolderType.MODIFIED_WITHOUT_EDITING));
myHolders.put(FileHolder.HolderType.IGNORED, new RecursiveFileHolder(project, FileHolder.HolderType.IGNORED));
myHolders.put(FileHolder.HolderType.LOCKED, new VirtualFileHolder(project, FileHolder.HolderType.LOCKED));
@@ -18,6 +18,9 @@ package com.intellij.openapi.vcs.changes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import java.util.HashMap;
import java.util.Map;
public class SwitchedFileHolder extends RecursiveFileHolder {
public SwitchedFileHolder(Project project, HolderType holderType) {
super(project, holderType);
@@ -46,4 +49,8 @@ public class SwitchedFileHolder extends RecursiveFileHolder {
final VirtualFile parent = file.getParent();
return (parent != null) && (scope.isRecursivelyDirty(parent));
}
public Map<VirtualFile, String> getFilesMapCopy() {
return new HashMap<VirtualFile, String>(myFiles);
}
}
@@ -188,6 +188,14 @@ class UpdatingChangeListBuilder implements ChangelistBuilder {
}
}
public void processRootSwitch(VirtualFile file, String branch) {
if (file == null) return;
checkIfDisposed();
if (myScope.belongsTo(new FilePathImpl(file))) {
((SwitchedFileHolder) myComposite.get(FileHolder.HolderType.ROOT_SWITCH)).addFile(file, branch, false);
}
}
public boolean isUpdatingUnversionedFiles() {
return myUpdateUnversioned;
}
@@ -702,6 +702,9 @@ public class ApplyPatchDifferentiatedDialog extends DialogWrapper {
}
return null;
}
public void preDecorate(Change change, ChangesBrowserNodeRenderer renderer, boolean showFlatten) {
}
}
public Collection<FilePatchInProgress> getIncluded() {
@@ -29,6 +29,8 @@ public interface ChangeNodeDecorator {
@Nullable
List<Pair<String, Stress>> stressPartsOfFileName(final Change change, final String parentPath);
void preDecorate(Change change, ChangesBrowserNodeRenderer renderer, boolean showFlatten);
enum Stress {
BOLD(Font.BOLD),
ITALIC(Font.ITALIC),
@@ -59,6 +59,11 @@ public class ChangesBrowserChangeNode extends ChangesBrowserNode<Change> impleme
final FilePath filePath = ChangesUtil.getFilePath(change);
final String fileName = filePath.getName();
VirtualFile vFile = filePath.getVirtualFile();
if (myDecorator != null) {
myDecorator.preDecorate(change, renderer, renderer.isShowFlatten());
}
final Color changeColor = change.getFileStatus().getColor();
renderer.appendFileName(vFile, fileName, changeColor);
@@ -37,6 +37,8 @@ import java.util.List;
* @author max
*/
public class ChangesBrowserNode<T> extends DefaultMutableTreeNode {
private SimpleTextAttributes myAttributes;
protected int myCount = -1;
protected int myDirectoryCount = -1;
public static final Object IGNORED_FILES_TAG = new Object() {
@@ -66,9 +68,15 @@ public class ChangesBrowserNode<T> extends DefaultMutableTreeNode {
return VcsBundle.message("changes.nodetitle.switched.files");
}
};
public static final Object SWITCHED_ROOTS_TAG = new Object() {
public String toString() {
return VcsBundle.message("changes.nodetitle.switched.roots");
}
};
protected ChangesBrowserNode(Object userObject) {
super(userObject);
myAttributes = SimpleTextAttributes.REGULAR_ATTRIBUTES;
}
public static ChangesBrowserNode create(final Project project, @NotNull final LocallyDeletedChange change) {
@@ -190,7 +198,7 @@ public class ChangesBrowserNode<T> extends DefaultMutableTreeNode {
}
public void render(final ChangesBrowserNodeRenderer renderer, final boolean selected, final boolean expanded, final boolean hasFocus) {
renderer.append(userObject.toString(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
renderer.append(userObject.toString(), myAttributes);
appendCount(renderer);
}
@@ -244,4 +252,8 @@ public class ChangesBrowserNode<T> extends DefaultMutableTreeNode {
public FilePath[] getFilePathsUnder() {
return new FilePath[0];
}
public void setAttributes(SimpleTextAttributes attributes) {
myAttributes = attributes;
}
}
@@ -30,10 +30,13 @@ import com.intellij.openapi.vcs.changes.issueLinks.TreeLinkMouseListener;
import com.intellij.openapi.vcs.diff.DiffProvider;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.*;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.SmartExpander;
import com.intellij.ui.TreeCopyProvider;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.awt.RelativeRectangle;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.intellij.util.EditSourceOnEnterKeyHandler;
import com.intellij.util.containers.Convertor;
@@ -134,6 +137,7 @@ public class ChangesListView extends Tree implements TypeSafeDataProvider, Advan
public void updateModel(List<? extends ChangeList> changeLists, List<VirtualFile> unversionedFiles, final List<LocallyDeletedChange> locallyDeletedFiles,
List<VirtualFile> modifiedWithoutEditing,
MultiMap<String, VirtualFile> switchedFiles,
@Nullable Map<VirtualFile, String> switchedRoots,
@Nullable List<VirtualFile> ignoredFiles,
final List<VirtualFile> lockedFolders,
@Nullable final Map<VirtualFile, LogicalLock> logicallyLockedFiles) {
@@ -141,7 +145,7 @@ public class ChangesListView extends Tree implements TypeSafeDataProvider, Advan
TreeModelBuilder builder = new TreeModelBuilder(myProject, isShowFlatten());
final DefaultTreeModel model = builder.buildModel(changeLists, unversionedFiles, locallyDeletedFiles, modifiedWithoutEditing,
switchedFiles, ignoredFiles, lockedFolders, logicallyLockedFiles);
switchedFiles, switchedRoots, ignoredFiles, lockedFolders, logicallyLockedFiles);
setModel(model);
setCellRenderer(new ChangesBrowserNodeRenderer(myProject, isShowFlatten(), true));
@@ -48,4 +48,7 @@ public class RemoteStatusChangeNodeDecorator implements ChangeNodeDecorator {
public List<Pair<String, Stress>> stressPartsOfFileName(Change change, String parentPath) {
return null;
}
public void preDecorate(Change change, ChangesBrowserNodeRenderer renderer, boolean showFlatten) {
}
}
@@ -17,11 +17,15 @@ package com.intellij.openapi.vcs.changes.ui;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NonNls;
@@ -102,6 +106,9 @@ public class TreeModelBuilder {
protected void reportState(boolean state) {
myReporter.report(state);
}
public void preDecorate(Change change, ChangesBrowserNodeRenderer renderer, boolean showFlatten) {
}
}
public DefaultTreeModel buildModel(final List<? extends ChangeList> changeLists,
@@ -109,6 +116,7 @@ public class TreeModelBuilder {
final List<LocallyDeletedChange> locallyDeletedFiles,
final List<VirtualFile> modifiedWithoutEditing,
final MultiMap<String, VirtualFile> switchedFiles,
@Nullable Map<VirtualFile, String> switchedRoots,
@Nullable final List<VirtualFile> ignoredFiles, @Nullable final List<VirtualFile> lockedFolders,
@Nullable final Map<VirtualFile, LogicalLock> logicallyLockedFiles) {
buildModel(changeLists);
@@ -119,6 +127,9 @@ public class TreeModelBuilder {
if (!unversionedFiles.isEmpty()) {
buildVirtualFiles(unversionedFiles, ChangesBrowserNode.UNVERSIONED_FILES_TAG);
}
if (switchedRoots != null && (! switchedRoots.isEmpty())) {
buildSwitchedRoots(switchedRoots);
}
if (!switchedFiles.isEmpty()) {
buildSwitchedFiles(switchedFiles);
}
@@ -231,6 +242,34 @@ public class TreeModelBuilder {
}
}
private void buildSwitchedRoots(final Map<VirtualFile, String> switchedRoots) {
final ChangesBrowserNode rootsHeadNode = ChangesBrowserNode.create(myProject, ChangesBrowserNode.SWITCHED_ROOTS_TAG);
rootsHeadNode.setAttributes(SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
model.insertNodeInto(rootsHeadNode, root, root.getChildCount());
for (VirtualFile vf : switchedRoots.keySet()) {
final HashMap<String, ChangesBrowserNode> foldersCache = new HashMap<String, ChangesBrowserNode>();
final ChangesGroupingPolicy policy = createGroupingPolicy();
final ContentRevision cr = new CurrentContentRevision(new FilePathImpl(vf));
final Change change = new Change(cr, cr, FileStatus.NOT_CHANGED);
final String branchName = switchedRoots.get(vf);
insertChangeNode(vf, foldersCache, policy, rootsHeadNode, new Computable<ChangesBrowserNode>() {
public ChangesBrowserNode compute() {
return new ChangesBrowserChangeNode(myProject, change, new ChangeNodeDecorator() {
public void decorate(Change change, SimpleColoredComponent component, boolean isShowFlatten) {
}
public List<Pair<String, Stress>> stressPartsOfFileName(Change change, String parentPath) {
return null;
}
public void preDecorate(Change change, ChangesBrowserNodeRenderer renderer, boolean showFlatten) {
renderer.append("[" + branchName + "] ", SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
}
});
}
});
}
}
private void buildSwitchedFiles(final MultiMap<String, VirtualFile> switchedFiles) {
ChangesBrowserNode baseNode = ChangesBrowserNode.create(myProject, ChangesBrowserNode.SWITCHED_FILES_TAG);
model.insertNodeInto(baseNode, root, root.getChildCount());
@@ -44,6 +44,7 @@ import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.actions.VcsContextFactory;
import com.intellij.openapi.vcs.changes.*;
@@ -59,9 +60,7 @@ import org.netbeans.lib.cvsclient.admin.Entry;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.*;
/**
* @author max
@@ -93,6 +92,9 @@ public class CvsChangeProvider implements ChangeProvider {
}
}
};
showBranchImOn(builder, dirtyScope);
for (FilePath path : dirtyScope.getRecursivelyDirtyDirectories()) {
final VirtualFile dir = path.getVirtualFile();
checkCanceled.run();
@@ -238,6 +240,61 @@ public class CvsChangeProvider implements ChangeProvider {
return new CvsRevisionNumber(correctedRevision);
}
private void showBranchImOn(final ChangelistBuilder builder, final VcsDirtyScope scope) {
final List<VirtualFile> dirs = ObjectsConvertor.fp2vf(scope.getRecursivelyDirtyDirectories());
final Collection<VirtualFile> roots = new ArrayList<VirtualFile>(scope.getAffectedContentRoots());
for (Iterator<VirtualFile> iterator = roots.iterator(); iterator.hasNext();) {
final VirtualFile root = iterator.next();
if (! dirs.contains(root)) iterator.remove();
}
if (roots.isEmpty()) return;
for (VirtualFile root : roots) {
checkTopLevelForBeingSwitched(root, builder);
}
}
private void checkTopLevelForBeingSwitched(final VirtualFile dir, final ChangelistBuilder builder) {
final CvsInfo info = myEntriesManager.getCvsInfoFor(dir);
if (info.getRepository() == null) return;
final String dirTag = info.getStickyTag();
if (dirTag != null) {
final String caption = getSwitchedTagCaption(dirTag, null, false);
if (caption != null) {
builder.processRootSwitch(dir, caption);
}
} else {
builder.processRootSwitch(dir, CvsUtil.HEAD);
}
}
@Nullable
private static String getSwitchedTagCaption(final String tag, final String parentTag, final boolean checkParentTag) {
if (tag == null) return CvsUtil.HEAD;
final String tagOnly = tag.substring(1);
if (CvsUtil.isNonDateTag(tag)) {
// a switch between a branch tag and a non-branch tag is not a switch
if (checkParentTag && parentTag != null && CvsUtil.isNonDateTag(parentTag)) {
String parentTagOnly = parentTag.substring(1);
if (tagOnly.equals(parentTagOnly)) {
return null;
}
}
return CvsBundle.message("switched.tag.format", tagOnly);
}
else if (tag.startsWith(CvsUtil.STICKY_DATE_PREFIX)) {
try {
Date date = Entry.STICKY_DATE_FORMAT.parse(tagOnly);
return CvsBundle.message("switched.date.format", date);
}
catch (ParseException e) {
return CvsBundle.message("switched.date.format", tagOnly);
}
}
return null;
}
private void checkSwitchedDir(final VirtualFile dir, final ChangelistBuilder builder, final VcsDirtyScope scope) {
VirtualFile parentDir = dir.getParent();
if (parentDir == null || !myFileIndex.isInContent(parentDir)) {
@@ -253,28 +310,9 @@ public class CvsChangeProvider implements ChangeProvider {
final CvsInfo parentInfo = myEntriesManager.getCvsInfoFor(parentDir);
final String parentDirTag = parentInfo.getStickyTag();
if (!Comparing.equal(dirTag, parentDirTag)) {
if (dirTag == null) {
builder.processSwitchedFile(dir, CvsUtil.HEAD, true);
}
else if (CvsUtil.isNonDateTag(dirTag)) {
final String tag = dirTag.substring(1);
// a switch between a branch tag and a non-branch tag is not a switch
if (parentDirTag != null && CvsUtil.isNonDateTag(parentDirTag)) {
String parentTag = parentDirTag.substring(1);
if (tag.equals(parentTag)) {
return;
}
}
builder.processSwitchedFile(dir, CvsBundle.message("switched.tag.format", tag), true);
}
else if (dirTag.startsWith(CvsUtil.STICKY_DATE_PREFIX)) {
try {
Date date = Entry.STICKY_DATE_FORMAT.parse(dirTag.substring(1));
builder.processSwitchedFile(dir, CvsBundle.message("switched.date.format", date), true);
}
catch (ParseException e) {
builder.processSwitchedFile(dir, CvsBundle.message("switched.date.format", dirTag.substring(1)), true);
}
final String caption = getSwitchedTagCaption(dirTag, parentDirTag, true);
if (caption != null) {
builder.processSwitchedFile(dir, caption, true);
}
}
else if (!scope.belongsTo(VcsContextFactory.SERVICE.getInstance().createFilePathOn(parentDir))) {
@@ -24,11 +24,11 @@ import com.intellij.openapi.vcs.update.UpdatedFilesReverseSide;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNPropertyData;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.wc.SVNPropertyData;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import java.io.File;
import java.util.ArrayList;
@@ -123,6 +123,9 @@ public class GatheringChangelistBuilder implements ChangelistBuilder {
}
public void processRootSwitch(VirtualFile file, String branch) {
}
public boolean isUpdatingUnversionedFiles() {
return false;
}