git log: + filter by user
authorirengrig <Irina.Chernushina@jetbrains.com>
Mon, 24 Jan 2011 10:01:14 +0000 (13:01 +0300)
committerirengrig <Irina.Chernushina@jetbrains.com>
Mon, 24 Jan 2011 10:01:14 +0000 (13:01 +0300)
16 files changed:
platform/lang-impl/src/com/intellij/util/CompletionContributorForTextField.java [new file with mode: 0644]
platform/lang-impl/src/com/intellij/util/TextFieldCompletionProvider.java [new file with mode: 0644]
platform/vcs-api/src/com/intellij/openapi/vcs/CheckSamePattern.java [moved from plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CheckSamePattern.java with 96% similarity]
plugins/git4idea/src/git4idea/config/GitConfigUtil.java
plugins/git4idea/src/git4idea/history/browser/LowLevelAccessImpl.java
plugins/git4idea/src/git4idea/history/browser/SymbolicRefs.java
plugins/git4idea/src/git4idea/history/wholeTree/BasePopupAction.java
plugins/git4idea/src/git4idea/history/wholeTree/BranchSelectorAction.java
plugins/git4idea/src/git4idea/history/wholeTree/GitLogUI.java
plugins/git4idea/src/git4idea/history/wholeTree/LoadController.java
plugins/git4idea/src/git4idea/history/wholeTree/Loader.java
plugins/git4idea/src/git4idea/history/wholeTree/Mediator.java
plugins/git4idea/src/git4idea/history/wholeTree/MediatorImpl.java
plugins/git4idea/src/git4idea/history/wholeTree/UserFilterI.java [moved from plugins/git4idea/src/git4idea/history/browser/Mediator.java with 65% similarity]
plugins/git4idea/src/git4idea/history/wholeTree/UsersFilterAction.java [new file with mode: 0644]
plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/SelectedChangeListsChecker.java

diff --git a/platform/lang-impl/src/com/intellij/util/CompletionContributorForTextField.java b/platform/lang-impl/src/com/intellij/util/CompletionContributorForTextField.java
new file mode 100644 (file)
index 0000000..0d0f3fd
--- /dev/null
@@ -0,0 +1,41 @@
+package com.intellij.util;
+
+import com.intellij.codeInsight.completion.CompletionContributor;
+import com.intellij.codeInsight.completion.CompletionParameters;
+import com.intellij.codeInsight.completion.CompletionResultSet;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiPlainTextFile;
+
+/**
+ * @author sergey.evdokimov
+ */
+public class CompletionContributorForTextField extends CompletionContributor {
+
+  @Override
+  public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result) {
+    PsiFile file = parameters.getOriginalFile();
+    if (!(file instanceof PsiPlainTextFile)) return;
+
+    ApplicationManager.getApplication().assertReadAccessAllowed();
+
+    TextFieldCompletionProvider field = file.getUserData(TextFieldCompletionProvider.COMPLETING_TEXT_FIELD_KEY);
+    if (field == null) return;
+
+    String text = file.getText();
+    int offset = parameters.getOffset();
+
+    String prefix = field.getPrefix(text.substring(0, Math.min(text.length(), offset)));
+
+    CompletionResultSet activeResult;
+
+    if (!result.getPrefixMatcher().getPrefix().equals(prefix)) {
+      activeResult = result.withPrefixMatcher(prefix);
+    }
+    else {
+      activeResult = result;
+    }
+
+    field.addCompletionVariants(text, offset, prefix, activeResult);
+  }
+}
diff --git a/platform/lang-impl/src/com/intellij/util/TextFieldCompletionProvider.java b/platform/lang-impl/src/com/intellij/util/TextFieldCompletionProvider.java
new file mode 100644 (file)
index 0000000..74e55e2
--- /dev/null
@@ -0,0 +1,49 @@
+package com.intellij.util;
+
+import com.intellij.codeInsight.completion.CompletionResultSet;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.fileTypes.PlainTextLanguage;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiFileFactory;
+import com.intellij.ui.EditorTextField;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author sergey.evdokimov
+ */
+public abstract class TextFieldCompletionProvider {
+
+  static final Key<TextFieldCompletionProvider> COMPLETING_TEXT_FIELD_KEY = Key.create("COMPLETING_TEXT_FIELD_KEY");
+
+  public void apply(@NotNull EditorTextField field) {
+    Project project = field.getProject();
+    assert project != null;
+    field.setDocument(createDocument(project));
+  }
+
+  private Document createDocument(final Project project) {
+    final FileType fileType = PlainTextLanguage.INSTANCE.getAssociatedFileType();
+    assert fileType != null;
+
+    final long stamp = LocalTimeCounter.currentTime();
+    final PsiFile psiFile = PsiFileFactory.getInstance(project)
+      .createFileFromText("Dummy." + fileType.getDefaultExtension(), fileType, "", stamp, true, false);
+
+    psiFile.putUserData(COMPLETING_TEXT_FIELD_KEY, this);
+
+    final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
+    assert document != null;
+    return document;
+  }
+
+  @NotNull
+  protected String getPrefix(@NotNull String currentTextPrefix) {
+    return currentTextPrefix;
+  }
+
+  protected abstract void addCompletionVariants(@NotNull String text, int offset, @NotNull String prefix, @NotNull CompletionResultSet result);
+}
similarity index 96%
rename from plugins/svn4idea/src/org/jetbrains/idea/svn/integrate/CheckSamePattern.java
rename to platform/vcs-api/src/com/intellij/openapi/vcs/CheckSamePattern.java
index 9489b30ff4eaccbedd02ddb0695a9395209f9858..ce531362eac4fd80857329ea4b8249de64e3bd2b 100644 (file)
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.jetbrains.idea.svn.integrate;
+package com.intellij.openapi.vcs;
 
 public class CheckSamePattern<T> {
   private boolean mySame;
index 3dbe7cf81a9910d1e421c6f737e7711286ea0460..3c986b6fc26ec23959c14713c5de67106ab3f785 100644 (file)
@@ -34,6 +34,8 @@ import java.util.Map;
  * Git utilities for working with configuration
  */
 public class GitConfigUtil {
+  public static final String USER_NAME = "user.name";
+
   /**
    * A private constructor for utility class
    */
index 28fdeb0924fb0003e7ecc12d64235813f2e43b3a..9db1e22fe7be9a6845ec1808be69e26ff33dd155 100644 (file)
@@ -26,6 +26,7 @@ import com.intellij.util.AsynchConsumer;
 import git4idea.GitBranch;
 import git4idea.GitTag;
 import git4idea.commands.GitFileUtils;
+import git4idea.config.GitConfigUtil;
 import git4idea.history.GitHistoryUtils;
 import git4idea.history.wholeTree.CommitHashPlusParents;
 import org.jetbrains.annotations.NotNull;
@@ -108,6 +109,7 @@ public class LowLevelAccessImpl implements LowLevelAccess {
     if (current != null) {
       refs.setTrackedRemote(current.getTrackedRemoteName(myProject, myRoot));
     }
+    refs.setUsername(GitConfigUtil.getValue(myProject, myRoot, GitConfigUtil.USER_NAME));
     return refs;
   }
 
index 0868aa585bbc836ffaa028bdd542c5d55ea1f944..cbef146be03f7f50e052b2c140d549abcd829692 100644 (file)
@@ -15,7 +15,6 @@ package git4idea.history.browser;
 import git4idea.GitBranch;
 
 import java.util.Collection;
-import java.util.List;
 import java.util.TreeSet;
 
 /**
@@ -27,6 +26,7 @@ public class SymbolicRefs {
   private final TreeSet<String> myLocalBranches;
   private final TreeSet<String> myRemoteBranches;
   private String myTrackedRemoteName;
+  private String myUsername;
 
   public SymbolicRefs() {
     myTags = new TreeSet<String>();
@@ -94,6 +94,14 @@ public class SymbolicRefs {
     return myTrackedRemoteName;
   }
 
+  public String getUsername() {
+    return myUsername;
+  }
+
+  public void setUsername(String username) {
+    myUsername = username;
+  }
+
   public static enum Kind {
     TAG,
     LOCAL,
index b2b400c578a38da2927130cb389168e5b1e39591..e1f0fcf9d4ee0a838182a1361bba9a11814c4fa1 100644 (file)
@@ -19,9 +19,10 @@ import com.intellij.ide.DataManager;
 import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
 import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
+import com.intellij.openapi.project.DumbAwareAction;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopup;
 import com.intellij.openapi.ui.popup.JBPopupFactory;
-import com.intellij.openapi.ui.popup.ListPopup;
 import com.intellij.openapi.util.IconLoader;
 import com.intellij.ui.awt.RelativePoint;
 import com.intellij.util.ui.UIUtil;
@@ -34,11 +35,11 @@ import java.awt.event.MouseEvent;
 /**
  * @author irengrig
  */
-public abstract class BasePopupAction extends AnAction implements CustomComponentAction {
+public abstract class BasePopupAction extends DumbAwareAction implements CustomComponentAction {
   private static final Icon ARROWS_ICON = IconLoader.getIcon("/ide/statusbar_arrows.png");
   protected final JLabel myLabel;
-  private final JPanel myPanel;
-  private final Project myProject;
+  protected final JPanel myPanel;
+  protected final Project myProject;
 
   public BasePopupAction(final Project project, final String labeltext) {
     myProject = project;
@@ -83,7 +84,7 @@ public abstract class BasePopupAction extends AnAction implements CustomComponen
     final DefaultActionGroup group = createActionGroup();
     final DataContext parent = DataManager.getInstance().getDataContext((Component) myPanel.getParent());
     final DataContext dataContext = SimpleDataContext.getSimpleContext(PlatformDataKeys.PROJECT.getName(), myProject, parent);
-    final ListPopup popup = JBPopupFactory.getInstance()
+    final JBPopup popup = JBPopupFactory.getInstance()
       .createActionGroupPopup(null, group, dataContext, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, true,
                               new Runnable() {
                                 @Override
index 321fb4b78a8f1e04eb29fbea70d8d67dea7cde5b..034fdf95c38e95558677dea05dfda44257880f90 100644 (file)
  */
 package git4idea.history.wholeTree;
 
-import com.intellij.ide.DataManager;
-import com.intellij.openapi.actionSystem.*;
-import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
-import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.project.DumbAwareAction;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.popup.JBPopupFactory;
-import com.intellij.openapi.ui.popup.ListPopup;
-import com.intellij.openapi.util.IconLoader;
-import com.intellij.openapi.vcs.actions.SelectedBlockHistoryAction;
-import com.intellij.ui.awt.RelativePoint;
 import com.intellij.util.Consumer;
-import com.intellij.util.ui.UIUtil;
 import git4idea.GitBranch;
 import git4idea.history.browser.SymbolicRefs;
 
-import javax.swing.*;
-import javax.swing.border.BevelBorder;
-import javax.swing.border.CompoundBorder;
-import java.awt.*;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
 import java.util.TreeSet;
 
 /**
@@ -100,7 +87,7 @@ public class BranchSelectorAction extends BasePopupAction {
     doAction(null);
   }
 
-  private class SelectBranchAction extends AnAction {
+  private class SelectBranchAction extends DumbAwareAction {
     private final String myValue;
 
     private SelectBranchAction(String text, String value) {
index c15c2ffc68e37ab187c62fd71c415040c5287260..596950b25f523dd1f69d49feba85b36aa77f0390 100644 (file)
@@ -27,10 +27,7 @@ import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.IconLoader;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.openapi.vcs.AbstractFilterChildren;
-import com.intellij.openapi.vcs.ComparableComparator;
-import com.intellij.openapi.vcs.VcsDataKeys;
-import com.intellij.openapi.vcs.VcsException;
+import com.intellij.openapi.vcs.*;
 import com.intellij.openapi.vcs.changes.Change;
 import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser;
 import com.intellij.openapi.vcs.changes.committed.RepositoryChangesBrowser;
@@ -93,6 +90,7 @@ public class GitLogUI implements Disposable {
 //  private JTextField myFilterField;
   private String myPreviousFilter;
   private List<String> mySearchContext;
+  private List<String> myUsersSearchContext;
   private String mySelectedBranch;
   private BranchSelectorAction myBranchSelectorAction;
   private MySpecificDetails myDetails;
@@ -102,10 +100,14 @@ public class GitLogUI implements Disposable {
 
   private StepType myState;
   private MoreAction myMoreAction;
+  private UsersFilterAction myUsersFilterAction;
+  private MyFilterUi myUserFilterI;
 
   public GitLogUI(Project project, final Mediator mediator) {
     myProject = project;
     myMediator = mediator;
+    mySearchContext = new ArrayList<String>();
+    myUsersSearchContext = new ArrayList<String>();
     myRefs = new HashMap<VirtualFile, SymbolicRefs>();
     myRecalculatedCommon = new SymbolicRefs();
     myPreviousFilter = "";
@@ -113,7 +115,6 @@ public class GitLogUI implements Disposable {
     myDescriptionRenderer = new DescriptionRenderer();
     mySelectionSpeedometer = new Speedometer(20, 400);
     createTableModel();
-    mySearchContext = new ArrayList<String>();
     myState = StepType.CONTINUE;
 
     myUIRefresh = new UIRefresh() {
@@ -149,22 +150,24 @@ public class GitLogUI implements Disposable {
         myRecalculatedCommon.clear();
         if (myRefs.isEmpty()) return;
 
-        String current = null;
-        boolean same = true;
+        final CheckSamePattern<String> currentUser = new CheckSamePattern<String>();
+        final CheckSamePattern<String> currentBranch = new CheckSamePattern<String>();
         for (SymbolicRefs refs : myRefs.values()) {
           myRecalculatedCommon.addLocals(refs.getLocalBranches());
           myRecalculatedCommon.addRemotes(refs.getRemoteBranches());
           myRecalculatedCommon.addTags(refs.getTags());
           final String currentFromRefs = refs.getCurrent() == null ? null : refs.getCurrent().getFullName();
-          if (current == null) {
-            current = currentFromRefs;
-          } else if (! current.equals(currentFromRefs)) {
-            same = false;
-          }
+          currentBranch.iterate(currentFromRefs);
+          currentUser.iterate(refs.getUsername());
         }
-        if (same) {
+        if (currentBranch.isSame()) {
           myRecalculatedCommon.setCurrent(myRefs.values().iterator().next().getCurrent());
         }
+        if (currentUser.isSame()) {
+          final String username = currentUser.getSameValue();
+          myRecalculatedCommon.setUsername(username);
+          myUserFilterI.setMe(username);
+        }
 
         myBranchSelectorAction.setSymbolicRefs(myRecalculatedCommon);
       }
@@ -445,9 +448,17 @@ public class GitLogUI implements Disposable {
         reloadRequest();
       }
     });
+    myUserFilterI = new MyFilterUi(new Runnable() {
+      @Override
+      public void run() {
+        reloadRequest();
+      }
+    });
+    myUsersFilterAction = new UsersFilterAction(myProject, myUserFilterI);
     myFilterAction = new FilterAction(myProject);
     group.add(new MyTextFieldAction());
     group.add(myBranchSelectorAction);
+    group.add(myUsersFilterAction);
     // first create filters...
     //group.add(myFilterAction);
     group.add(new MyCherryPick());
@@ -599,6 +610,7 @@ public class GitLogUI implements Disposable {
     mySearchContext.clear();
     final List<String> words = new ArrayList<String>();
     for (String string : strings) {
+      if (string.trim().length() == 0) continue;
       mySearchContext.add(string.toLowerCase());
       final String word = StringUtil.escapeToRegexp(string);
       sb.append(word).append(".*");
@@ -652,6 +664,7 @@ public class GitLogUI implements Disposable {
   }
 
   List<ColumnInfo> columns() {
+    initAuthor();
     return Arrays.asList((ColumnInfo)COMMENT, AUTHOR, DATE);
   }
 
@@ -673,7 +686,13 @@ public class GitLogUI implements Disposable {
     }
   };
 
-  private abstract class HighlightingRendererBase {
+  private static abstract class HighlightingRendererBase {
+    private final List<String> mySearchContext;
+
+    protected HighlightingRendererBase(List<String> searchContext) {
+      mySearchContext = searchContext;
+    }
+
     protected abstract void usual(final String s);
     protected abstract void highlight(final String s);
 
@@ -708,13 +727,16 @@ public class GitLogUI implements Disposable {
 
   private class HighLightingRenderer extends ColoredTableCellRenderer {
     private final SimpleTextAttributes myHighlightAttributes;
+    private final List<String> mySearchContext;
     private final SimpleTextAttributes myUsualAttributes;
     protected final HighlightingRendererBase myWorker;
 
-    public HighLightingRenderer(SimpleTextAttributes highlightAttributes, SimpleTextAttributes usualAttributes) {
+    public HighLightingRenderer(SimpleTextAttributes highlightAttributes, SimpleTextAttributes usualAttributes,
+                                final List<String> searchContext) {
       myHighlightAttributes = highlightAttributes;
+      mySearchContext = searchContext;
       myUsualAttributes = usualAttributes == null ? SimpleTextAttributes.REGULAR_ATTRIBUTES : usualAttributes;
-      myWorker = new HighlightingRendererBase() {
+      myWorker = new HighlightingRendererBase(searchContext) {
         @Override
         protected void usual(String s) {
           append(s, myUsualAttributes);
@@ -844,7 +866,7 @@ public class GitLogUI implements Disposable {
       private final Consumer<String> myConsumer;
 
       private Inner() {
-        super(HIGHLIGHT_TEXT_ATTRIBUTES, null);
+        super(HIGHLIGHT_TEXT_ATTRIBUTES, null, mySearchContext);
         myIssueLinkRenderer = new IssueLinkRenderer(myProject, this);
         myConsumer = new Consumer<String>() {
           @Override
@@ -889,22 +911,28 @@ public class GitLogUI implements Disposable {
     return bkgColor;
   }
 
-  private final ColumnInfo<Object, String> AUTHOR = new ColumnInfo<Object, String>("Author") {
-    private final TableCellRenderer myRenderer = new HighLightingRenderer(HIGHLIGHT_TEXT_ATTRIBUTES, SimpleTextAttributes.REGULAR_ATTRIBUTES);
+  private ColumnInfo<Object, String> AUTHOR;
 
-    @Override
-    public String valueOf(Object o) {
-      if (o instanceof GitCommit) {
-        return ((GitCommit) o).getAuthor();
+  private void initAuthor() {
+    AUTHOR = new ColumnInfo<Object, String>("Author") {
+      private final TableCellRenderer myRenderer = new HighLightingRenderer(HIGHLIGHT_TEXT_ATTRIBUTES,
+                                                                            SimpleTextAttributes.REGULAR_ATTRIBUTES, myUsersSearchContext);
+
+      @Override
+      public String valueOf(Object o) {
+        if (o instanceof GitCommit) {
+          return ((GitCommit)o).getAuthor();
+        }
+        return "";
       }
-      return "";
-    }
 
-    @Override
-    public TableCellRenderer getRenderer(Object o) {
-      return myRenderer;
-    }
-  };
+      @Override
+      public TableCellRenderer getRenderer(Object o) {
+        return myRenderer;
+      }
+    };
+  }
+
   private final ColumnInfo<Object, String> DATE = new ColumnInfo<Object, String>("Date") {
     private final TableCellRenderer myRenderer = new SimpleRenderer(SimpleTextAttributes.REGULAR_ATTRIBUTES, false);
 
@@ -963,20 +991,35 @@ public class GitLogUI implements Disposable {
     myDetailsCache.resetBranchesCache();
     final Collection<String> startingPoints = mySelectedBranch == null ? Collections.<String>emptyList() : Collections.singletonList(mySelectedBranch);
     myDescriptionRenderer.resetIcons();
-    if (StringUtil.isEmptyOrSpaces(myPreviousFilter)) {
-      mySearchContext.clear();
-      myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, Collections.<ChangesFilter.Filter>emptyList(), null);
+    final boolean commentFilterEmpty = StringUtil.isEmptyOrSpaces(myPreviousFilter);
+    mySearchContext.clear();
+    myUsersSearchContext.clear();
+
+    if (commentFilterEmpty && (myUserFilterI.myFilter == null)) {
+      myUsersSearchContext.clear();
+      myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, Collections.<Collection<ChangesFilter.Filter>>emptyList(), null);
     } else {
-      final List<ChangesFilter.Filter> filters = new ArrayList<ChangesFilter.Filter>();
-      final Pair<String, List<String>> preparse = preparse(myPreviousFilter);
-      filters.add(new ChangesFilter.Comment(preparse.getFirst()));
+      final List<Collection<ChangesFilter.Filter>> filters = new ArrayList<Collection<ChangesFilter.Filter>>();
 
-      for (String s : preparse.getSecond()) {
-        filters.add(new ChangesFilter.Author(s));
-        filters.add(new ChangesFilter.Committer(s));
+      if (! commentFilterEmpty) {
+        final Pair<String, List<String>> preparse = preparse(myPreviousFilter);
+        final String first = preparse.getFirst();
+        filters.add(Collections.<ChangesFilter.Filter>singletonList(new ChangesFilter.Comment(first)));
+      }
+      if (myUserFilterI.myFilter != null) {
+        final String[] strings = myUserFilterI.myFilter.split(",");
+        final List<ChangesFilter.Filter> userFilters = new ArrayList<ChangesFilter.Filter>();
+        for (String string : strings) {
+          string = string.trim();
+          if (string.length() == 0) continue;
+          myUsersSearchContext.add(string.toLowerCase());
+          final String regexp = StringUtil.escapeToRegexp(string);
+          userFilters.add(new ChangesFilter.Committer(regexp));
+        }
+        filters.add(userFilters);
       }
 
-      myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, filters, myPreviousFilter.split("[\\s]"));
+      myMediator.reload(new RootsHolder(myRootsUnderVcs), startingPoints, filters, commentFilterEmpty ? null : myPreviousFilter.split("[\\s]"));
     }
     updateMoreVisibility();
     selectionChanged();
@@ -1107,6 +1150,7 @@ public class GitLogUI implements Disposable {
     private final StringBuilder mySb;
 
     private HtmlHighlighter(String text) {
+      super(mySearchContext);
       myText = text;
       mySb = new StringBuilder();
     }
@@ -1206,4 +1250,48 @@ public class GitLogUI implements Disposable {
       myMoreAction.setVisible(false);
     }
   }
+
+  private static class MyFilterUi implements UserFilterI {
+    private boolean myMeIsKnown;
+    private String myMe;
+    private String myFilter;
+    private final Runnable myReloadCallback;
+
+    public MyFilterUi(Runnable reloadCallback) {
+      myReloadCallback = reloadCallback;
+    }
+
+    @Override
+    public void allSelected() {
+      myFilter = null;
+      myReloadCallback.run();
+    }
+
+    @Override
+    public void meSelected() {
+      myFilter = myMe;
+      myReloadCallback.run();
+    }
+
+    @Override
+    public void filter(String s) {
+      myFilter = s;
+      myReloadCallback.run();
+    }
+
+    @Override
+    public boolean isMeKnown() {
+      return myMeIsKnown;
+    }
+
+    @Override
+    public String getMe() {
+      return myMe;
+    }
+
+    public void setMe(final String me) {
+      myMeIsKnown = ! StringUtil.isEmptyOrSpaces(me);
+      myMe = me == null ? "" : me.trim();
+    }
+  }
 }
index 531e05e622030e46a074363d18d251cdab73ceb9..764f1724f2119dc20852758ef5795ea57cb357d0 100644 (file)
@@ -47,7 +47,7 @@ public class LoadController implements Loader {
   public void loadSkeleton(final Mediator.Ticket ticket,
                            final RootsHolder rootsHolder,
                            final Collection<String> startingPoints,
-                           final Collection<ChangesFilter.Filter> filters,
+                           final Collection<Collection<ChangesFilter.Filter>> filters,
                            String[] possibleHashes,
                            final LoadGrowthController loadGrowthController) {
     if (myPreviousAlgorithm != null) {
@@ -63,18 +63,40 @@ public class LoadController implements Loader {
 
       if (filters.isEmpty()) {
         final LoaderAndRefresherImpl loaderAndRefresher =
-        new LoaderAndRefresherImpl(ticket, filters, myMediator, startingPoints, myDetailsCache, myProject, rootHolder, myUsersIndex,
-                                   loadGrowthController.getId());
+        new LoaderAndRefresherImpl(ticket, Collections.<ChangesFilter.Filter>emptyList(), myMediator, startingPoints, myDetailsCache,
+                                   myProject, rootHolder, myUsersIndex, loadGrowthController.getId());
         list.add(loaderAndRefresher);
       } else {
-        for (ChangesFilter.Filter filter : filters) {
+        Collection<Collection<ChangesFilter.Filter>> reordered = new ArrayList<Collection<ChangesFilter.Filter>>();
+        final Iterator<Collection<ChangesFilter.Filter>> iterator = filters.iterator();
+        if (iterator.hasNext()) {
+          final Collection<ChangesFilter.Filter> first = iterator.next();
+          for (ChangesFilter.Filter filter : first) {
+            final ArrayList<ChangesFilter.Filter> newList = new ArrayList<ChangesFilter.Filter>();
+            newList.add(filter);
+            reordered.add(newList);
+          }
+        }
+        while (iterator.hasNext()) {
+          final Collection<ChangesFilter.Filter> next = iterator.next();
+          final Collection<Collection<ChangesFilter.Filter>> reorderedCopy = reordered;
+          reordered = new ArrayList<Collection<ChangesFilter.Filter>>();
+          for (ChangesFilter.Filter filter : next) {
+            for (Collection<ChangesFilter.Filter> filterCollection : reorderedCopy) {
+              final ArrayList<ChangesFilter.Filter> newList = new ArrayList<ChangesFilter.Filter>(filterCollection);
+              newList.add(filter);
+              reordered.add(newList);
+            }
+          }
+        }
+
+        for (Collection<ChangesFilter.Filter> filterCollection : reordered) {
           final LoaderAndRefresherImpl loaderAndRefresher =
-          new LoaderAndRefresherImpl(ticket, Collections.singletonList(filter), myMediator, startingPoints, myDetailsCache, myProject,
-                                     rootHolder, myUsersIndex, loadGrowthController.getId());
+          new LoaderAndRefresherImpl(ticket, filterCollection, myMediator, startingPoints, myDetailsCache, myProject, rootHolder, myUsersIndex,
+                                     loadGrowthController.getId());
           list.add(loaderAndRefresher);
         }
       }
-
       ++ i;
     }
 
index 68ecfd00df4d951bc0af7f738205fc9609d3fe64..104b4f6b979035a53cf89795be39649a5921b911 100644 (file)
@@ -26,7 +26,7 @@ public interface Loader {
   void loadSkeleton(Mediator.Ticket ticket,
                     RootsHolder rootsHolder,
                     final Collection<String> startingPoints,
-                    final Collection<ChangesFilter.Filter> filters, String[] possibleHashes, LoadGrowthController loadGrowthController);
+                    final Collection<Collection<ChangesFilter.Filter>> filters, String[] possibleHashes, LoadGrowthController loadGrowthController);
 
   void resume();
 }
index 900949a6d0c4fada253ee98b8a8ee049dbd58410..a44f4d7653dc4ba9ed701001d1723d464e9205a5 100644 (file)
@@ -27,7 +27,7 @@ import java.util.List;
 public interface Mediator {
   void reload(RootsHolder rootsHolder,
               final Collection<String> startingPoints,
-              final Collection<ChangesFilter.Filter> filters,
+              final Collection<Collection<ChangesFilter.Filter>> filters,
               @Nullable String[] possibleHashes);
 
   /**
index 43ec251b8e2a5497d6168820c14cf90853180056..7ae3b6ac489e40ab64a58c41210d53c63acda15a 100644 (file)
@@ -102,7 +102,7 @@ public class MediatorImpl implements Mediator {
   @Override
   public void reload(final RootsHolder rootsHolder,
                      final Collection<String> startingPoints,
-                     final Collection<ChangesFilter.Filter> filters,
+                     final Collection<Collection<ChangesFilter.Filter>> filters,
                      String[] possibleHashes) {
     myTicket.increment();
     myTableWrapper.reset();
similarity index 65%
rename from plugins/git4idea/src/git4idea/history/browser/Mediator.java
rename to plugins/git4idea/src/git4idea/history/wholeTree/UserFilterI.java
index 0fed61f0ee057a2e418164de1b56b1b009543315..0ed63bf16372821ff3ba321ee26cbf56a1fc2de3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2010 JetBrains s.r.o.
+ * 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.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package git4idea.history.browser;
+package git4idea.history.wholeTree;
 
-import java.util.Collection;
-
-public interface Mediator {
-  Collection<GitCommit> getOrLoadParents(final GitCommit commit);
+/**
+ * @author irengrig
+ *         Date: 1/20/11
+ *         Time: 7:36 PM
+ */
+public interface UserFilterI {
+  void allSelected();
+  void meSelected();
+  void filter(final String s);
+  boolean isMeKnown();
+  String getMe();
 }
diff --git a/plugins/git4idea/src/git4idea/history/wholeTree/UsersFilterAction.java b/plugins/git4idea/src/git4idea/history/wholeTree/UsersFilterAction.java
new file mode 100644 (file)
index 0000000..4c29929
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * 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.codeInsight.completion.CompletionResultSet;
+import com.intellij.codeInsight.lookup.LookupElementBuilder;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonShortcuts;
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.fileTypes.FileTypes;
+import com.intellij.openapi.keymap.KeymapUtil;
+import com.intellij.openapi.project.DumbAwareAction;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.ComponentPopupBuilder;
+import com.intellij.openapi.ui.popup.JBPopup;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.ui.EditorCustomization;
+import com.intellij.ui.EditorTextField;
+import com.intellij.ui.EditorTextFieldProvider;
+import com.intellij.util.TextFieldCompletionProvider;
+import git4idea.history.NewGitUsersComponent;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.border.CompoundBorder;
+import java.awt.*;
+import java.util.List;
+
+/**
+ * @author irengrig
+ *         Date: 1/20/11
+ *         Time: 7:35 PM
+ */
+public class UsersFilterAction extends BasePopupAction {
+  public static final String ALL = "All";
+  public static final String USER = "User:";
+  private final UserFilterI myUserFilterI;
+  private AnAction myAllAction;
+  private AnAction mySelectMe;
+  private AnAction mySelect;
+  private String myCurrentText;
+  private final NewGitUsersComponent myUsers;
+  private EditorTextField myEditorField;
+  private JBPopup myPopup;
+  private ComponentPopupBuilder myComponentPopupBuilder;
+  private AnAction mySelectOkAction;
+
+  public UsersFilterAction(final Project project, final UserFilterI userFilterI) {
+    super(project, USER);
+    myUserFilterI = userFilterI;
+    myCurrentText = "";
+    myUsers = NewGitUsersComponent.getInstance(myProject);
+    myAllAction = new DumbAwareAction(ALL) {
+      @Override
+      public void actionPerformed(AnActionEvent e) {
+        myLabel.setText(ALL);
+        myUserFilterI.allSelected();
+        myCurrentText = "";
+        myPanel.setToolTipText(USER + " " + ALL);
+      }
+    };
+    mySelectMe = new DumbAwareAction() {
+      @Override
+      public void actionPerformed(AnActionEvent e) {
+        final String meText = getMeText();
+        myLabel.setText(meText);
+        myPanel.setToolTipText(USER + " " + meText);
+        myUserFilterI.meSelected();
+        myCurrentText = "";
+      }
+
+      @Override
+      public void update(AnActionEvent e) {
+        super.update(e);
+        e.getPresentation().setVisible(myUserFilterI.isMeKnown());
+        e.getPresentation().setText(getMeText());
+      }
+    };
+    createPopup(project);
+    mySelect = new DumbAwareAction("Select..") {
+      @Override
+      public void actionPerformed(AnActionEvent e) {
+        if (myPopup != null) {
+          mySelectOkAction.unregisterCustomShortcutSet(myPopup.getContent());
+        }
+        myPopup = myComponentPopupBuilder.createPopup();
+        myEditorField.setText(myCurrentText);
+        final JComponent content = myPopup.getContent();
+        mySelectOkAction.registerCustomShortcutSet(CommonShortcuts.CTRL_ENTER, content);
+        myPopup.showInScreenCoordinates(myLabel, new Point(0,0));
+        //myPopup.showInCenterOf(myLabel);
+      }
+    };
+    myLabel.setText(ALL);
+  }
+
+  private void createPopup(Project project) {
+    final JPanel panel = new JPanel(new BorderLayout());
+    final EditorTextFieldProvider service = ServiceManager.getService(project, EditorTextFieldProvider.class);
+    myEditorField = service.getEditorField(FileTypes.PLAIN_TEXT.getLanguage(), project, EditorCustomization.Feature.SOFT_WRAP);
+    myEditorField.setBorder(new CompoundBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2), myEditorField.getBorder()));
+    myEditorField.setText("s");
+    myEditorField.setText(myCurrentText);
+    myEditorField.setPreferredSize(new Dimension(200, 70));
+    panel.add(myEditorField, BorderLayout.CENTER);
+
+    final TextFieldCompletionProvider textFieldCompletionProvider = new TextFieldCompletionProvider() {
+      @NotNull
+      @Override
+      protected String getPrefix(@NotNull String currentTextPrefix) {
+        final int text = currentTextPrefix.lastIndexOf(',');
+        return text == -1 ? currentTextPrefix : currentTextPrefix.substring(text + 1).trim();
+      }
+
+      @Override
+      protected void addCompletionVariants(@NotNull String text,
+                                           int offset,
+                                           @NotNull String prefix,
+                                           @NotNull CompletionResultSet result) {
+        final List<String> list = myUsers.get();
+        if (list != null) {
+          for (String completionVariant : list) {
+            final LookupElementBuilder element = LookupElementBuilder.create(completionVariant);
+            result.addElement(element.addLookupString(completionVariant.toLowerCase()));
+          }
+        }
+      }
+    };
+    textFieldCompletionProvider.apply(myEditorField);
+
+    myComponentPopupBuilder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, myEditorField)
+      .setCancelOnClickOutside(true).setDimensionServiceKey(myProject,
+                                                            "git4idea.history.wholeTree.UsersFilterAction.Select", false)
+      .setAdText(KeymapUtil.getShortcutsText(CommonShortcuts.CTRL_ENTER.getShortcuts()) + " to finish")
+      .setTitle("Specify user names, comma separated")
+      .setMovable(true)
+      .setRequestFocus(true).setResizable(true);
+    mySelectOkAction = new AnAction() {
+      @Override
+      public void actionPerformed(AnActionEvent e) {
+        myPopup.closeOk(e.getInputEvent());
+        final String newText = myEditorField.getText();
+        if (Comparing.equal(newText.trim(), myCurrentText.trim())) return;
+        myCurrentText = newText;
+        final String[] pieces = myCurrentText.trim().split(",");
+        if (pieces.length == 0) {
+          myLabel.setText(ALL);
+        } else if (pieces.length == 1) {
+          myLabel.setText(pieces[0].trim());
+        } else {
+          myLabel.setText(pieces[0].trim() + "+");
+        }
+        myPanel.setToolTipText(USER + " " + myCurrentText);
+        myUserFilterI.filter(myCurrentText);
+      }
+    };
+  }
+
+  private String getMeText() {
+    return new StringBuilder().append("me ( ").append(myUserFilterI.getMe()).append(" )").toString();
+  }
+
+  @Override
+  protected DefaultActionGroup createActionGroup() {
+    final DefaultActionGroup group = new DefaultActionGroup();
+    group.add(myAllAction);
+    group.add(mySelectMe);
+    group.add(mySelect);
+    return group;
+  }
+}
index 8a8c97557952200a791e255903b8b78b453afca0..a1806625c5a0f6d3043903ed502c234b70659219 100644 (file)
@@ -16,6 +16,7 @@
 package org.jetbrains.idea.svn.integrate;
 
 import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.vcs.CheckSamePattern;
 import com.intellij.openapi.vcs.VcsDataKeys;
 import com.intellij.openapi.vcs.changes.ChangeList;
 import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;