mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
git log structure chooser
This commit is contained in:
+1
-1
@@ -160,7 +160,7 @@ public class ContentEntryTreeEditor {
|
||||
};
|
||||
|
||||
|
||||
myFileSystemTree = new FileSystemTreeImpl(myProject, myDescriptor, myTree, getContentEntryCellRenderer(), init) {
|
||||
myFileSystemTree = new FileSystemTreeImpl(myProject, myDescriptor, myTree, getContentEntryCellRenderer(), init, null) {
|
||||
protected AbstractTreeBuilder createTreeBuilder(JTree tree, DefaultTreeModel treeModel, AbstractTreeStructure treeStructure,
|
||||
Comparator<NodeDescriptor> comparator, FileChooserDescriptor descriptor,
|
||||
final Runnable onInitialized) {
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.intellij.ui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -74,4 +76,12 @@ public class CollectionListModel extends AbstractListModel {
|
||||
int i = myItems.indexOf(element);
|
||||
fireContentsChanged(this, i, i);
|
||||
}
|
||||
|
||||
public void sort(final Comparator<?> comparator) {
|
||||
Collections.sort(myItems, comparator);
|
||||
}
|
||||
|
||||
public List getItems() {
|
||||
return Collections.unmodifiableList(myItems);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-6
@@ -17,7 +17,6 @@ package com.intellij.openapi.fileChooser.ex;
|
||||
|
||||
import com.intellij.ide.util.treeView.AbstractTreeBuilder;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeStructure;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeUi;
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
@@ -39,11 +38,14 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.PopupHandler;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.ui.TreeSpeedSearch;
|
||||
import com.intellij.ui.UIBundle;
|
||||
import com.intellij.ui.treeStructure.SimpleNodeRenderer;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import com.intellij.util.containers.ConvertingIterator;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -73,13 +75,14 @@ public class FileSystemTreeImpl implements FileSystemTree {
|
||||
private final MyExpansionListener myExpansionListener = new MyExpansionListener();
|
||||
|
||||
public FileSystemTreeImpl(@Nullable Project project, FileChooserDescriptor descriptor) {
|
||||
this(project, descriptor, new Tree(), null, null);
|
||||
this(project, descriptor, new Tree(), null, null, null);
|
||||
myTree.setRootVisible(descriptor.isTreeRootVisible());
|
||||
myTree.setShowsRootHandles(true);
|
||||
}
|
||||
|
||||
public FileSystemTreeImpl(@Nullable Project project, FileChooserDescriptor descriptor, Tree tree, TreeCellRenderer renderer,
|
||||
final Runnable onInitialized) {
|
||||
final Runnable onInitialized,
|
||||
Convertor<TreePath, String> speedSearchConvertor) {
|
||||
myProject = project;
|
||||
myTreeStructure = new FileTreeStructure(project, descriptor);
|
||||
myDescriptor = descriptor;
|
||||
@@ -114,7 +117,11 @@ public class FileSystemTreeImpl implements FileSystemTree {
|
||||
}
|
||||
});
|
||||
|
||||
new TreeSpeedSearch(myTree);
|
||||
if (speedSearchConvertor != null) {
|
||||
new TreeSpeedSearch(myTree, speedSearchConvertor);
|
||||
} else {
|
||||
new TreeSpeedSearch(myTree);
|
||||
}
|
||||
myTree.setLineStyleAngled();
|
||||
TreeUtil.installActions(myTree);
|
||||
|
||||
@@ -220,6 +227,10 @@ public class FileSystemTreeImpl implements FileSystemTree {
|
||||
}
|
||||
}
|
||||
|
||||
public AbstractTreeBuilder getTreeBuilder() {
|
||||
return myTreeBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since tree updating is an asynchronous operation
|
||||
*/
|
||||
|
||||
@@ -27,12 +27,9 @@ import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.FilePath;
|
||||
import com.intellij.openapi.vcs.FileStatus;
|
||||
import com.intellij.openapi.vcs.FileStatusManager;
|
||||
import com.intellij.openapi.vcs.VcsBundle;
|
||||
import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vcs.changes.ChangesUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.ui.components.panels.NonOpaquePanel;
|
||||
@@ -52,7 +49,6 @@ import javax.swing.border.Border;
|
||||
import javax.swing.tree.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
@@ -640,50 +636,32 @@ public abstract class ChangesTreeList<T> extends JPanel {
|
||||
public MyListCellRenderer() {
|
||||
super(new BorderLayout());
|
||||
myCheckbox = new JCheckBox();
|
||||
myTextRenderer = new ColoredListCellRenderer() {
|
||||
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
|
||||
final FilePath path = TreeModelBuilder.getPathForObject(value);
|
||||
if (path.isDirectory()) {
|
||||
setIcon(PlatformIcons.DIRECTORY_CLOSED_ICON);
|
||||
} else {
|
||||
setIcon(path.getFileType().getIcon());
|
||||
}
|
||||
final FileStatus fileStatus;
|
||||
if (value instanceof Change) {
|
||||
fileStatus = ((Change) value).getFileStatus();
|
||||
}
|
||||
else {
|
||||
final VirtualFile virtualFile = path.getVirtualFile();
|
||||
if (virtualFile != null) {
|
||||
fileStatus = FileStatusManager.getInstance(myProject).getStatus(virtualFile);
|
||||
}
|
||||
else {
|
||||
fileStatus = FileStatus.NOT_CHANGED;
|
||||
}
|
||||
}
|
||||
append(path.getName(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, fileStatus.getColor(), null));
|
||||
myTextRenderer = new VirtualFileListCellRenderer(myProject) {
|
||||
@Override
|
||||
protected void putParentPath(Object value, FilePath path, FilePath self) {
|
||||
super.putParentPath(value, path, self);
|
||||
final boolean applyChangeDecorator = (value instanceof Change) && myChangeDecorator != null;
|
||||
final File parentFile = path.getIOFile().getParentFile();
|
||||
if (parentFile != null) {
|
||||
final String parentPath = parentFile.getPath();
|
||||
List<Pair<String,ChangeNodeDecorator.Stress>> parts = null;
|
||||
if (applyChangeDecorator) {
|
||||
parts = myChangeDecorator.stressPartsOfFileName((Change)value, parentPath);
|
||||
}
|
||||
if (parts == null) {
|
||||
parts = Collections.singletonList(new Pair<String, ChangeNodeDecorator.Stress>(parentPath, ChangeNodeDecorator.Stress.PLAIN));
|
||||
}
|
||||
|
||||
append(" (");
|
||||
for (Pair<String, ChangeNodeDecorator.Stress> part : parts) {
|
||||
append(part.getFirst(), part.getSecond().derive(SimpleTextAttributes.GRAYED_ATTRIBUTES));
|
||||
}
|
||||
append(")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
|
||||
}
|
||||
if (applyChangeDecorator) {
|
||||
myChangeDecorator.decorate((Change) value, this, isShowFlatten());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putParentPathImpl(Object value, String parentPath, FilePath self) {
|
||||
final boolean applyChangeDecorator = (value instanceof Change) && myChangeDecorator != null;
|
||||
List<Pair<String,ChangeNodeDecorator.Stress>> parts = null;
|
||||
if (applyChangeDecorator) {
|
||||
parts = myChangeDecorator.stressPartsOfFileName((Change)value, parentPath);
|
||||
}
|
||||
if (parts == null) {
|
||||
super.putParentPathImpl(value, parentPath, self);
|
||||
return;
|
||||
}
|
||||
|
||||
for (Pair<String, ChangeNodeDecorator.Stress> part : parts) {
|
||||
append(part.getFirst(), part.getSecond().derive(SimpleTextAttributes.GRAYED_ATTRIBUTES));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
myCheckbox.setBackground(null);
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.vcs.changes.ui;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vcs.FilePath;
|
||||
import com.intellij.openapi.vcs.FileStatus;
|
||||
import com.intellij.openapi.vcs.FileStatusManager;
|
||||
import com.intellij.openapi.vcs.changes.Change;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.ColoredListCellRenderer;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
* Date: 7/8/11
|
||||
* Time: 12:21 PM
|
||||
*/
|
||||
public class VirtualFileListCellRenderer extends ColoredListCellRenderer {
|
||||
private final FileStatusManager myFileStatusManager;
|
||||
private final boolean myIgnoreFileStatus;
|
||||
|
||||
public VirtualFileListCellRenderer(final Project project) {
|
||||
this(project, false);
|
||||
}
|
||||
|
||||
public VirtualFileListCellRenderer(final Project project, final boolean ignoreFileStatus) {
|
||||
myIgnoreFileStatus = ignoreFileStatus;
|
||||
myFileStatusManager = FileStatusManager.getInstance(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
|
||||
final FilePath path = TreeModelBuilder.getPathForObject(value);
|
||||
renderIcon(path);
|
||||
final FileStatus fileStatus = myIgnoreFileStatus ? FileStatus.NOT_CHANGED : getStatus(value, path);
|
||||
append(getName(path), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, fileStatus.getColor(), null));
|
||||
putParentPath(value, path, path);
|
||||
}
|
||||
|
||||
protected String getName(FilePath path) {
|
||||
return path.getName();
|
||||
}
|
||||
|
||||
protected FileStatus getStatus(Object value, FilePath path) {
|
||||
final FileStatus fileStatus;
|
||||
if (value instanceof Change) {
|
||||
fileStatus = ((Change) value).getFileStatus();
|
||||
}
|
||||
else {
|
||||
final VirtualFile virtualFile = path.getVirtualFile();
|
||||
if (virtualFile != null) {
|
||||
fileStatus = myFileStatusManager.getStatus(virtualFile);
|
||||
}
|
||||
else {
|
||||
fileStatus = FileStatus.NOT_CHANGED;
|
||||
}
|
||||
}
|
||||
return fileStatus;
|
||||
}
|
||||
|
||||
protected void renderIcon(FilePath path) {
|
||||
if (path.isDirectory()) {
|
||||
setIcon(PlatformIcons.DIRECTORY_CLOSED_ICON);
|
||||
} else {
|
||||
setIcon(path.getFileType().getIcon());
|
||||
}
|
||||
}
|
||||
|
||||
protected void putParentPath(Object value, FilePath path, FilePath self) {
|
||||
final File parentFile = path.getIOFile().getParentFile();
|
||||
if (parentFile != null) {
|
||||
final String parentPath = parentFile.getPath();
|
||||
append(" (", SimpleTextAttributes.GRAYED_ATTRIBUTES);
|
||||
putParentPathImpl(value, parentPath, self);
|
||||
append(")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
|
||||
protected void putParentPathImpl(Object value, String parentPath, FilePath self) {
|
||||
append(parentPath, SimpleTextAttributes.GRAYED_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.intellij.util.containers.hash.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -91,4 +92,9 @@ public class SelectedState<T> {
|
||||
public Set<T> getSelected() {
|
||||
return Collections.unmodifiableSet(mySelected);
|
||||
}
|
||||
|
||||
public void setSelection(Collection<T> files) {
|
||||
mySelected.clear();
|
||||
mySelected.addAll(files);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,20 @@
|
||||
package com.intellij.util.treeWithCheckedNodes;
|
||||
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vcs.impl.CollectionsDelta;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.PairProcessor;
|
||||
import com.intellij.util.PlusMinus;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.TreeNodeState;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
@@ -35,6 +41,8 @@ import javax.swing.tree.DefaultMutableTreeNode;
|
||||
public class SelectionManager {
|
||||
private final SelectedState<VirtualFile> myState;
|
||||
private final Convertor<DefaultMutableTreeNode, VirtualFile> myNodeConvertor;
|
||||
@Nullable
|
||||
private PlusMinus<VirtualFile> mySelectionChangeListener;
|
||||
|
||||
public SelectionManager(int selectedSize, int queueSize, final Convertor<DefaultMutableTreeNode, VirtualFile> nodeConvertor) {
|
||||
myNodeConvertor = nodeConvertor;
|
||||
@@ -43,14 +51,17 @@ public class SelectionManager {
|
||||
|
||||
public void toggleSelection(final DefaultMutableTreeNode node) {
|
||||
final StateWorker stateWorker = new StateWorker(node, myNodeConvertor);
|
||||
if (stateWorker.getVf() == null) return;
|
||||
final VirtualFile vf = stateWorker.getVf();
|
||||
if (vf == null) return;
|
||||
|
||||
final TreeNodeState state = getStateImpl(stateWorker);
|
||||
if (TreeNodeState.HAVE_SELECTED_ABOVE.equals(state)) return;
|
||||
if (TreeNodeState.CLEAR.equals(state) && (! myState.canAddSelection())) return;
|
||||
|
||||
final HashSet<VirtualFile> old = new HashSet<VirtualFile>(myState.getSelected());
|
||||
|
||||
final TreeNodeState futureState =
|
||||
myState.putAndPass(stateWorker.getVf(), TreeNodeState.SELECTED.equals(state) ? TreeNodeState.CLEAR : TreeNodeState.SELECTED);
|
||||
myState.putAndPass(vf, TreeNodeState.SELECTED.equals(state) ? TreeNodeState.CLEAR : TreeNodeState.SELECTED);
|
||||
|
||||
// for those possibly duplicate nodes (i.e. when we have root for module and root for VCS root, each file is shown twice in a tree ->
|
||||
// clear all suspicious cached)
|
||||
@@ -58,7 +69,7 @@ public class SelectionManager {
|
||||
myState.clearAllCachedMatching(new Processor<VirtualFile>() {
|
||||
@Override
|
||||
public boolean process(VirtualFile virtualFile) {
|
||||
return VfsUtil.isAncestor(virtualFile, stateWorker.getVf(), false);
|
||||
return VfsUtil.isAncestor(virtualFile, vf, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -73,6 +84,7 @@ public class SelectionManager {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// todo vf, vf - what is correct?
|
||||
myState.clearAllCachedMatching(new Processor<VirtualFile>() {
|
||||
@Override
|
||||
public boolean process(VirtualFile vf) {
|
||||
@@ -84,6 +96,38 @@ public class SelectionManager {
|
||||
myState.remove(selected);
|
||||
}
|
||||
}
|
||||
final Set<VirtualFile> selectedAfter = myState.getSelected();
|
||||
if (mySelectionChangeListener != null && ! old.equals(selectedAfter)) {
|
||||
final Set<VirtualFile> removed = CollectionsDelta.notInSecond(old, selectedAfter);
|
||||
final Set<VirtualFile> newlyAdded = CollectionsDelta.notInSecond(selectedAfter, old);
|
||||
if (newlyAdded != null) {
|
||||
for (VirtualFile file : newlyAdded) {
|
||||
if (mySelectionChangeListener != null) {
|
||||
mySelectionChangeListener.plus(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed != null) {
|
||||
for (VirtualFile file : removed) {
|
||||
if (mySelectionChangeListener != null) {
|
||||
mySelectionChangeListener.minus(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canAddSelection() {
|
||||
return myState.canAddSelection();
|
||||
}
|
||||
|
||||
public void setSelection(Collection<VirtualFile> files) {
|
||||
myState.setSelection(files);
|
||||
for (VirtualFile file : files) {
|
||||
if (mySelectionChangeListener != null) {
|
||||
mySelectionChangeListener.plus(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TreeNodeState getState(final DefaultMutableTreeNode node) {
|
||||
@@ -120,6 +164,19 @@ public class SelectionManager {
|
||||
return TreeNodeState.CLEAR;
|
||||
}
|
||||
|
||||
public void removeSelection(final VirtualFile elementAt) {
|
||||
myState.remove(elementAt);
|
||||
myState.clearAllCachedMatching(new Processor<VirtualFile>() {
|
||||
@Override
|
||||
public boolean process(VirtualFile virtualFile) {
|
||||
return VfsUtil.isAncestor(virtualFile, elementAt, false) || VfsUtil.isAncestor(elementAt, virtualFile, false);
|
||||
}
|
||||
});
|
||||
if (mySelectionChangeListener != null) {
|
||||
mySelectionChangeListener.minus(elementAt);
|
||||
}
|
||||
}
|
||||
|
||||
private static class StateWorker {
|
||||
private final DefaultMutableTreeNode myNode;
|
||||
private final Convertor<DefaultMutableTreeNode, VirtualFile> myConvertor;
|
||||
@@ -148,4 +205,12 @@ public class SelectionManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PlusMinus<VirtualFile> getSelectionChangeListener() {
|
||||
return mySelectionChangeListener;
|
||||
}
|
||||
|
||||
public void setSelectionChangeListener(PlusMinus<VirtualFile> selectionChangeListener) {
|
||||
mySelectionChangeListener = selectionChangeListener;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,12 +304,15 @@ public class GitChangeUtils {
|
||||
|
||||
@Nullable
|
||||
public static SHAHash commitExists(final Project project, final VirtualFile root, final String anyReference,
|
||||
final String... parameters) {
|
||||
List<VirtualFile> paths, final String... parameters) {
|
||||
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
|
||||
h.setNoSSH(true);
|
||||
h.setSilent(true);
|
||||
h.addParameters(parameters);
|
||||
h.addParameters("--max-count=1", "--pretty=%H", "--encoding=UTF-8", anyReference, "--");
|
||||
if (paths != null && ! paths.isEmpty()) {
|
||||
h.addRelativeFiles(paths);
|
||||
}
|
||||
try {
|
||||
final String output = h.run().trim();
|
||||
if (StringUtil.isEmptyOrSpaces(output)) return null;
|
||||
|
||||
@@ -166,7 +166,7 @@ public class GitHistoryProvider implements VcsHistoryProvider, VcsCacheableHisto
|
||||
final VirtualFile root = GitUtil.getGitRoot(filePath);
|
||||
if (root == null) return false;
|
||||
|
||||
final SHAHash shaHash = GitChangeUtils.commitExists(myProject, root, beforeVersionId, "--all");
|
||||
final SHAHash shaHash = GitChangeUtils.commitExists(myProject, root, beforeVersionId, null, "--all");
|
||||
if (shaHash == null) {
|
||||
throw new VcsException("Can not apply patch to " + filePath.getPath() + ".\nCan not find revision '" + beforeVersionId + "'.");
|
||||
}
|
||||
|
||||
@@ -410,11 +410,11 @@ public class GitHistoryUtils {
|
||||
}
|
||||
|
||||
public static void historyWithLinks(final Project project,
|
||||
FilePath path,
|
||||
final SymbolicRefs refs,
|
||||
final AsynchConsumer<GitCommit> gitCommitConsumer,
|
||||
final Getter<Boolean> isCanceled,
|
||||
final String... parameters) throws VcsException {
|
||||
FilePath path,
|
||||
final SymbolicRefs refs,
|
||||
final AsynchConsumer<GitCommit> gitCommitConsumer,
|
||||
final Getter<Boolean> isCanceled,
|
||||
Collection<VirtualFile> paths, final String... parameters) throws VcsException {
|
||||
// adjust path using change manager
|
||||
path = getLastCommitName(project, path);
|
||||
final VirtualFile root = GitUtil.getGitRoot(path);
|
||||
@@ -425,9 +425,14 @@ public class GitHistoryUtils {
|
||||
h.setStdoutSuppressed(true);
|
||||
h.addParameters(parameters);
|
||||
parser.parseStatusBeforeName(true);
|
||||
h.addParameters("--name-status", parser.getPretty(), "--encoding=UTF-8", "--full-history", "--sparse");
|
||||
h.addParameters("--name-status", parser.getPretty(), "--encoding=UTF-8", "--full-history");
|
||||
h.endOptions();
|
||||
h.addRelativePaths(path);
|
||||
if (paths != null && ! paths.isEmpty()) {
|
||||
h.addRelativeFiles(paths);
|
||||
} else {
|
||||
h.addRelativePaths(path);
|
||||
h.addParameters("--sparse");
|
||||
}
|
||||
|
||||
final VcsException[] exc = new VcsException[1];
|
||||
final Semaphore semaphore = new Semaphore();
|
||||
@@ -609,7 +614,7 @@ public class GitHistoryUtils {
|
||||
|
||||
public static void hashesWithParents(Project project, FilePath path, final AsynchConsumer<CommitHashPlusParents> consumer,
|
||||
final Getter<Boolean> isCanceled,
|
||||
final String... parameters) throws VcsException {
|
||||
Collection<VirtualFile> paths, final String... parameters) throws VcsException {
|
||||
// adjust path using change manager
|
||||
path = getLastCommitName(project, path);
|
||||
final VirtualFile root = GitUtil.getGitRoot(path);
|
||||
@@ -619,10 +624,15 @@ public class GitHistoryUtils {
|
||||
h.setNoSSH(true);
|
||||
h.setStdoutSuppressed(true);
|
||||
h.addParameters(parameters);
|
||||
h.addParameters(parser.getPretty(), "--encoding=UTF-8", "--full-history", "--sparse");
|
||||
h.addParameters(parser.getPretty(), "--encoding=UTF-8", "--full-history");
|
||||
|
||||
h.endOptions();
|
||||
h.addRelativePaths(path);
|
||||
if (paths != null && ! paths.isEmpty()) {
|
||||
h.addRelativeFiles(paths);
|
||||
} else {
|
||||
h.addParameters("--sparse");
|
||||
h.addRelativePaths(path);
|
||||
}
|
||||
|
||||
final Semaphore semaphore = new Semaphore();
|
||||
h.addLineListener(new GitLineHandlerListener() {
|
||||
|
||||
@@ -19,9 +19,7 @@ import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vcs.AreaMap;
|
||||
import com.intellij.openapi.vcs.FilePath;
|
||||
import com.intellij.openapi.vcs.changes.FilePathsHelper;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.PairProcessor;
|
||||
import git4idea.GitUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -32,21 +30,13 @@ import java.util.regex.Pattern;
|
||||
|
||||
public class ChangesFilter {
|
||||
|
||||
public static void filtersToParameters(Collection<Filter> filters, List<String> parameters) {
|
||||
public static void filtersToParameters(Collection<Filter> filters, List<String> parameters, Collection<VirtualFile> paths) {
|
||||
for (Filter filter : filters) {
|
||||
filter.getCommandParametersFilter().applyToCommandLine(parameters);
|
||||
filter.getCommandParametersFilter().applyToPaths(paths);
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] filtersToParameterArray(Collection<Filter> filters) {
|
||||
if (filters == null || filters.isEmpty()) return ArrayUtil.EMPTY_STRING_ARRAY;
|
||||
final ArrayList<String> strings = new ArrayList<String>();
|
||||
for (Filter filter : filters) {
|
||||
filter.getCommandParametersFilter().applyToCommandLine(strings);
|
||||
}
|
||||
return ArrayUtil.toStringArray(strings);
|
||||
}
|
||||
|
||||
public abstract static class Merger {
|
||||
private final Collection<MemoryFilter> myFilters;
|
||||
private MemoryFilter myResult;
|
||||
@@ -141,6 +131,7 @@ public class ChangesFilter {
|
||||
|
||||
public interface CommandParametersFilter {
|
||||
void applyToCommandLine(final List<String> sink);
|
||||
void applyToPaths(Collection<VirtualFile> paths);
|
||||
}
|
||||
|
||||
public interface Filter {
|
||||
@@ -163,6 +154,10 @@ public class ChangesFilter {
|
||||
public void applyToCommandLine(List<String> sink) {
|
||||
sink.add("--author=" + myRegexp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToPaths(Collection<VirtualFile> paths) {
|
||||
}
|
||||
};
|
||||
myMemoryFilter = new MemoryFilter() {
|
||||
public boolean applyInMemory(GitCommit commit) {
|
||||
@@ -211,6 +206,10 @@ public class ChangesFilter {
|
||||
public void applyToCommandLine(List<String> sink) {
|
||||
sink.add("--committer=" + myRegexp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToPaths(Collection<VirtualFile> paths) {
|
||||
}
|
||||
};
|
||||
myMemoryFilter = new MemoryFilter() {
|
||||
public boolean applyInMemory(GitCommit commit) {
|
||||
@@ -257,6 +256,10 @@ public class ChangesFilter {
|
||||
public void applyToCommandLine(List<String> sink) {
|
||||
sink.add("--before=" + formatDate(myDate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToPaths(Collection<VirtualFile> paths) {
|
||||
}
|
||||
};
|
||||
myMemoryFilter = new MemoryFilter() {
|
||||
public boolean applyInMemory(GitCommit commit) {
|
||||
@@ -303,6 +306,10 @@ public class ChangesFilter {
|
||||
public void applyToCommandLine(List<String> sink) {
|
||||
sink.add("--after=" + formatDate(myDate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToPaths(Collection<VirtualFile> paths) {
|
||||
}
|
||||
};
|
||||
myMemoryFilter = new MemoryFilter() {
|
||||
public boolean applyInMemory(GitCommit commit) {
|
||||
@@ -374,8 +381,13 @@ public class ChangesFilter {
|
||||
};
|
||||
}
|
||||
|
||||
// todo optimization here
|
||||
public boolean addPath(final VirtualFile vf) {
|
||||
public void addFiles(final Collection<VirtualFile> files) {
|
||||
for (VirtualFile file : files) {
|
||||
myMap.put(FilePathsHelper.convertWithLastSeparator(file), file);
|
||||
}
|
||||
}
|
||||
|
||||
/*public boolean addPath(final VirtualFile vf) {
|
||||
final Collection<VirtualFile> filesWeAlreadyHave = myMap.values();
|
||||
final Collection<VirtualFile> childrenToRemove = new ArrayList<VirtualFile>();
|
||||
for (VirtualFile current : filesWeAlreadyHave) {
|
||||
@@ -396,7 +408,7 @@ public class ChangesFilter {
|
||||
|
||||
myMap.put(FilePathsHelper.convertWithLastSeparator(vf), vf);
|
||||
return true;
|
||||
}
|
||||
} */
|
||||
|
||||
public boolean containsFile(final VirtualFile vf) {
|
||||
return myMap.contains(FilePathsHelper.convertWithLastSeparator(vf));
|
||||
@@ -412,7 +424,16 @@ public class ChangesFilter {
|
||||
|
||||
// can be applied only in memory
|
||||
public CommandParametersFilter getCommandParametersFilter() {
|
||||
return null;
|
||||
return new CommandParametersFilter() {
|
||||
@Override
|
||||
public void applyToCommandLine(List<String> sink) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToPaths(Collection<VirtualFile> paths) {
|
||||
paths.addAll(myMap.values());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -435,6 +456,10 @@ public class ChangesFilter {
|
||||
sink.add("--grep=" + myRegexp);
|
||||
sink.add("--regexp-ignore-case");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToPaths(Collection<VirtualFile> paths) {
|
||||
}
|
||||
};
|
||||
myMemoryFilter = new MemoryFilter() {
|
||||
public boolean applyInMemory(GitCommit commit) {
|
||||
|
||||
@@ -67,7 +67,8 @@ public class LowLevelAccessImpl implements LowLevelAccess {
|
||||
final AsynchConsumer<CommitHashPlusParents> consumer,
|
||||
Getter<Boolean> isCanceled, int useMaxCnt) throws VcsException {
|
||||
final List<String> parameters = new ArrayList<String>();
|
||||
ChangesFilter.filtersToParameters(filters, parameters);
|
||||
final Collection<VirtualFile> paths = new HashSet<VirtualFile>();
|
||||
ChangesFilter.filtersToParameters(filters, parameters, paths);
|
||||
|
||||
if (! startingPoints.isEmpty()) {
|
||||
for (String startingPoint : startingPoints) {
|
||||
@@ -80,7 +81,7 @@ public class LowLevelAccessImpl implements LowLevelAccess {
|
||||
parameters.add("--max-count=" + useMaxCnt);
|
||||
}
|
||||
|
||||
GitHistoryUtils.hashesWithParents(myProject, new FilePathImpl(myRoot), consumer, isCanceled, ArrayUtil.toStringArray(parameters));
|
||||
GitHistoryUtils.hashesWithParents(myProject, new FilePathImpl(myRoot), consumer, isCanceled, paths, ArrayUtil.toStringArray(parameters));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,7 +144,8 @@ public class LowLevelAccessImpl implements LowLevelAccess {
|
||||
parameters.add("--max-count=" + useMaxCnt);
|
||||
}
|
||||
|
||||
ChangesFilter.filtersToParameters(filters, parameters);
|
||||
final Collection<VirtualFile> paths = new HashSet<VirtualFile>();
|
||||
ChangesFilter.filtersToParameters(filters, parameters, paths);
|
||||
|
||||
if (! startingPoints.isEmpty()) {
|
||||
for (String startingPoint : startingPoints) {
|
||||
@@ -158,7 +160,7 @@ public class LowLevelAccessImpl implements LowLevelAccess {
|
||||
}
|
||||
|
||||
GitHistoryUtils.historyWithLinks(myProject, new FilePathImpl(myRoot),
|
||||
refs, consumer, isCanceled, ArrayUtil.toStringArray(parameters));
|
||||
refs, consumer, isCanceled, paths, ArrayUtil.toStringArray(parameters));
|
||||
}
|
||||
|
||||
public List<String> getBranchesWithCommit(final SHAHash hash) throws VcsException {
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.continuation.ContinuationContext;
|
||||
import com.intellij.util.continuation.TaskDescriptor;
|
||||
@@ -100,8 +101,11 @@ public class ByRootLoader extends TaskDescriptor {
|
||||
public void consume(List<ChangesFilter.Filter> filters) {
|
||||
ProgressManager.checkCanceled();
|
||||
try {
|
||||
final List<String> parameters = new ArrayList<String>();
|
||||
final List<VirtualFile> paths = new ArrayList<VirtualFile>();
|
||||
ChangesFilter.filtersToParameters(filters, parameters, paths);
|
||||
final List<Pair<String,GitCommit>> stash = GitHistoryUtils.loadStashStackAsCommits(myProject, myRootHolder.getRoot(),
|
||||
mySymbolicRefs, ChangesFilter.filtersToParameterArray(filters));
|
||||
mySymbolicRefs, parameters.toArray(new String[parameters.size()]));
|
||||
if (stash == null) return;
|
||||
for (Pair<String, GitCommit> pair : stash) {
|
||||
ProgressManager.checkCanceled();
|
||||
@@ -120,7 +124,7 @@ public class ByRootLoader extends TaskDescriptor {
|
||||
myMediator.acceptException(e);
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
}, true, myRootHolder.getRoot());
|
||||
|
||||
myDetailsCache.putStash(myRootHolder.getRoot(), stashMap);
|
||||
ProgressManager.checkCanceled();
|
||||
@@ -141,7 +145,11 @@ public class ByRootLoader extends TaskDescriptor {
|
||||
public void consume(List<ChangesFilter.Filter> filters) {
|
||||
for (String hash : hashes) {
|
||||
try {
|
||||
final SHAHash shaHash = GitChangeUtils.commitExists(myProject, myRootHolder.getRoot(), hash, ChangesFilter.filtersToParameterArray(filters));
|
||||
final List<String> parameters = new ArrayList<String>();
|
||||
final List<VirtualFile> paths = new ArrayList<VirtualFile>();
|
||||
ChangesFilter.filtersToParameters(filters, parameters, paths);
|
||||
final SHAHash shaHash = GitChangeUtils.commitExists(myProject, myRootHolder.getRoot(), hash, paths,
|
||||
parameters.toArray(new String[parameters.size()]));
|
||||
if (shaHash == null) continue;
|
||||
if (controlSet.contains(shaHash)) continue;
|
||||
controlSet.add(shaHash);
|
||||
@@ -167,7 +175,7 @@ public class ByRootLoader extends TaskDescriptor {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}, false, myRootHolder.getRoot());
|
||||
|
||||
if (! result.isEmpty()) {
|
||||
final StepType stepType = myMediator.appendResult(myTicket, result, null);
|
||||
|
||||
@@ -16,14 +16,12 @@
|
||||
package git4idea.history.wholeTree;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Consumer;
|
||||
import git4idea.history.browser.ChangesFilter;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
@@ -36,7 +34,7 @@ public class GitLogFilters {
|
||||
@Nullable
|
||||
private final Set<ChangesFilter.Filter> myCommitterFilters;
|
||||
@Nullable
|
||||
private final Set<ChangesFilter.Filter> myStructureFilters;
|
||||
private final Map<VirtualFile, ChangesFilter.Filter> myStructureFilters;
|
||||
@Nullable
|
||||
private final List<String> myPossibleReferencies;
|
||||
|
||||
@@ -46,14 +44,14 @@ public class GitLogFilters {
|
||||
|
||||
public GitLogFilters(@Nullable ChangesFilter.Comment commentFilter,
|
||||
@Nullable Set<ChangesFilter.Filter> committerFilters,
|
||||
@Nullable Set<ChangesFilter.Filter> structureFilters, @Nullable List<String> possibleReferencies) {
|
||||
@Nullable Map<VirtualFile, ChangesFilter.Filter> structureFilters, @Nullable List<String> possibleReferencies) {
|
||||
myCommentFilter = commentFilter;
|
||||
myCommitterFilters = committerFilters;
|
||||
myStructureFilters = structureFilters;
|
||||
myPossibleReferencies = possibleReferencies;
|
||||
}
|
||||
|
||||
public void callConsumer(final Consumer<List<ChangesFilter.Filter>> consumer, boolean takeComment) {
|
||||
public void callConsumer(final Consumer<List<ChangesFilter.Filter>> consumer, boolean takeComment, final VirtualFile root) {
|
||||
final List<Set<ChangesFilter.Filter>> filters = new ArrayList<Set<ChangesFilter.Filter>>();
|
||||
if (takeComment && myCommentFilter != null) {
|
||||
filters.add(Collections.<ChangesFilter.Filter, ChangesFilter.Filter>singletonMap(myCommentFilter, myCommentFilter).keySet());
|
||||
@@ -62,7 +60,10 @@ public class GitLogFilters {
|
||||
filters.add(myCommitterFilters);
|
||||
}
|
||||
if (myStructureFilters != null) {
|
||||
filters.add(myStructureFilters);
|
||||
final ChangesFilter.Filter filter = myStructureFilters.get(root);
|
||||
if (filter != null) {
|
||||
filters.add(Collections.singleton(filter));
|
||||
}
|
||||
}
|
||||
final Set<List<ChangesFilter.Filter>> cartesian = Sets.cartesianProduct(filters);
|
||||
if (cartesian.isEmpty()) {
|
||||
@@ -85,7 +86,7 @@ public class GitLogFilters {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Set<ChangesFilter.Filter> getStructureFilters() {
|
||||
public Map<VirtualFile,ChangesFilter.Filter> getStructureFilters() {
|
||||
return myStructureFilters;
|
||||
}
|
||||
|
||||
@@ -98,4 +99,12 @@ public class GitLogFilters {
|
||||
public List<String> getPossibleReferencies() {
|
||||
return myPossibleReferencies;
|
||||
}
|
||||
|
||||
public boolean haveStructureFilter() {
|
||||
return myStructureFilters != null;
|
||||
}
|
||||
|
||||
public boolean haveStructuresForRoot(VirtualFile root) {
|
||||
return haveStructureFilter() && myStructureFilters.containsKey(root);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator;
|
||||
import com.intellij.openapi.ide.CopyPasteManager;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -36,6 +37,7 @@ import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
|
||||
import com.intellij.openapi.vcs.changes.issueLinks.TableLinkMouseListener;
|
||||
import com.intellij.openapi.vcs.ui.SearchFieldAction;
|
||||
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.ColoredTableCellRenderer;
|
||||
import com.intellij.ui.PopupHandler;
|
||||
@@ -45,6 +47,7 @@ import com.intellij.ui.table.JBTable;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.PairConsumer;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.text.DateFormatUtil;
|
||||
@@ -107,6 +110,8 @@ public class GitLogUI implements Disposable {
|
||||
private MyFilterUi myUserFilterI;
|
||||
private MyCherryPick myCherryPickAction;
|
||||
private MyRefreshAction myRefreshAction;
|
||||
private MyStructureFilter myStructureFilter;
|
||||
private StructureFilterAction myStructureFilterAction;
|
||||
private AnAction myCopyHashAction;
|
||||
// todo group somewhere??
|
||||
private Consumer<CommitI> myDetailsLoaderImpl;
|
||||
@@ -603,6 +608,7 @@ public class GitLogUI implements Disposable {
|
||||
}
|
||||
group.add(myBranchSelectorAction.asTextAction());
|
||||
group.add(myUsersFilterAction.asTextAction());
|
||||
group.add(myStructureFilterAction.asTextAction());
|
||||
group.add(myCherryPickAction);
|
||||
group.add(ActionManager.getInstance().getAction("ChangesView.CreatePatchFromChanges"));
|
||||
group.add(myRefreshAction);
|
||||
@@ -618,16 +624,20 @@ public class GitLogUI implements Disposable {
|
||||
reloadRequest();
|
||||
}
|
||||
});
|
||||
myUserFilterI = new MyFilterUi(new Runnable() {
|
||||
final Runnable reloadCallback = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
reloadRequest();
|
||||
}
|
||||
});
|
||||
};
|
||||
myUserFilterI = new MyFilterUi(reloadCallback);
|
||||
myUsersFilterAction = new UsersFilterAction(myProject, myUserFilterI);
|
||||
group.add(new MyTextFieldAction());
|
||||
group.add(myBranchSelectorAction);
|
||||
group.add(myUsersFilterAction);
|
||||
myStructureFilter = new MyStructureFilter(reloadCallback);
|
||||
myStructureFilterAction = new StructureFilterAction(myProject, myStructureFilter);
|
||||
group.add(myStructureFilterAction);
|
||||
myCherryPickAction = new MyCherryPick();
|
||||
group.add(myCherryPickAction);
|
||||
group.add(ActionManager.getInstance().getAction("ChangesView.CreatePatchFromChanges"));
|
||||
@@ -1118,7 +1128,7 @@ public class GitLogUI implements Disposable {
|
||||
myCommentSearchContext.clear();
|
||||
myUsersSearchContext.clear();
|
||||
|
||||
if (commentFilterEmpty && (myUserFilterI.myFilter == null)) {
|
||||
if (commentFilterEmpty && (myUserFilterI.myFilter == null) && myStructureFilter.myAllSelected) {
|
||||
myUsersSearchContext.clear();
|
||||
myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, new GitLogFilters());
|
||||
} else {
|
||||
@@ -1140,9 +1150,33 @@ public class GitLogUI implements Disposable {
|
||||
userFilters.add(new ChangesFilter.Author(regexp));
|
||||
}
|
||||
}
|
||||
Map<VirtualFile, ChangesFilter.Filter> structureFilters = null;
|
||||
if (! myStructureFilter.myAllSelected) {
|
||||
structureFilters = new HashMap<VirtualFile, ChangesFilter.Filter>();
|
||||
final Collection<VirtualFile> selected = new ArrayList<VirtualFile>(myStructureFilter.getSelected());
|
||||
final ArrayList<VirtualFile> copy = new ArrayList<VirtualFile>(myRootsUnderVcs);
|
||||
Collections.sort(copy, FilePathComparator.getInstance());
|
||||
Collections.reverse(copy);
|
||||
for (VirtualFile root : copy) {
|
||||
final Collection<VirtualFile> selectedForRoot = new SmartList<VirtualFile>();
|
||||
final Iterator<VirtualFile> iterator = selected.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
VirtualFile next = iterator.next();
|
||||
if (VfsUtil.isAncestor(root, next, false)) {
|
||||
selectedForRoot.add(next);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
if (! selectedForRoot.isEmpty()) {
|
||||
final ChangesFilter.StructureFilter structureFilter = new ChangesFilter.StructureFilter();
|
||||
structureFilter.addFiles(selectedForRoot);
|
||||
structureFilters.put(root, structureFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List<String> possibleReferencies = commentFilterEmpty ? null : Arrays.asList(myPreviousFilter.split("[\\s]"));
|
||||
myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, new GitLogFilters(comment, userFilters, null,
|
||||
myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, new GitLogFilters(comment, userFilters, structureFilters,
|
||||
possibleReferencies));
|
||||
}
|
||||
myCommentSearchContext.addHighlighter(myDetailsPanel.getHtmlHighlighter());
|
||||
@@ -1302,4 +1336,37 @@ public class GitLogUI implements Disposable {
|
||||
myMe = me == null ? "" : me.trim();
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyStructureFilter implements StructureFilterI {
|
||||
private boolean myAllSelected;
|
||||
private final List<VirtualFile> myFiles;
|
||||
private final Runnable myReloadCallback;
|
||||
|
||||
private MyStructureFilter(Runnable reloadCallback) {
|
||||
myReloadCallback = reloadCallback;
|
||||
myFiles = new ArrayList<VirtualFile>();
|
||||
myAllSelected = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void allSelected() {
|
||||
if (myAllSelected) return;
|
||||
myAllSelected = true;
|
||||
myReloadCallback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void select(Collection<VirtualFile> files) {
|
||||
myAllSelected = false;
|
||||
if (Comparing.haveEqualElements(files, myFiles)) return;
|
||||
myFiles.clear();
|
||||
myFiles.addAll(files);
|
||||
myReloadCallback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<VirtualFile> getSelected() {
|
||||
return myFiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,15 +65,21 @@ public class LoadController implements Loader {
|
||||
new LoaderAndRefresherImpl.OneRootHolder(root) :
|
||||
new LoaderAndRefresherImpl.ManyCaseHolder(i, rootsHolder);
|
||||
|
||||
final boolean haveStructureFilter = filters.haveStructureFilter();
|
||||
// check if no files under root are selected
|
||||
if (haveStructureFilter && ! filters.haveStructuresForRoot(root)) {
|
||||
++ i;
|
||||
continue;
|
||||
}
|
||||
filters.callConsumer(new Consumer<List<ChangesFilter.Filter>>() {
|
||||
@Override
|
||||
public void consume(final List<ChangesFilter.Filter> filters) {
|
||||
final LoaderAndRefresherImpl loaderAndRefresher =
|
||||
new LoaderAndRefresherImpl(ticket, filters, myMediator, startingPoints, myDetailsCache, myProject, rootHolder, myUsersIndex,
|
||||
loadGrowthController.getId());
|
||||
loadGrowthController.getId(), haveStructureFilter);
|
||||
list.add(loaderAndRefresher);
|
||||
}
|
||||
}, true);
|
||||
}, true, root);
|
||||
|
||||
shortLoaders.add(new ByRootLoader(myProject, rootHolder, myMediator, myDetailsCache, ticket, myUsersIndex, filters, startingPoints));
|
||||
++ i;
|
||||
|
||||
@@ -12,13 +12,10 @@
|
||||
*/
|
||||
package git4idea.history.wholeTree;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
*/
|
||||
public interface LoaderAndRefresher<T> {
|
||||
void loadByHashesAside(final List<String> hashes);
|
||||
LoadAlgorithm.Result<T> load(final LoadAlgorithm.LoadType loadType, long continuation);
|
||||
StepType flushIntoUI();
|
||||
void interrupt();
|
||||
|
||||
@@ -23,8 +23,10 @@ import com.intellij.util.BufferedListConsumer;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import git4idea.GitBranch;
|
||||
import git4idea.changes.GitChangeUtils;
|
||||
import git4idea.history.browser.*;
|
||||
import git4idea.history.browser.ChangesFilter;
|
||||
import git4idea.history.browser.GitCommit;
|
||||
import git4idea.history.browser.LowLevelAccessImpl;
|
||||
import git4idea.history.browser.SymbolicRefs;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
@@ -53,6 +55,7 @@ public class LoaderAndRefresherImpl implements LoaderAndRefresher<CommitHashPlus
|
||||
private LowLevelAccessImpl myLowLevelAccess;
|
||||
private SymbolicRefs mySymbolicRefs;
|
||||
private final LoadGrowthController.ID myId;
|
||||
private final boolean myHaveStructureFilter;
|
||||
// state
|
||||
@NotNull
|
||||
private volatile StepType myStepType;
|
||||
@@ -69,10 +72,11 @@ public class LoaderAndRefresherImpl implements LoaderAndRefresher<CommitHashPlus
|
||||
Project project,
|
||||
MyRootHolder rootHolder,
|
||||
final UsersIndex usersIndex,
|
||||
final LoadGrowthController.ID id) {
|
||||
final LoadGrowthController.ID id, boolean haveStructureFilter) {
|
||||
myRootHolder = rootHolder;
|
||||
myUsersIndex = usersIndex;
|
||||
myId = id;
|
||||
myHaveStructureFilter = haveStructureFilter;
|
||||
myLoadParents = filters == null || filters.isEmpty();
|
||||
myTicket = ticket;
|
||||
myFilters = filters;
|
||||
@@ -142,7 +146,7 @@ public class LoaderAndRefresherImpl implements LoaderAndRefresher<CommitHashPlus
|
||||
|
||||
myRepeatingLoadConsumer.reset();
|
||||
int count = MediatorImpl.ourManyLoadedStep;
|
||||
boolean shouldFull = true;
|
||||
boolean shouldFull = ! myHaveStructureFilter;
|
||||
if (LoadAlgorithm.LoadType.TEST.equals(loadType)) {
|
||||
count = ourFirstLoadCount;
|
||||
} else if (LoadAlgorithm.LoadType.SHORT.equals(loadType) || LoadAlgorithm.LoadType.SHORT_START.equals(loadType)) {
|
||||
@@ -251,30 +255,6 @@ public class LoaderAndRefresherImpl implements LoaderAndRefresher<CommitHashPlus
|
||||
return filters;
|
||||
}
|
||||
|
||||
public void loadByHashesAside(final List<String> hashes) {
|
||||
final List<CommitI> result = new ArrayList<CommitI>();
|
||||
final List<List<AbstractHash>> parents = myLoadParents ? new ArrayList<List<AbstractHash>>() : null;
|
||||
for (String hash : hashes) {
|
||||
try {
|
||||
final SHAHash shaHash = GitChangeUtils.commitExists(myProject, myRootHolder.getRoot(), hash);
|
||||
if (shaHash == null) continue;
|
||||
final List<GitCommit> commits = myLowLevelAccess.getCommitDetails(Collections.singletonList(shaHash.getValue()), mySymbolicRefs);
|
||||
myDetailsCache.acceptAnswer(commits, myRootHolder.getRoot());
|
||||
appendCommits(result, parents, commits);
|
||||
}
|
||||
catch (VcsException e1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (! result.isEmpty()) {
|
||||
final StepType stepType = myMediator.appendResult(myTicket, result, parents);
|
||||
// here we react only on "stop", not on "pause"
|
||||
if (StepType.STOP.equals(stepType)) {
|
||||
myStepType = StepType.STOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void appendCommits(List<CommitI> result, List<List<AbstractHash>> parents, List<GitCommit> commits) {
|
||||
for (GitCommit commit : commits) {
|
||||
final Commit commitObj =
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package git4idea.history.wholeTree;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Consumer;
|
||||
import git4idea.GitVcs;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
* Date: 2/3/11
|
||||
* Time: 4:29 PM
|
||||
*/
|
||||
public class StructureFilterAction extends BasePopupAction {
|
||||
public static final String ALL = "All";
|
||||
public static final String STRUCTURE = "Structure:";
|
||||
public static final String FILTER = "(filter)";
|
||||
private final DumbAwareAction myAll;
|
||||
private final DumbAwareAction mySelect;
|
||||
private final StructureFilterI myStructureFilterI;
|
||||
|
||||
public StructureFilterAction(Project project, final StructureFilterI structureFilterI) {
|
||||
super(project, STRUCTURE, "Structure");
|
||||
myStructureFilterI = structureFilterI;
|
||||
myAll = new DumbAwareAction(ALL) {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
myLabel.setText(ALL);
|
||||
myPanel.setToolTipText(STRUCTURE + " " + ALL);
|
||||
structureFilterI.allSelected();
|
||||
}
|
||||
};
|
||||
mySelect = new DumbAwareAction("Select...") {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
final VcsStructureChooser vcsStructureChooser =
|
||||
new VcsStructureChooser(GitVcs.getInstance(myProject), "Select folders to filter by", structureFilterI.getSelected());
|
||||
vcsStructureChooser.show();
|
||||
if (vcsStructureChooser.getExitCode() == DialogWrapper.CANCEL_EXIT_CODE) return;
|
||||
final Collection<VirtualFile> files = vcsStructureChooser.getSelectedFiles();
|
||||
final Map<VirtualFile,String> modulesSet = vcsStructureChooser.getModulesSet();
|
||||
String text;
|
||||
if (files.size() == 1) {
|
||||
final VirtualFile file = files.iterator().next();
|
||||
final String module = modulesSet.get(file);
|
||||
text = module == null ? file.getName() : module;
|
||||
}
|
||||
else {
|
||||
text = FILTER;
|
||||
}
|
||||
text = text.length() > 20 ? FILTER : text;
|
||||
myLabel.setText(text);
|
||||
|
||||
final String toolTip;
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (VirtualFile file : files) {
|
||||
sb.append("<br><b>");
|
||||
final String module = modulesSet.get(file);
|
||||
final String name = module == null ? file.getName() : module;
|
||||
sb.append(name).append("</b> (").append(file.getPath()).append(")");
|
||||
}
|
||||
toolTip = sb.toString();
|
||||
myPanel.setToolTipText("<html><b>" + STRUCTURE + "</b><br>" + toolTip + "</html>");
|
||||
structureFilterI.select(files);
|
||||
}
|
||||
};
|
||||
myLabel.setText(ALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createActions(Consumer<AnAction> actionConsumer) {
|
||||
actionConsumer.consume(myAll);
|
||||
actionConsumer.consume(mySelect);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package git4idea.history.wholeTree;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
* Date: 7/8/11
|
||||
* Time: 1:49 PM
|
||||
*/
|
||||
public interface StructureFilterI {
|
||||
void allSelected();
|
||||
void select(final Collection<VirtualFile> files);
|
||||
Collection<VirtualFile> getSelected();
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package git4idea.history.wholeTree;
|
||||
|
||||
import com.intellij.ide.util.treeView.AbstractTreeUi;
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diff.impl.patch.formove.FilePathComparator;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileChooser.ex.FileNodeDescriptor;
|
||||
import com.intellij.openapi.fileChooser.ex.FileSystemTreeImpl;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.Splitter;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.vcs.AbstractVcs;
|
||||
import com.intellij.openapi.vcs.FilePath;
|
||||
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
|
||||
import com.intellij.openapi.vcs.changes.ui.VirtualFileListCellRenderer;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.PlusMinus;
|
||||
import com.intellij.util.TreeNodeState;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.intellij.util.containers.hash.HashSet;
|
||||
import com.intellij.util.treeWithCheckedNodes.SelectionManager;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreeCellRenderer;
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
* Date: 2/3/11
|
||||
* Time: 12:04 PM
|
||||
*/
|
||||
public class VcsStructureChooser extends DialogWrapper {
|
||||
private final static int MAX_FOLDERS = 10;
|
||||
public static final Border BORDER = IdeBorderFactory.createBorder(SideBorder.TOP | SideBorder.LEFT);
|
||||
public static final String DEFAULT_TEXT = "<html>Selected:</html>";
|
||||
public static final String CAN_NOT_ADD_TEXT = "<html>Selected: <font color=red>(You have added " + MAX_FOLDERS + " elements. No more is allowed.)</font></html>";
|
||||
private final AbstractVcs myVcs;
|
||||
private Set<VirtualFile> myRoots;
|
||||
private Map<VirtualFile, String> myModulesSet;
|
||||
private SelectionManager mySelectionManager;
|
||||
private DefaultMutableTreeNode myRoot;
|
||||
private JBList mySelectedList;
|
||||
private JLabel mySelectedLabel;
|
||||
private Tree myTree;
|
||||
|
||||
public VcsStructureChooser(final AbstractVcs vcs, final String title, final Collection<VirtualFile> initialSelection) {
|
||||
super(vcs.getProject(), true);
|
||||
setTitle(title);
|
||||
myVcs = vcs;
|
||||
mySelectionManager = new SelectionManager(MAX_FOLDERS, 500, MyNodeConvertor.getInstance());
|
||||
init();
|
||||
mySelectionManager.setSelection(initialSelection);
|
||||
checkEmptyness();
|
||||
}
|
||||
|
||||
// todo background?
|
||||
private void calculateRoots() {
|
||||
final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myVcs.getProject());
|
||||
final VirtualFile[] rootsUnderVcs = vcsManager.getRootsUnderVcs(myVcs);
|
||||
|
||||
final ModuleManager moduleManager = ModuleManager.getInstance(myVcs.getProject());
|
||||
// assertion for read access inside
|
||||
final Module[] modules = ApplicationManager.getApplication().runReadAction(new Computable<Module[]>() {
|
||||
public Module[] compute() {
|
||||
return moduleManager.getModules();
|
||||
}
|
||||
});
|
||||
|
||||
myRoots = new HashSet<VirtualFile>();
|
||||
myRoots.addAll(Arrays.asList(rootsUnderVcs));
|
||||
myModulesSet = new HashMap<VirtualFile, String>();
|
||||
for (Module module : modules) {
|
||||
final VirtualFile[] files = ModuleRootManager.getInstance(module).getContentRoots();
|
||||
for (VirtualFile file : files) {
|
||||
if (myVcs.equals(vcsManager.getVcsFor(file))) {
|
||||
myModulesSet.put(file, module.getName());
|
||||
myRoots.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<VirtualFile, String> getModulesSet() {
|
||||
return myModulesSet;
|
||||
}
|
||||
|
||||
public Collection<VirtualFile> getSelectedFiles() {
|
||||
return ((CollectionListModel) mySelectedList.getModel()).getItems();
|
||||
}
|
||||
|
||||
private void checkEmptyness() {
|
||||
setOKActionEnabled(mySelectedList.getModel().getSize() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDimensionServiceKey() {
|
||||
return "git4idea.history.wholeTree.VcsStructureChooser";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myTree;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, true, true, true, false, true);
|
||||
calculateRoots();
|
||||
final ArrayList<VirtualFile> list = new ArrayList<VirtualFile>(myRoots);
|
||||
final Comparator<VirtualFile> comparator = new Comparator<VirtualFile>() {
|
||||
@Override
|
||||
public int compare(VirtualFile o1, VirtualFile o2) {
|
||||
final String module1 = myModulesSet.get(o1);
|
||||
final String path1 = module1 != null ? module1 : o1.getPath();
|
||||
final String module2 = myModulesSet.get(o2);
|
||||
final String path2 = module2 != null ? module2 : o2.getPath();
|
||||
return path1.compareToIgnoreCase(path2);
|
||||
}
|
||||
};
|
||||
for (VirtualFile root : list) {
|
||||
descriptor.addRoot(root);
|
||||
}
|
||||
myTree = new Tree();
|
||||
myTree.setMinimumSize(new Dimension(200, 200));
|
||||
myTree.setBorder(BORDER);
|
||||
myTree.setShowsRootHandles(true);
|
||||
myTree.setRootVisible(true);
|
||||
final MyCheckboxTreeCellRenderer cellRenderer = new MyCheckboxTreeCellRenderer(mySelectionManager, myModulesSet, myVcs.getProject(),
|
||||
myTree, myRoots);
|
||||
final FileSystemTreeImpl fileSystemTree = new FileSystemTreeImpl(myVcs.getProject(), descriptor, myTree, cellRenderer, null, new Convertor<TreePath, String>() {
|
||||
@Override
|
||||
public String convert(TreePath o) {
|
||||
final DefaultMutableTreeNode lastPathComponent = ((DefaultMutableTreeNode) o.getLastPathComponent());
|
||||
final Object uo = lastPathComponent.getUserObject();
|
||||
if (uo instanceof FileNodeDescriptor) {
|
||||
final VirtualFile file = ((FileNodeDescriptor)uo).getElement().getFile();
|
||||
final String module = myModulesSet.get(file);
|
||||
if (module != null) return module;
|
||||
return file == null ? "" : file.getName();
|
||||
}
|
||||
return o.toString();
|
||||
}
|
||||
});
|
||||
final AbstractTreeUi ui = fileSystemTree.getTreeBuilder().getUi();
|
||||
ui.setNodeDescriptorComparator(new Comparator<NodeDescriptor>() {
|
||||
@Override
|
||||
public int compare(NodeDescriptor o1, NodeDescriptor o2) {
|
||||
if (o1 instanceof FileNodeDescriptor && o2 instanceof FileNodeDescriptor) {
|
||||
final VirtualFile f1 = ((FileNodeDescriptor)o1).getElement().getFile();
|
||||
final VirtualFile f2 = ((FileNodeDescriptor)o2).getElement().getFile();
|
||||
return comparator.compare(f1, f2);
|
||||
}
|
||||
return o1.getIndex() - o2.getIndex();
|
||||
}
|
||||
});
|
||||
myRoot = (DefaultMutableTreeNode)myTree.getModel().getRoot();
|
||||
|
||||
myTree.addMouseListener(new MouseAdapter() {
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
int row = myTree.getRowForLocation(e.getX(), e.getY());
|
||||
if (row < 0) return;
|
||||
final Object o = myTree.getPathForRow(row).getLastPathComponent();
|
||||
if (myRoot == o || getFile(o) == null) return;
|
||||
|
||||
Rectangle rowBounds = myTree.getRowBounds(row);
|
||||
cellRenderer.setBounds(rowBounds);
|
||||
Rectangle checkBounds = cellRenderer.myCheckbox.getBounds();
|
||||
checkBounds.setLocation(rowBounds.getLocation());
|
||||
|
||||
if (checkBounds.height == 0) checkBounds.height = rowBounds.height;
|
||||
|
||||
if (checkBounds.contains(e.getPoint())) {
|
||||
mySelectionManager.toggleSelection((DefaultMutableTreeNode)o);
|
||||
myTree.revalidate();
|
||||
myTree.repaint();
|
||||
e.consume();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
myTree.addKeyListener(new KeyAdapter() {
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
|
||||
TreePath treePath = myTree.getLeadSelectionPath();
|
||||
if (treePath == null) return;
|
||||
final Object o = treePath.getLastPathComponent();
|
||||
if (myRoot == o || getFile(o) == null) return;
|
||||
mySelectionManager.toggleSelection((DefaultMutableTreeNode)o);
|
||||
myTree.revalidate();
|
||||
myTree.repaint();
|
||||
e.consume();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final Splitter splitter = new Splitter(true, 0.7f);
|
||||
splitter.setFirstComponent(new JBScrollPane(fileSystemTree.getTree()));
|
||||
final JPanel wrapper = new JPanel(new BorderLayout());
|
||||
mySelectedLabel = new JLabel(DEFAULT_TEXT);
|
||||
mySelectedLabel.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0));
|
||||
wrapper.add(mySelectedLabel, BorderLayout.NORTH);
|
||||
mySelectedList = new JBList(new CollectionListModel(new ArrayList<VirtualFile>()));
|
||||
mySelectedList.setCellRenderer(new WithModulesListCellRenderer(myVcs.getProject(), myModulesSet));
|
||||
wrapper.add(ScrollPaneFactory.createScrollPane(mySelectedList), BorderLayout.CENTER);
|
||||
splitter.setSecondComponent(wrapper);
|
||||
|
||||
mySelectionManager.setSelectionChangeListener(new PlusMinus<VirtualFile>() {
|
||||
@Override
|
||||
public void plus(VirtualFile virtualFile) {
|
||||
final CollectionListModel model = (CollectionListModel)mySelectedList.getModel();
|
||||
model.add(virtualFile);
|
||||
model.sort(FilePathComparator.getInstance());
|
||||
recalculateErrorText();
|
||||
mySelectedList.revalidate();
|
||||
mySelectedList.repaint();
|
||||
}
|
||||
|
||||
private void recalculateErrorText() {
|
||||
checkEmptyness();
|
||||
if (mySelectionManager.canAddSelection()) {
|
||||
mySelectedLabel.setText(DEFAULT_TEXT);
|
||||
} else {
|
||||
mySelectedLabel.setText(CAN_NOT_ADD_TEXT);
|
||||
}
|
||||
mySelectedLabel.revalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void minus(VirtualFile virtualFile) {
|
||||
final CollectionListModel defaultListModel = (CollectionListModel)mySelectedList.getModel();
|
||||
for (int i = 0; i < defaultListModel.getSize(); i++) {
|
||||
final VirtualFile elementAt = (VirtualFile)defaultListModel.getElementAt(i);
|
||||
if (virtualFile.equals(elementAt)) {
|
||||
defaultListModel.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
defaultListModel.sort(FilePathComparator.getInstance());
|
||||
recalculateErrorText();
|
||||
mySelectedList.revalidate();
|
||||
mySelectedList.repaint();
|
||||
}
|
||||
});
|
||||
mySelectedList.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
if (e.getModifiers() == 0 && e.getKeyCode() == KeyEvent.VK_DELETE) {
|
||||
final int[] idx = mySelectedList.getSelectedIndices();
|
||||
if (idx != null && idx.length > 0) {
|
||||
final int answer = Messages
|
||||
.showYesNoDialog(myVcs.getProject(), "Remove selected paths from filter?", "Remove from filter", Messages.getQuestionIcon());
|
||||
if (Messages.OK == answer) {
|
||||
Arrays.sort(idx);
|
||||
for (int i = idx.length - 1; i >= 0; --i) {
|
||||
int i1 = idx[i];
|
||||
mySelectionManager.removeSelection((VirtualFile)((CollectionListModel) mySelectedList.getModel()).getElementAt(i1));
|
||||
myTree.revalidate();
|
||||
myTree.repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return splitter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile getFile(final Object node) {
|
||||
if (! (((DefaultMutableTreeNode)node).getUserObject() instanceof FileNodeDescriptor)) return null;
|
||||
final FileNodeDescriptor descriptor = (FileNodeDescriptor)((DefaultMutableTreeNode)node).getUserObject();
|
||||
if (descriptor.getElement().getFile() == null) return null;
|
||||
return descriptor.getElement().getFile();
|
||||
}
|
||||
|
||||
private static class MyCheckboxTreeCellRenderer extends JPanel implements TreeCellRenderer {
|
||||
private final WithModulesListCellRenderer myTextRenderer;
|
||||
public final JCheckBox myCheckbox;
|
||||
private final SelectionManager mySelectionManager;
|
||||
private final Map<VirtualFile, String> myModulesSet;
|
||||
private final Collection<VirtualFile> myRoots;
|
||||
private final ColoredTreeCellRenderer myColoredRenderer;
|
||||
private final JLabel myEmpty;
|
||||
private final JList myFictive;
|
||||
|
||||
private MyCheckboxTreeCellRenderer(final SelectionManager selectionManager, Map<VirtualFile, String> modulesSet, final Project project,
|
||||
final JTree tree, final Collection<VirtualFile> roots) {
|
||||
super(new BorderLayout());
|
||||
mySelectionManager = selectionManager;
|
||||
myModulesSet = modulesSet;
|
||||
myRoots = roots;
|
||||
myColoredRenderer = new ColoredTreeCellRenderer() {
|
||||
@Override
|
||||
public void customizeCellRenderer(JTree tree,
|
||||
Object value,
|
||||
boolean selected,
|
||||
boolean expanded,
|
||||
boolean leaf,
|
||||
int row,
|
||||
boolean hasFocus) {
|
||||
append(value.toString());
|
||||
}
|
||||
};
|
||||
myFictive = new JBList();
|
||||
myFictive.setBackground(tree.getBackground());
|
||||
myFictive.setSelectionBackground(UIUtil.getListSelectionBackground());
|
||||
myFictive.setSelectionForeground(UIUtil.getListSelectionForeground());
|
||||
|
||||
myTextRenderer = new WithModulesListCellRenderer(project, myModulesSet) {
|
||||
@Override
|
||||
protected void putParentPath(Object value, FilePath path, FilePath self) {
|
||||
if (myRoots.contains(self.getVirtualFile())) {
|
||||
super.putParentPath(value, path, self);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
myCheckbox = new JCheckBox();
|
||||
myEmpty = new JLabel("");
|
||||
|
||||
add(myCheckbox, BorderLayout.WEST);
|
||||
add(myTextRenderer, BorderLayout.CENTER);
|
||||
myCheckbox.setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTreeCellRendererComponent(JTree tree,
|
||||
Object value,
|
||||
boolean selected,
|
||||
boolean expanded,
|
||||
boolean leaf,
|
||||
int row,
|
||||
boolean hasFocus) {
|
||||
myTextRenderer.setOpened(expanded);
|
||||
invalidate();
|
||||
final VirtualFile file = getFile(value);
|
||||
final DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
|
||||
if (file == null) {
|
||||
if (value instanceof DefaultMutableTreeNode) {
|
||||
final Object uo = node.getUserObject();
|
||||
if (uo instanceof String) {
|
||||
myColoredRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
|
||||
return myColoredRenderer;
|
||||
}
|
||||
}
|
||||
return myEmpty;
|
||||
}
|
||||
myCheckbox.setVisible(true);
|
||||
final TreeNodeState state = mySelectionManager.getState(node);
|
||||
myCheckbox.setEnabled(TreeNodeState.CLEAR.equals(state) || TreeNodeState.SELECTED.equals(state));
|
||||
myCheckbox.setSelected(!TreeNodeState.CLEAR.equals(state));
|
||||
myTextRenderer.getListCellRendererComponent(myFictive, file, 0, selected, hasFocus);
|
||||
revalidate();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyNodeConvertor implements Convertor<DefaultMutableTreeNode, VirtualFile> {
|
||||
private final static MyNodeConvertor ourInstance = new MyNodeConvertor();
|
||||
|
||||
public static MyNodeConvertor getInstance() {
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile convert(DefaultMutableTreeNode o) {
|
||||
return ((FileNodeDescriptor)o.getUserObject()).getElement().getFile();
|
||||
}
|
||||
}
|
||||
|
||||
private static class WithModulesListCellRenderer extends VirtualFileListCellRenderer {
|
||||
private boolean opened;
|
||||
private final Map<VirtualFile, String> myModules;
|
||||
|
||||
private WithModulesListCellRenderer(Project project, final Map<VirtualFile, String> modules) {
|
||||
super(project, true);
|
||||
myModules = modules;
|
||||
}
|
||||
|
||||
public void setOpened(boolean opened) {
|
||||
this.opened = opened;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName(FilePath path) {
|
||||
final String module = myModules.get(path.getVirtualFile());
|
||||
if (module != null) {
|
||||
return module;
|
||||
}
|
||||
return super.getName(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderIcon(FilePath path) {
|
||||
final String module = myModules.get(path.getVirtualFile());
|
||||
if (module != null) {
|
||||
if (opened) {
|
||||
setIcon(PlatformIcons.CONTENT_ROOT_ICON_OPEN);
|
||||
} else {
|
||||
setIcon(PlatformIcons.CONTENT_ROOT_ICON_CLOSED);
|
||||
}
|
||||
} else {
|
||||
if (path.isDirectory()) {
|
||||
if (opened) {
|
||||
setIcon(PlatformIcons.DIRECTORY_OPEN_ICON);
|
||||
} else {
|
||||
setIcon(PlatformIcons.DIRECTORY_CLOSED_ICON);
|
||||
}
|
||||
} else {
|
||||
setIcon(path.getFileType().getIcon());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putParentPathImpl(Object value, String parentPath, FilePath self) {
|
||||
append(self.getPath(), SimpleTextAttributes.GRAYED_ATTRIBUTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,7 @@ public class GitHistoryUtilsTest extends GitSingleUserTest {
|
||||
}
|
||||
};
|
||||
|
||||
GitHistoryUtils.hashesWithParents(myProject, bfilePath, consumer, null);
|
||||
GitHistoryUtils.hashesWithParents(myProject, bfilePath, consumer, null, null);
|
||||
|
||||
assertEquals(hashesWithParents.size(), expectedSize);
|
||||
for (Iterator hit = hashesWithParents.iterator(), myIt = myRevisionsAfterRename.iterator(); hit.hasNext(); ) {
|
||||
|
||||
Reference in New Issue
Block a user