[log] Rewrite filtering & migrate from GraphFacade to VisibleGraph

New VcsLogFilterer to use SingleTaskController to make a single visible
graph update even for multiple refresh/change filters/request more
commits actions.

Instead of delegating to the GraphFacade of the DataPack send the
updated VisiblePack (containing the VisibleGraph and the reference to
the actual DataPack) to the UI.

Merge GraphTableModel with AbstractVcsLogTableModel;
GraphCellRenderer with AbstractCellRenderer
since there is only 1 implementation now.

This fixes the following issues:
IDEA-125278, IDEA-119054, IDEA-124822, IDEA-115917, IDEA-123885,
IDEA-123426, IDEA-115486, IDEA-115479
This commit is contained in:
Kirill Likhodedov
2014-10-03 19:40:41 +04:00
parent 3157954fd2
commit aaeeaf421f
45 changed files with 1090 additions and 2021 deletions
@@ -2101,6 +2101,30 @@ public class ContainerUtil extends ContainerUtilRt {
return -1;
}
@Contract(pure=true)
public static <T> int lastIndexOf(@NotNull List<T> list, @NotNull Condition<T> condition) {
for (int i = list.size() - 1; i >= 0; i--) {
T t = list.get(i);
if (condition.value(t)) {
return i;
}
}
return -1;
}
@Nullable
@Contract(pure = true)
public static <T, U extends T> U findLastInstance(@NotNull List<T> list, @NotNull final Class<U> clazz) {
int i = lastIndexOf(list, new Condition<T>() {
@Override
public boolean value(T t) {
return clazz.isInstance(t);
}
});
//noinspection unchecked
return i < 0 ? null : (U)list.get(i);
}
@Contract(pure=true)
public static <T> int indexOf(@NotNull List<T> list, @NotNull final T object) {
return indexOf(list, new Condition<T>() {
@@ -2243,6 +2267,20 @@ public class ContainerUtil extends ContainerUtilRt {
return ContainerUtilRt.map2Set(collection, mapper);
}
@NotNull
@Contract(pure=true)
public static <T, V> Set<V> map2SetNotNull(@NotNull Collection<? extends T> collection, @NotNull Function<T, V> mapper) {
if (collection.isEmpty()) return Collections.emptySet();
Set <V> set = new HashSet<V>(collection.size());
for (T t : collection) {
V value = mapper.fun(t);
if (value != null) {
set.add(value);
}
}
return set.isEmpty() ? Collections.<V>emptySet() : set;
}
@NotNull
@Contract(pure=true)
public static <T> T[] toArray(@NotNull List<T> collection, @NotNull T[] array) {
@@ -1,33 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public class ChangeCursorActionRequest implements GraphActionRequest {
@NotNull private final Cursor myCursor;
public ChangeCursorActionRequest(@NotNull Cursor cursor) {
myCursor = cursor;
}
@NotNull
public Cursor getCursor() {
return myCursor;
}
}
@@ -1,41 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
public class ClickGraphAction implements GraphAction {
private final int myVisibleRow;
@Nullable private final Point myRelativePoint;
public ClickGraphAction(int visibleRow, @Nullable Point relativePoint) {
myVisibleRow = visibleRow;
myRelativePoint = relativePoint;
}
public int getRow() {
return myVisibleRow;
}
@Nullable
public Point getRelativePoint() {
return myRelativePoint;
}
}
@@ -1,19 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
public interface GraphAction {
}
@@ -1,24 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
/**
* Action which should be executed by the code which called {@link GraphFacade#performAction(com.intellij.vcs.log.graph.GraphAction)}.
* E.g. when user clicks to the arrow to go to the parent/child commit, the table should jump to concrete row.
*/
public interface GraphActionRequest {
}
@@ -1,40 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
import org.jetbrains.annotations.Nullable;
/**
* The graph returns an instance of the GraphAnswer as a reaction to {@link #performAction(com.intellij.vcs.log.graph.GraphAction)}.
*/
public interface GraphAnswer {
/**
* Tells how graph was changed after performing action <br/>
* {@code null} means that graph didn't change (e.g. when user clicks on the arrow to go to the parent/child commit).
*/
@Nullable
GraphChange getGraphChange();
/**
* Tells which action should be execute by the client code. <br/>
* {@code null} means that the graph doesn't expect the client code to execute anything special
* (although of course it can react somehow on the returned {@link #getGraphChange() GraphChange}.
*/
@Nullable
GraphActionRequest getActionRequest();
}
@@ -1,24 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
/**
* Describes how graph was changed after {@link GraphFacade#performAction(GraphAction) executing some action on it}
* (e.g. a linear branch was collapsed).
*/
public interface GraphChange {
}
@@ -1,87 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
import com.intellij.openapi.util.Condition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
/**
* The only point of interaction with the Graph.
* <p/>
* Any access to the methods of this class must be performed from the EDT.
*/
public interface GraphFacade {
/**
* Paints the given row.
*/
@NotNull
PaintInfo paint(int visibleRow);
/**
* Performs some user action on the graph, which can be a click, hover, drag, changing view parameters, etc. <br/>
* Changes are applied to the graph immediately, and the method returns the result of such action. <br/>
* In some cases the action might take significant time (say, 1 second) - clients of this method take care of it themselves: by showing
* some modal progress or else.
*/
@Nullable
GraphAnswer performAction(@NotNull GraphAction action);
/**
* Returns all commits in the graph without considering commits visibility.
* To get visible commits use {@link #getVisibleCommitCount} & {@link #getCommitAtRow}.
*/
@NotNull
List<GraphCommit<Integer>> getAllCommits();
/**
* A shorthand to getVisibleCommits().get(visibleRow), but may be faster.
*/
int getCommitAtRow(int visibleRow);
/**
* A shorthand to {@code getVisibleCommits().size()}, but is faster.
*/
int getVisibleCommitCount();
/**
* Set branches which should be visible in the log, all others will be hidden.
* Pass {@code null} to show all branches, i.e. reset this branch filter.
*
* @param heads branches represented by commit indices of commits they point to; or {@code null} if all branches should be shown.
* @see #setFilter(Condition)
*/
void setVisibleBranches(@Nullable Collection<Integer> heads);
/**
* Set filter to the commits displayed by the log.
*
* @param visibilityPredicate check if the given commit should be shown or not. null means no filter.
* @see #setVisibleBranches(Collection)
*/
void setFilter(@Nullable Condition<Integer> visibilityPredicate);
/**
* Returns the provider of some information about the graph.
*/
@NotNull
GraphInfoProvider getInfoProvider();
}
@@ -1,43 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
/**
* Use this provider to get information from the Graph. <br/>
* An instance of GraphInfoProvider is obtained via {@link GraphFacade#getInfoProvider()}.
*/
public interface GraphInfoProvider {
@NotNull
Set<Integer> getContainingBranches(int visibleRow); // this requires graph iteration => can take some time
@NotNull
RowInfo getRowInfo(int visibleRow);
boolean areLongEdgesHidden();
/**
* Some information about row highlighting etc. TBD
*/
interface RowInfo {
int getOneOfHeads();
}
}
@@ -1,29 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
public class JumpToNotLoadCommitActionRequest implements GraphActionRequest {
private final int commitHashIndex;
public JumpToNotLoadCommitActionRequest(int commitHashIndex) {
this.commitHashIndex = commitHashIndex;
}
public int getCommitHashIndex() {
return commitHashIndex;
}
}
@@ -1,30 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
public class JumpToRowActionRequest implements GraphActionRequest {
private final int myRow;
public JumpToRowActionRequest(int row) {
myRow = row;
}
public int getRow() {
return myRow;
}
}
@@ -1,32 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
public class LinearBranchesExpansionAction implements GraphAction {
public static LinearBranchesExpansionAction EXPAND = new LinearBranchesExpansionAction(true);
public static LinearBranchesExpansionAction COLLAPSE = new LinearBranchesExpansionAction(false);
private final boolean myExpand;
private LinearBranchesExpansionAction(boolean expand) {
myExpand = expand;
}
public boolean shouldExpand() {
return myExpand;
}
}
@@ -1,37 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
public class LongEdgesAction implements GraphAction {
private static LongEdgesAction SHOW = new LongEdgesAction(true);
private static LongEdgesAction HIDE = new LongEdgesAction(false);
private final boolean myShowLongEdges;
public static LongEdgesAction valueOf(boolean showLongEdges) {
return showLongEdges ? SHOW : HIDE;
}
private LongEdgesAction(boolean show) {
myShowLongEdges = show;
}
public boolean shouldShowLongEdges() {
return myShowLongEdges;
}
}
@@ -1,41 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public class MouseOverAction implements GraphAction {
private final int myVisibleRow;
@NotNull private final Point myRelativePoint;
public MouseOverAction(int visibleRow, @NotNull Point relativePoint) {
myVisibleRow = visibleRow;
myRelativePoint = relativePoint;
}
public int getRow() {
return myVisibleRow;
}
@NotNull
public Point getRelativePoint() {
return myRelativePoint;
}
}
@@ -1,37 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
public interface PaintInfo {
/**
* Returns the image to actually paint.
*/
@NotNull
Image getImage();
/**
* Returns the "interesting" width of the painted image, i.e. the width which the text in the table should be offset by. <br/>
* It can be smaller than the width of {@link #getImage() the image}, because we allow the text to cover part of the graph
* (some diagonal edges, etc.)
*/
int getWidth();
}
@@ -1,31 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.graph;
public class SelectAllRelativeCommitsAction implements GraphAction {
public static final SelectAllRelativeCommitsAction DESELECT_ALL = new SelectAllRelativeCommitsAction(-1);
private final int visibleRowIndex;
public SelectAllRelativeCommitsAction(int visibleRowIndex) {
this.visibleRowIndex = visibleRowIndex;
}
public int getVisibleRowIndex() {
return visibleRowIndex;
}
}
@@ -1,275 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log;
import com.intellij.openapi.util.Condition;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.graph.*;
import com.intellij.vcs.log.graph.actions.ActionController;
import com.intellij.vcs.log.graph.actions.GraphMouseAction;
import com.intellij.vcs.log.printer.idea.ColorGenerator;
import com.intellij.vcs.log.printer.idea.GraphCellPainter;
import com.intellij.vcs.log.printer.idea.PrintParameters;
import com.intellij.vcs.log.printer.idea.SimpleGraphCellPainter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DelegateGraphFacade implements GraphFacade {
@NotNull
public static final GraphAnswer JUMP_TO_0_GRAPH_ANSWER = new GraphAnswer() {
@Nullable
@Override
public GraphChange getGraphChange() {
return new GraphChange() {
};
}
@Nullable
@Override
public GraphActionRequest getActionRequest() {
return new JumpToRowActionRequest(0);
}
};
@NotNull
private final PermanentGraph<Integer> myPermanentGraph;
private final GraphCellPainter myGraphCellPainter;
@NotNull
private PermanentGraph.SortType mySortType = PermanentGraph.SortType.Normal;
@NotNull
private VisibleGraph<Integer> myVisibleGraph;
@Nullable
private Set<Integer> myHeads = null;
@Nullable
private Condition<Integer> myVisibilityPredicate = null;
public DelegateGraphFacade(@NotNull PermanentGraph<Integer> permanentGraph, @NotNull ColorGenerator colorGenerator) {
myPermanentGraph = permanentGraph;
myGraphCellPainter = new SimpleGraphCellPainter(colorGenerator);
updateVisibleGraph();
}
private void updateVisibleGraph() {
myVisibleGraph = myPermanentGraph.createVisibleGraph(mySortType, myHeads, myVisibilityPredicate);
}
@NotNull
@Override
public PaintInfo paint(int visibleRow) {
Collection<PrintElement> printElements = myVisibleGraph.getRowInfo(visibleRow).getPrintElements();
int maxIndex = 0;
for (PrintElement printElement : printElements) {
maxIndex = Math.max(maxIndex, printElement.getPositionInCurrentRow());
}
maxIndex++;
final BufferedImage image =
UIUtil.createImage(PrintParameters.WIDTH_NODE * (maxIndex + 4), PrintParameters.HEIGHT_CELL, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = image.createGraphics();
myGraphCellPainter.draw(g2, printElements);
final int width = maxIndex * PrintParameters.WIDTH_NODE;
return new PaintInfo() {
@NotNull
@Override
public Image getImage() {
return image;
}
@Override
public int getWidth() {
return width;
}
};
}
@Nullable
@Override
public GraphAnswer performAction(@NotNull GraphAction action) {
ActionController<Integer> actionController = myVisibleGraph.getActionController();
if (action instanceof ClickGraphAction) {
ClickGraphAction clickGraphAction = (ClickGraphAction)action;
Point relativePoint = clickGraphAction.getRelativePoint();
PrintElement printElement = null;
if (relativePoint != null) {
Collection<PrintElement> printElements = myVisibleGraph.getRowInfo(clickGraphAction.getRow()).getPrintElements();
printElement = myGraphCellPainter.mouseOver(printElements, relativePoint.x, relativePoint.y);
}
return convert(actionController.performMouseAction(new GraphMouseActionImpl(printElement, GraphMouseAction.Type.CLICK)));
}
if (action instanceof MouseOverAction) {
MouseOverAction mouseOverAction = (MouseOverAction)action;
Point relativePoint = mouseOverAction.getRelativePoint();
Collection<PrintElement> printElements = myVisibleGraph.getRowInfo(mouseOverAction.getRow()).getPrintElements();
PrintElement printElement = myGraphCellPainter.mouseOver(printElements, relativePoint.x, relativePoint.y);
return convert(actionController.performMouseAction(new GraphMouseActionImpl(printElement, GraphMouseAction.Type.OVER)));
}
if (action instanceof LongEdgesAction) {
boolean shouldShowLongEdges = ((LongEdgesAction)action).shouldShowLongEdges();
actionController.setLongEdgesHidden(!shouldShowLongEdges);
}
if (action instanceof LinearBranchesExpansionAction) {
boolean shouldExpand = ((LinearBranchesExpansionAction)action).shouldExpand();
actionController.setLinearBranchesExpansion(!shouldExpand);
return JUMP_TO_0_GRAPH_ANSWER;
}
if (action instanceof BekGraphAction) {
mySortType = ((BekGraphAction)action).getSortType();
updateVisibleGraph();
return JUMP_TO_0_GRAPH_ANSWER;
}
return null;
}
@NotNull
private GraphAnswer convert(@NotNull com.intellij.vcs.log.graph.actions.GraphAnswer<Integer> graphAnswer) {
final Integer commitToJump = graphAnswer.getCommitToJump();
final Cursor cursorToSet = graphAnswer.getCursorToSet();
return new GraphAnswer() {
@Nullable
@Override
public GraphChange getGraphChange() {
if (commitToJump != null) {
return new GraphChange() {};
}
return null;
}
@Nullable
@Override
public GraphActionRequest getActionRequest() {
if (cursorToSet != null)
return new ChangeCursorActionRequest(cursorToSet);
if (commitToJump != null) {
int visibleRowIndex = myVisibleGraph.getVisibleRowIndex(commitToJump);
if (visibleRowIndex == -1)
return new JumpToNotLoadCommitActionRequest(commitToJump);
return new JumpToRowActionRequest(visibleRowIndex);
}
return null;
}
};
}
@NotNull
@Override
public List<GraphCommit<Integer>> getAllCommits() {
return myPermanentGraph.getAllCommits();
}
@Override
public int getCommitAtRow(int visibleRow) {
return myVisibleGraph.getRowInfo(visibleRow).getCommit();
}
@Override
public int getVisibleCommitCount() {
return myVisibleGraph.getVisibleCommitCount();
}
@Override
public void setVisibleBranches(@Nullable Collection<Integer> heads) {
boolean needUpdate;
if (heads == null) {
needUpdate = myHeads != null;
myHeads = null;
} else {
needUpdate = true;
myHeads = new HashSet<Integer>(heads);
}
if (needUpdate)
updateVisibleGraph();
}
@Override
public void setFilter(@Nullable Condition<Integer> visibilityPredicate) {
boolean needUpdate = !(visibilityPredicate == myVisibilityPredicate);
myVisibilityPredicate = visibilityPredicate;
if (needUpdate)
updateVisibleGraph();
}
@NotNull
@Override
public GraphInfoProvider getInfoProvider() {
return new GraphInfoProvider() {
@NotNull
@Override
public Set<Integer> getContainingBranches(int visibleRow) {
return myPermanentGraph.getContainingBranches(getCommitAtRow(visibleRow));
}
@NotNull
@Override
public RowInfo getRowInfo(int visibleRow) {
final Integer oneOfHeads = myVisibleGraph.getRowInfo(visibleRow).getOneOfHeads();
return new RowInfo() {
@Override
public int getOneOfHeads() {
return oneOfHeads;
}
};
}
@Override
public boolean areLongEdgesHidden() {
return myVisibleGraph.getActionController().areLongEdgesHidden();
}
};
}
private static class GraphMouseActionImpl implements GraphMouseAction {
@Nullable
private final PrintElement myAffectedElement;
@NotNull
private final Type myType;
private GraphMouseActionImpl(@Nullable PrintElement affectedElement, @NotNull Type type) {
myAffectedElement = affectedElement;
myType = type;
}
@Nullable
@Override
public PrintElement getAffectedElement() {
return myAffectedElement;
}
@NotNull
@Override
public Type getType() {
return myType;
}
}
}
@@ -13,21 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.graph;
package com.intellij.vcs.log.data;
import org.jetbrains.annotations.NotNull;
public class BekGraphAction implements GraphAction {
enum CommitCountStage {
@NotNull
private final PermanentGraph.SortType mySortType;
INITIAL(5),
FIRST_STEP(2000),
ALL(Integer.MAX_VALUE);
public BekGraphAction(@NotNull PermanentGraph.SortType sortType) {
mySortType = sortType;
private final int myCount;
CommitCountStage(int count) {
myCount = count;
}
@NotNull
public PermanentGraph.SortType getSortType() {
return mySortType;
CommitCountStage next() {
CommitCountStage[] values = values();
return ordinal() == values.length - 1 ? this : values[ordinal() + 1];
}
public int getCount() {
return myCount;
}
}
@@ -12,7 +12,7 @@ import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsLogProvider;
import com.intellij.vcs.log.VcsShortCommitDetails;
import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel;
import com.intellij.vcs.log.ui.tables.GraphTableModel;
import com.intellij.vcs.log.util.SequentialLimitedLifoExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -81,7 +81,7 @@ public abstract class DataGetter<T extends VcsShortCommitDetails> implements Dis
}
@Nullable
public T getCommitData(int row, @NotNull AbstractVcsLogTableModel<?> tableModel) {
public T getCommitData(int row, @NotNull GraphTableModel tableModel) {
assert EventQueue.isDispatchThread();
Hash hash = tableModel.getHashAtRow(row);
if (hash == null) {
@@ -122,7 +122,7 @@ public abstract class DataGetter<T extends VcsShortCommitDetails> implements Dis
@Nullable
protected abstract T getFromAdditionalCache(@NotNull Hash hash);
private void runLoadAroundCommitData(int row, @NotNull AbstractVcsLogTableModel<?> tableModel) {
private void runLoadAroundCommitData(int row, @NotNull GraphTableModel tableModel) {
long taskNumber = myCurrentTaskIndex++;
MultiMap<VirtualFile, Hash> commits = getCommitsAround(row, tableModel, UP_PRELOAD_COUNT, DOWN_PRELOAD_COUNT);
for (Map.Entry<VirtualFile, Collection<Hash>> hashesByRoots : commits.entrySet()) {
@@ -143,7 +143,7 @@ public abstract class DataGetter<T extends VcsShortCommitDetails> implements Dis
}
@NotNull
private static MultiMap<VirtualFile, Hash> getCommitsAround(int selectedRow, @NotNull AbstractVcsLogTableModel<?> model,
private static MultiMap<VirtualFile, Hash> getCommitsAround(int selectedRow, @NotNull GraphTableModel model,
int above, int below) {
MultiMap<VirtualFile, Hash> commits = MultiMap.create();
for (int row = Math.max(0, selectedRow - above); row < selectedRow + below && row < model.getRowCount(); row++) {
@@ -6,22 +6,18 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.graph.GraphColorManagerImpl;
import com.intellij.vcs.log.graph.GraphCommit;
import com.intellij.vcs.log.graph.GraphFacade;
import com.intellij.vcs.log.graph.PermanentGraph;
import com.intellij.vcs.log.graph.impl.facade.PermanentGraphImpl;
import com.intellij.vcs.log.printer.idea.ColorGenerator;
import com.intellij.vcs.log.util.StopWatch;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.util.*;
import java.util.List;
public class DataPack implements VcsLogDataPack {
public class DataPack {
@NotNull private final RefsModel myRefsModel;
@NotNull private final PermanentGraph<Integer> myPermanentGraph;
@NotNull private final GraphFacade myGraphFacade;
@NotNull private final Map<VirtualFile, VcsLogProvider> myLogProviders;
private boolean myFull;
@@ -33,25 +29,7 @@ public class DataPack implements VcsLogDataPack {
boolean full) {
RefsModel refsModel = new RefsModel(refs, hashMap.asIndexGetter());
PermanentGraph<Integer> graph = buildPermanentGraph(commits, refsModel, hashMap.asIndexGetter(), hashMap.asHashGetter(), providers);
return new DataPack(refsModel, graph, createGraphFacade(graph), providers, full);
}
@NotNull
private static GraphFacade createGraphFacade(@NotNull PermanentGraph<Integer> permanentGraph) {
GraphFacade facade;
if (!permanentGraph.getAllCommits().isEmpty()) {
ColorGenerator colorGenerator = new ColorGenerator() {
@Override
public Color getColor(int colorId) {
return com.intellij.vcs.log.graph.ColorGenerator.getColor(colorId);
}
};
facade = new DelegateGraphFacade(permanentGraph, colorGenerator);
}
else {
facade = new EmptyGraphFacade();
}
return facade;
return new DataPack(refsModel, graph, providers, full);
}
@NotNull
@@ -91,22 +69,17 @@ public class DataPack implements VcsLogDataPack {
return map;
}
DataPack(@NotNull RefsModel refsModel, @NotNull PermanentGraph<Integer> permanentGraph, @NotNull GraphFacade graphFacade,
@NotNull Map<VirtualFile, VcsLogProvider> providers, boolean full) {
DataPack(@NotNull RefsModel refsModel,
@NotNull PermanentGraph<Integer> permanentGraph,
@NotNull Map<VirtualFile, VcsLogProvider> providers,
boolean full) {
myRefsModel = refsModel;
myPermanentGraph = permanentGraph;
myGraphFacade = graphFacade;
myLogProviders = providers;
myFull = full;
}
@NotNull
public GraphFacade getGraphFacade() {
return myGraphFacade;
}
@NotNull
@Override
public VcsLogRefs getRefs() {
return myRefsModel;
}
@@ -117,7 +90,6 @@ public class DataPack implements VcsLogDataPack {
}
@NotNull
@Override
public Map<VirtualFile, VcsLogProvider> getLogProviders() {
return myLogProviders;
}
@@ -30,7 +30,7 @@ public class EmptyDataPack {
@NotNull
public static DataPack getInstance() {
RefsModel emptyModel = new RefsModel(Collections.<VirtualFile, Set<VcsRef>>emptyMap(), new ConstantFunction<Hash, Integer>(0));
return new DataPack(emptyModel, EmptyPermanentGraph.getInstance(), new EmptyGraphFacade(), Collections.<VirtualFile, VcsLogProvider>emptyMap(), false);
return new DataPack(emptyModel, EmptyPermanentGraph.getInstance(), Collections.<VirtualFile, VcsLogProvider>emptyMap(), false);
}
}
@@ -1,109 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.data;
import com.intellij.openapi.util.Condition;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.graph.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
class EmptyGraphFacade implements GraphFacade {
public static final BufferedImage EMPTY_IMAGE = UIUtil.createImage(1, 1, Transparency.TRANSLUCENT);
@NotNull
@Override
public PaintInfo paint(int visibleRow) {
return new PaintInfo() {
@NotNull
@Override
public Image getImage() {
return EMPTY_IMAGE;
}
@Override
public int getWidth() {
return 0;
}
};
}
@Nullable
@Override
public GraphAnswer performAction(@NotNull GraphAction action) {
return null;
}
@NotNull
@Override
public List<GraphCommit<Integer>> getAllCommits() {
return Collections.emptyList();
}
@Override
public int getCommitAtRow(int visibleRow) {
return -1;
}
@Override
public int getVisibleCommitCount() {
return 0;
}
@Override
public void setVisibleBranches(@Nullable Collection<Integer> heads) {
}
@Override
public void setFilter(@Nullable Condition<Integer> visibilityPredicate) {
}
@NotNull
@Override
public GraphInfoProvider getInfoProvider() {
return new GraphInfoProvider() {
@NotNull
@Override
public Set<Integer> getContainingBranches(int visibleRow) {
return Collections.emptySet();
}
@NotNull
@Override
public RowInfo getRowInfo(int visibleRow) {
return new RowInfo() {
@Override
public int getOneOfHeads() {
return -1;
}
};
}
@Override
public boolean areLongEdgesHidden() {
return false;
}
};
}
}
@@ -1,33 +0,0 @@
/*
* Copyright 2000-2013 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.vcs.log.data;
public enum LoadMoreStage {
/**
* Initial table view; "load more" was not requested yet.
*/
INITIAL,
/**
* "Load more" was once requested with a limited number of commits.
*/
LOADED_MORE,
/**
* All commits matching the given filters were requested from the VCS, requesting more won't cause any effect.
*/
ALL_REQUESTED
}
@@ -18,6 +18,7 @@ package com.intellij.vcs.log.data;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -82,10 +83,13 @@ public abstract class SingleTaskController<Request, Result> {
/**
* The underlying currently active task should use this method to inform that it has completed the execution. <br/>
* The result is immediately passed to the result handler specified in the constructor.
* If the result is not null, it is immediately passed to the result handler specified in the constructor.
* Otherwise result handler is not called, the task just completes.
*/
protected final void taskCompleted(@NotNull Result result) {
myResultHandler.consume(result);
protected final void taskCompleted(@Nullable Result result) {
if (result != null) {
myResultHandler.consume(result);
}
synchronized (LOCK) {
if (myAwaitingRequests.isEmpty()) {
myActive = false;
@@ -22,7 +22,6 @@ import com.intellij.openapi.progress.BackgroundTaskQueue;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
@@ -30,8 +29,8 @@ import com.intellij.util.Consumer;
import com.intellij.util.ThrowableConsumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.Topic;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.graph.PermanentGraph;
import com.intellij.vcs.log.util.StopWatch;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -61,13 +60,6 @@ public class VcsLogDataHolder implements Disposable, VcsLogDataProvider {
private final Map<VirtualFile, VcsUser> myCurrentUser = ContainerUtil.newHashMap();
private final Consumer<DataPack> myDataPackUpdateHandler;
/**
* Indicates if user wants the whole log graph to be shown.
* Initially we show only the top of the log (even after we've loaded the whole log structure) for performance reasons.
* However, if user once navigates to some old commit, we build the whole graph and show it until the next full refresh or project reload.
*/
private volatile boolean myFullLogShowing;
/**
* Cached details of the latest commits.
* We store them separately from the cache of {@link DataGetter}, to make sure that they are always available,
@@ -82,11 +74,14 @@ public class VcsLogDataHolder implements Disposable, VcsLogDataProvider {
private final ContainingBranchesGetter myContainingBranchesGetter;
@NotNull private final VcsLogRefresher myRefresher;
private final VcsLogFiltererImpl myFilterer;
public VcsLogDataHolder(@NotNull Project project,
@NotNull Disposable parentDisposable,
@NotNull Map<VirtualFile, VcsLogProvider> logProviders,
@NotNull VcsLogSettings settings, Consumer<DataPack> dataPackUpdateHandler) {
@NotNull VcsLogSettings settings,
@NotNull VcsLogUiProperties uiProperties,
@NotNull Consumer<VisiblePack> visiblePackConsumer) {
Disposer.register(parentDisposable, this);
myProject = project;
myLogProviders = logProviders;
@@ -94,7 +89,6 @@ public class VcsLogDataHolder implements Disposable, VcsLogDataProvider {
myMiniDetailsGetter = new MiniDetailsGetter(this, logProviders);
myDetailsGetter = new CommitDetailsGetter(this, logProviders);
mySettings = settings;
myDataPackUpdateHandler = dataPackUpdateHandler;
myUserRegistry = (VcsUserRegistryImpl)ServiceManager.getService(project, VcsUserRegistry.class);
try {
@@ -105,8 +99,20 @@ public class VcsLogDataHolder implements Disposable, VcsLogDataProvider {
}
myContainingBranchesGetter = new ContainingBranchesGetter(project, this, this);
myFilterer = new VcsLogFiltererImpl(myProject, myLogProviders, myHashMap, myTopCommitsDetailsCache, myDetailsGetter,
uiProperties.isBek() ? PermanentGraph.SortType.Bek : PermanentGraph.SortType.Normal,
visiblePackConsumer);
myDataPackUpdateHandler = new Consumer<DataPack>() {
@Override
public void consume(DataPack dataPack) {
myFilterer.onRefresh(dataPack);
myProject.getMessageBus().syncPublisher(REFRESH_COMPLETED).refresh(dataPack);
}
};
myRefresher = new VcsLogRefresherImpl(myProject, myHashMap, myLogProviders, myUserRegistry, myTopCommitsDetailsCache,
dataPackUpdateHandler, new Consumer<Exception>() {
myDataPackUpdateHandler, new Consumer<Exception>() {
@Override
public void consume(Exception e) {
LOG.error(e);
@@ -114,6 +120,11 @@ public class VcsLogDataHolder implements Disposable, VcsLogDataProvider {
}, mySettings.getRecentCommitsCount());
}
@NotNull
public VcsLogFilterer getFilterer() {
return myFilterer;
}
@Override
@NotNull
public Hash getHash(int commitIndex) {
@@ -167,54 +178,9 @@ public class VcsLogDataHolder implements Disposable, VcsLogDataProvider {
}
private void resetState() {
myFullLogShowing = false;
myTopCommitsDetailsCache.clear();
}
/**
* Show the full log tree to the user.
* Initially only the top part of the log is shown to avoid memory and performance problems.
* However, if user wants to navigate to something in the past, we rebuild the log and show it.
* <p/>
* Method returns immediately, log building is executed in the background.
* <p/>
* TODO: in most cases, users don't need to go to such deep past even if they need to go deeper than to 1000 most recent commits.
* Therefore optimize: Add a hash parameter, and build only the necessary part of the log + some commits below.
*
* @param onSuccess Invoked in the EDT after the log DataPack is built.
*/
public void showFullLog(@NotNull final Runnable onSuccess) {
if (myFullLogShowing) {
return;
}
runInBackground(new ThrowableConsumer<ProgressIndicator, VcsException>() {
@Override
public void consume(ProgressIndicator indicator) throws VcsException {
if (myFullLogShowing) {
return;
}
// TODO
myFullLogShowing = true;
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
if (!Disposer.isDisposed(VcsLogDataHolder.this)) {
onSuccess.run();
}
}
});
}
}, "Building full log...");
}
public boolean isFullLogShowing() {
return myFullLogShowing;
}
@NotNull
public Set<VcsUser> getAllUsers() {
return myUserRegistry.getUsers();
@@ -253,23 +219,6 @@ public class VcsLogDataHolder implements Disposable, VcsLogDataProvider {
return myContainingBranchesGetter;
}
@Nullable
public Hash findHashByString(@NotNull String string) {
final String pHash = string.toLowerCase();
try {
return myHashMap.findHash(new Condition<Hash>() {
@Override
public boolean value(@NotNull Hash hash) {
return hash.toString().toLowerCase().startsWith(pHash);
}
});
}
catch (IOException e) {
LOG.error(e);
return null;
}
}
void runInBackground(final ThrowableConsumer<ProgressIndicator, VcsException> task, final String title) {
myDataLoaderQueue.run(new Task.Backgroundable(myProject, title) {
@Override
@@ -1,269 +1,34 @@
/*
* Copyright 2000-2014 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.vcs.log.data;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.ThrowableConsumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.graph.GraphFacade;
import com.intellij.vcs.log.impl.VcsLogUtil;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel;
import com.intellij.vcs.log.ui.tables.EmptyTableModel;
import com.intellij.vcs.log.ui.tables.GraphTableModel;
import gnu.trove.TIntHashSet;
import com.intellij.vcs.log.VcsLogFilterCollection;
import com.intellij.vcs.log.graph.PermanentGraph;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*/
public interface VcsLogFilterer {
public class VcsLogFilterer {
void onRefresh(@NotNull DataPack dataPack);
private static final Logger LOG = Logger.getInstance(VcsLogFilterer.class);
void onFiltersChange(@NotNull VcsLogFilterCollection filters);
private static final int LOAD_MORE_COMMITS_FIRST_STEP_LIMIT = 2000;
void onSortTypeChange(@NotNull PermanentGraph.SortType sortType);
@NotNull private final VcsLogDataHolder myLogDataHolder;
@NotNull private final VcsLogUiImpl myUI;
public VcsLogFilterer(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiImpl ui) {
myLogDataHolder = logDataHolder;
myUI = ui;
}
@NotNull
public AbstractVcsLogTableModel applyFiltersAndUpdateUi(@NotNull DataPack dataPack, @NotNull VcsLogFilterCollection filters) {
resetFilters(dataPack);
VcsLogHashFilter hashFilter = filters.getHashFilter();
if (hashFilter != null && !hashFilter.getHashes().isEmpty()) { // hashes should be shown, no matter if they match other filters or not
return applyHashFilter(dataPack, hashFilter.getHashes());
}
List<VcsLogDetailsFilter> detailsFilters = filters.getDetailsFilters();
applyGraphFilters(dataPack, filters.getBranchFilter());
return applyDetailsFilter(dataPack, detailsFilters);
}
private GraphTableModel applyHashFilter(@NotNull DataPack dataPack, @NotNull Collection<String> hashes) {
final List<Integer> indices = ContainerUtil.mapNotNull(hashes, new Function<String, Integer>() {
@Override
public Integer fun(String partOfHash) {
Hash hash = myLogDataHolder.findHashByString(partOfHash);
return hash != null ? myLogDataHolder.getCommitIndex(hash) : null;
}
});
dataPack.getGraphFacade().setVisibleBranches(null);
dataPack.getGraphFacade().setFilter(new Condition<Integer>() {
@Override
public boolean value(Integer integer) {
return indices.contains(integer);
}
});
return new GraphTableModel(dataPack, myLogDataHolder, myUI, LoadMoreStage.ALL_REQUESTED);
}
private static void resetFilters(@NotNull DataPack dataPack) {
GraphFacade facade = dataPack.getGraphFacade();
facade.setVisibleBranches(null);
facade.setFilter(null);
}
private AbstractVcsLogTableModel applyDetailsFilter(DataPack dataPack, List<VcsLogDetailsFilter> detailsFilters) {
if (!detailsFilters.isEmpty()) {
List<Hash> filteredCommits = filterInMemory(dataPack, detailsFilters);
if (filteredCommits.isEmpty()) {
return new EmptyTableModel(dataPack, myLogDataHolder, myUI, LoadMoreStage.INITIAL);
}
else{
Condition<Integer> filter = getFilterFromCommits(filteredCommits);
dataPack.getGraphFacade().setFilter(filter);
}
}
else {
dataPack.getGraphFacade().setFilter(null);
}
return new GraphTableModel(dataPack, myLogDataHolder, myUI, LoadMoreStage.INITIAL);
}
private Condition<Integer> getFilterFromCommits(List<Hash> filteredCommits) {
final Set<Integer> commitSet = ContainerUtil.map2Set(filteredCommits, new Function<Hash, Integer>() {
@Override
public Integer fun(Hash hash) {
return myLogDataHolder.getCommitIndex(hash);
}
});
return new Condition<Integer>() {
@Override
public boolean value(Integer integer) {
return commitSet.contains(integer);
}
};
}
public void requestVcs(@NotNull final DataPack dataPack, @NotNull VcsLogFilterCollection filters,
@NotNull final LoadMoreStage loadMoreStage, @NotNull final Runnable onSuccess) {
ApplicationManager.getApplication().assertIsDispatchThread();
int maxCount = loadMoreStage == LoadMoreStage.INITIAL ? LOAD_MORE_COMMITS_FIRST_STEP_LIMIT : -1;
getFilteredDetailsFromTheVcs(dataPack.getLogProviders(), filters, new Consumer<List<Hash>>() {
@Override
public void consume(List<Hash> hashes) {
LoadMoreStage newLoadMoreStage = advanceLoadMoreStage(loadMoreStage);
TIntHashSet previouslySelected = myUI.getSelectedCommits();
AbstractVcsLogTableModel model;
if (hashes.isEmpty()) {
model = new EmptyTableModel(dataPack, myLogDataHolder, myUI, newLoadMoreStage);
}
else {
dataPack.getGraphFacade().setFilter(getFilterFromCommits(hashes));
model = new GraphTableModel(dataPack, myLogDataHolder, myUI, newLoadMoreStage);
}
myUI.setModel(model, dataPack, previouslySelected);
myUI.repaintUI();
onSuccess.run();
}
}, maxCount);
}
@NotNull
private static LoadMoreStage advanceLoadMoreStage(@NotNull LoadMoreStage loadMoreStage) {
LoadMoreStage newLoadMoreStage;
if (loadMoreStage == LoadMoreStage.INITIAL) {
newLoadMoreStage = LoadMoreStage.LOADED_MORE;
}
else if (loadMoreStage == LoadMoreStage.LOADED_MORE) {
newLoadMoreStage = LoadMoreStage.ALL_REQUESTED;
}
else {
LOG.warn("Incorrect previous load more stage: " + loadMoreStage);
newLoadMoreStage = LoadMoreStage.ALL_REQUESTED;
}
return newLoadMoreStage;
}
private void applyGraphFilters(@NotNull final DataPack dataPack, @Nullable final VcsLogBranchFilter branchFilter) {
try {
dataPack.getGraphFacade().setVisibleBranches(branchFilter != null ? getMatchingHeads(dataPack, branchFilter) : null);
}
catch (InvalidRequestException e) {
if (!myLogDataHolder.isFullLogShowing()) {
myLogDataHolder.showFullLog(EmptyRunnable.getInstance());
throw new ProcessCanceledException();
}
else {
throw e;
}
}
}
@NotNull
private Collection<Integer> getMatchingHeads(@NotNull DataPack dataPack, @NotNull VcsLogBranchFilter branchFilter) {
final Collection<String> branchNames = new HashSet<String>(branchFilter.getBranchNames());
return ContainerUtil.mapNotNull(dataPack.getRefsModel().getAllRefs(), new Function<VcsRef, Integer>() {
@Override
public Integer fun(VcsRef ref) {
if (branchNames.contains(ref.getName())) {
return myLogDataHolder.getCommitIndex(ref.getCommitHash());
}
return null;
}
});
}
@NotNull
private List<Hash> filterInMemory(@NotNull DataPack dataPack, @NotNull List<VcsLogDetailsFilter> detailsFilters) {
List<Hash> result = ContainerUtil.newArrayList();
for (int visibleCommit : VcsLogUtil.getVisibleCommits(dataPack.getGraphFacade())) {
VcsCommitMetadata data = getDetailsFromCache(visibleCommit);
if (data == null) {
// no more continuous details in the cache
break;
}
if (matchesAllFilters(data, detailsFilters)) {
result.add(data.getId());
}
}
return result;
}
private static boolean matchesAllFilters(@NotNull final VcsCommitMetadata commit, @NotNull List<VcsLogDetailsFilter> detailsFilters) {
return !ContainerUtil.exists(detailsFilters, new Condition<VcsLogDetailsFilter>() {
@Override
public boolean value(VcsLogDetailsFilter filter) {
return !filter.matches(commit);
}
});
}
@Nullable
private VcsCommitMetadata getDetailsFromCache(final int commitIndex) {
final Hash hash = myLogDataHolder.getHash(commitIndex);
VcsCommitMetadata details = myLogDataHolder.getTopCommitDetails(hash);
if (details != null) {
return details;
}
final Ref<VcsCommitMetadata> ref = Ref.create();
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
ref.set(myLogDataHolder.getCommitDetailsGetter().getCommitDataIfAvailable(hash));
}
});
return ref.get();
}
public void getFilteredDetailsFromTheVcs(@NotNull final Map<VirtualFile, VcsLogProvider> providers,
@NotNull final VcsLogFilterCollection filterCollection,
@NotNull final Consumer<List<Hash>> success,
final int maxCount) {
myLogDataHolder.runInBackground(new ThrowableConsumer<ProgressIndicator, VcsException>() {
@Override
public void consume(ProgressIndicator indicator) throws VcsException {
Collection<List<? extends TimedVcsCommit>> logs = ContainerUtil.newArrayList();
for (Map.Entry<VirtualFile, VcsLogProvider> entry : providers.entrySet()) {
final VirtualFile root = entry.getKey();
if (filterCollection.getStructureFilter() != null && filterCollection.getStructureFilter().getFiles(root).isEmpty()
|| filterCollection.getUserFilter() != null && filterCollection.getUserFilter().getUserNames(root).isEmpty()) {
// there is a structure or user filter, but it doesn't match this root
continue;
}
List<TimedVcsCommit> matchingCommits = entry.getValue().getCommitsMatchingFilter(root, filterCollection, maxCount);
logs.add(matchingCommits);
}
final List<? extends TimedVcsCommit> compoundLog = new VcsLogMultiRepoJoiner().join(logs);
final List<Hash> list = ContainerUtil.map(compoundLog, new Function<TimedVcsCommit, Hash>() {
@Override
public Hash fun(TimedVcsCommit commit) {
return commit.getId();
}
});
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
if (!myLogDataHolder.getProject().isDisposed()) {
success.consume(list);
}
}
});
}
}, "Looking for more results...");
}
void moreCommitsNeeded(@NotNull Runnable onLoaded);
}
@@ -0,0 +1,169 @@
/*
* Copyright 2000-2014 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.vcs.log.data;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.impl.ProgressManagerImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsCommitMetadata;
import com.intellij.vcs.log.VcsLogFilterCollection;
import com.intellij.vcs.log.VcsLogProvider;
import com.intellij.vcs.log.graph.PermanentGraph;
import com.intellij.vcs.log.impl.VcsLogFilterCollectionImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
public class VcsLogFiltererImpl implements VcsLogFilterer {
@NotNull private final SingleTaskController<Request, VisiblePack> myTaskController;
@NotNull private final VisiblePackBuilder myVisiblePackBuilder;
@NotNull private VcsLogFilterCollection myFilters;
@NotNull private PermanentGraph.SortType mySortType;
@NotNull private CommitCountStage myCommitCount = CommitCountStage.INITIAL;
@Nullable private DataPack myDataPack;
VcsLogFiltererImpl(@NotNull final Project project,
@NotNull Map<VirtualFile, VcsLogProvider> providers,
@NotNull VcsLogHashMap hashMap,
@NotNull Map<Hash, VcsCommitMetadata> topCommitsDetailsCache,
@NotNull CommitDetailsGetter detailsGetter,
@NotNull final PermanentGraph.SortType initialSortType,
@NotNull final Consumer<VisiblePack> visiblePackConsumer) {
myVisiblePackBuilder = new VisiblePackBuilder(providers, hashMap, topCommitsDetailsCache, detailsGetter);
myFilters = new VcsLogFilterCollectionImpl(null, null, null, null, null, null);
mySortType = initialSortType;
myTaskController = new SingleTaskController<Request, VisiblePack>(visiblePackConsumer) {
@Override
protected void startNewBackgroundTask() {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
ProgressManagerImpl.runProcessWithProgressAsynchronously(new MyTask(project, "Applying filters..."));
}
});
}
};
}
@Override
public void onRefresh(@NotNull DataPack dataPack) {
myTaskController.request(new RefreshRequest(dataPack));
}
@Override
public void onFiltersChange(@NotNull VcsLogFilterCollection newFilters) {
myTaskController.request(new FilterRequest(newFilters));
}
@Override
public void onSortTypeChange(@NotNull PermanentGraph.SortType sortType) {
myTaskController.request(new SortTypeRequest(sortType));
}
@Override
public void moreCommitsNeeded(@NotNull Runnable onLoaded) {
myTaskController.request(new MoreCommitsRequest(onLoaded));
}
private class MyTask extends Task.Backgroundable {
public MyTask(@Nullable Project project, @NotNull String title) {
super(project, title);
}
@Override
public void run(@NotNull ProgressIndicator indicator) {
VisiblePack visiblePack = null;
List<Request> requests;
while (!(requests = myTaskController.popRequests()).isEmpty()) {
RefreshRequest refreshRequest = ContainerUtil.findLastInstance(requests, RefreshRequest.class);
FilterRequest filterRequest = ContainerUtil.findLastInstance(requests, FilterRequest.class);
SortTypeRequest sortTypeRequest = ContainerUtil.findLastInstance(requests, SortTypeRequest.class);
if (refreshRequest != null) {
myDataPack = refreshRequest.dataPack;
}
if (filterRequest != null) {
myFilters = filterRequest.filters;
}
if (sortTypeRequest != null) {
mySortType = sortTypeRequest.sortType;
}
if (myDataPack == null) { // when filter is set during initialization, just remember filters
continue;
}
if (filterRequest != null) { // "more commits needed" has no effect if filter changes; it also can't come after filter change request
myCommitCount = CommitCountStage.INITIAL;
}
else if (ContainerUtil.findInstance(requests, MoreCommitsRequest.class) != null) {
myCommitCount = myCommitCount.next();
}
Pair<VisiblePack, CommitCountStage> pair = myVisiblePackBuilder.build(myDataPack, mySortType, myFilters, myCommitCount);
visiblePack = pair.first;
myCommitCount = pair.second;
}
// visible pack can be null (e.g. when filter is set during initialization) => we just remember filters set by user
myTaskController.taskCompleted(visiblePack);
}
}
private interface Request {
}
private static final class RefreshRequest implements Request {
private final DataPack dataPack;
RefreshRequest(DataPack dataPack) {
this.dataPack = dataPack;
}
}
private static final class FilterRequest implements Request {
private final VcsLogFilterCollection filters;
FilterRequest(VcsLogFilterCollection filters) {
this.filters = filters;
}
}
private static final class SortTypeRequest implements Request {
private final PermanentGraph.SortType sortType;
SortTypeRequest(PermanentGraph.SortType sortType) {
this.sortType = sortType;
}
}
private static final class MoreCommitsRequest implements Request {
private final Runnable onLoaded;
MoreCommitsRequest(Runnable onLoaded) {
this.onLoaded = onLoaded;
}
}
}
@@ -92,6 +92,23 @@ public class VcsLogHashMap implements Disposable {
}
}
@Nullable
public Hash findHashByString(@NotNull String string) {
final String pHash = string.toLowerCase();
try {
return findHash(new Condition<Hash>() {
@Override
public boolean value(@NotNull Hash hash) {
return hash.toString().toLowerCase().startsWith(pHash);
}
});
}
catch (IOException e) {
LOG.error(e);
return null;
}
}
@NotNull
public NotNullFunction<Hash, Integer> asIndexGetter() {
return new NotNullFunction<Hash, Integer>() {
@@ -0,0 +1,67 @@
/*
* Copyright 2000-2014 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.vcs.log.data;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcs.log.VcsLogDataPack;
import com.intellij.vcs.log.VcsLogProvider;
import com.intellij.vcs.log.VcsLogRefs;
import com.intellij.vcs.log.graph.VisibleGraph;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
public class VisiblePack implements VcsLogDataPack {
public static final VisiblePack EMPTY = new VisiblePack(EmptyDataPack.getInstance(), EmptyVisibleGraph.getInstance(), false);
@NotNull private final DataPack myDataPack;
@NotNull private final VisibleGraph<Integer> myVisibleGraph;
private final boolean myCanRequestMore;
VisiblePack(@NotNull DataPack dataPack, @NotNull VisibleGraph<Integer> graph, boolean canRequestMore) {
myDataPack = dataPack;
myVisibleGraph = graph;
myCanRequestMore = canRequestMore;
}
@NotNull
public VisibleGraph<Integer> getVisibleGraph() {
return myVisibleGraph;
}
public boolean canRequestMore() {
return myCanRequestMore;
}
@NotNull
@Override
public Map<VirtualFile, VcsLogProvider> getLogProviders() {
return myDataPack.getLogProviders();
}
@NotNull
@Override
public VcsLogRefs getRefs() {
return myDataPack.getRefs();
}
@NotNull
public RefsModel getRefsModel() {
return myDataPack.getRefsModel();
}
}
@@ -0,0 +1,228 @@
/*
* Copyright 2000-2014 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.vcs.log.data;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.graph.GraphCommit;
import com.intellij.vcs.log.graph.PermanentGraph;
import com.intellij.vcs.log.graph.VisibleGraph;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
class VisiblePackBuilder {
private static final Logger LOG = Logger.getInstance(VisiblePackBuilder.class);
@NotNull private final VcsLogHashMap myHashMap;
@NotNull private final Map<Hash, VcsCommitMetadata> myTopCommitsDetailsCache;
@NotNull private final CommitDetailsGetter myCommitDetailsGetter;
@NotNull private final Map<VirtualFile, VcsLogProvider> myLogProviders;
VisiblePackBuilder(@NotNull Map<VirtualFile, VcsLogProvider> providers,
@NotNull VcsLogHashMap hashMap,
@NotNull Map<Hash, VcsCommitMetadata> topCommitsDetailsCache,
@NotNull CommitDetailsGetter detailsGetter) {
myHashMap = hashMap;
myTopCommitsDetailsCache = topCommitsDetailsCache;
myCommitDetailsGetter = detailsGetter;
myLogProviders = providers;
}
@NotNull
Pair<VisiblePack, CommitCountStage> build(@NotNull DataPack dataPack,
@NotNull PermanentGraph.SortType sortType,
@NotNull VcsLogFilterCollection filters,
@NotNull CommitCountStage commitCount) {
VcsLogHashFilter hashFilter = filters.getHashFilter();
if (hashFilter != null && !hashFilter.getHashes().isEmpty()) { // hashes should be shown, no matter if they match other filters or not
return Pair.create(applyHashFilter(dataPack, hashFilter.getHashes(), sortType), commitCount);
}
List<VcsLogDetailsFilter> detailsFilters = filters.getDetailsFilters();
Condition<Integer> filter;
boolean canRequestMore;
if (!detailsFilters.isEmpty()) {
List<Hash> matchingCommits = null;
if (commitCount == CommitCountStage.INITIAL) {
matchingCommits = filterInMemory(dataPack.getPermanentGraph(), detailsFilters);
if (matchingCommits.size() < commitCount.getCount()) {
commitCount = commitCount.next();
matchingCommits = null;
}
}
if (matchingCommits == null) {
try {
matchingCommits = getFilteredDetailsFromTheVcs(myLogProviders, filters, commitCount.getCount());
}
catch (VcsException e) {
// TODO show an error balloon or something else for non-ea guys.
matchingCommits = Collections.emptyList();
LOG.error(e);
}
}
filter = getFilterFromCommits(matchingCommits);
canRequestMore = matchingCommits.size() >= commitCount.getCount(); // from VCS: only "==", but from memory can be ">"
}
else {
filter = null;
canRequestMore = false;
}
Set<Integer> heads = getMatchingHeads(dataPack.getRefs(), filters);
VisibleGraph<Integer> visibleGraph = dataPack.getPermanentGraph().createVisibleGraph(sortType, heads, filter);
return Pair.create(new VisiblePack(dataPack, visibleGraph, canRequestMore), commitCount);
}
private VisiblePack applyHashFilter(@NotNull DataPack dataPack,
@NotNull Collection<String> hashes,
@NotNull PermanentGraph.SortType sortType) {
final Set<Integer> indices = ContainerUtil.map2SetNotNull(hashes, new Function<String, Integer>() {
@Override
public Integer fun(String partOfHash) {
Hash hash = myHashMap.findHashByString(partOfHash);
return hash != null ? myHashMap.getCommitIndex(hash) : null;
}
});
VisibleGraph<Integer> visibleGraph = dataPack.getPermanentGraph().createVisibleGraph(sortType, null, new Condition<Integer>() {
@Override
public boolean value(Integer integer) {
return indices.contains(integer);
}
});
return new VisiblePack(dataPack, visibleGraph, false);
}
@Nullable
private Set<Integer> getMatchingHeads(@NotNull VcsLogRefs refs, @NotNull VcsLogFilterCollection filters) {
VcsLogBranchFilter branchFilter = filters.getBranchFilter();
if (branchFilter == null) {
return null;
}
final Collection<String> branchNames = new HashSet<String>(branchFilter.getBranchNames());
return new HashSet<Integer>(ContainerUtil.mapNotNull(refs.getBranches(), new Function<VcsRef, Integer>() {
@Override
public Integer fun(VcsRef ref) {
if (branchNames.contains(ref.getName())) {
return myHashMap.getCommitIndex(ref.getCommitHash());
}
return null;
}
}));
}
@NotNull
private List<Hash> filterInMemory(@NotNull PermanentGraph<Integer> permanentGraph, @NotNull List<VcsLogDetailsFilter> detailsFilters) {
List<Hash> result = ContainerUtil.newArrayList();
for (GraphCommit<Integer> commit : permanentGraph.getAllCommits()) {
VcsCommitMetadata data = getDetailsFromCache(commit.getId());
if (data == null) {
// no more continuous details in the cache
break;
}
if (matchesAllFilters(data, detailsFilters)) {
result.add(data.getId());
}
}
return result;
}
private static boolean matchesAllFilters(@NotNull final VcsCommitMetadata commit, @NotNull List<VcsLogDetailsFilter> detailsFilters) {
return ContainerUtil.and(detailsFilters, new Condition<VcsLogDetailsFilter>() {
@Override
public boolean value(VcsLogDetailsFilter filter) {
return filter.matches(commit);
}
});
}
@Nullable
private VcsCommitMetadata getDetailsFromCache(final int commitIndex) {
final Hash hash = myHashMap.getHash(commitIndex);
VcsCommitMetadata details = myTopCommitsDetailsCache.get(hash);
if (details != null) {
return details;
}
return UIUtil.invokeAndWaitIfNeeded(new Computable<VcsCommitMetadata>() {
@Override
public VcsCommitMetadata compute() {
return myCommitDetailsGetter.getCommitDataIfAvailable(hash);
}
});
}
@NotNull
private static List<Hash> getFilteredDetailsFromTheVcs(@NotNull Map<VirtualFile, VcsLogProvider> providers,
@NotNull VcsLogFilterCollection filterCollection,
int maxCount) throws VcsException {
Collection<List<TimedVcsCommit>> logs = ContainerUtil.newArrayList();
for (Map.Entry<VirtualFile, VcsLogProvider> entry : providers.entrySet()) {
VirtualFile root = entry.getKey();
if (filterCollection.getStructureFilter() != null && filterCollection.getStructureFilter().getFiles(root).isEmpty()
|| filterCollection.getUserFilter() != null && filterCollection.getUserFilter().getUserNames(root).isEmpty()) {
// there is a structure or user filter, but it doesn't match this root
continue;
}
List<TimedVcsCommit> matchingCommits = entry.getValue().getCommitsMatchingFilter(root, filterCollection, maxCount);
logs.add(matchingCommits);
}
List<TimedVcsCommit> compoundLog = new VcsLogMultiRepoJoiner<Hash, TimedVcsCommit>().join(logs);
return ContainerUtil.map(compoundLog, new Function<TimedVcsCommit, Hash>() {
@Override
public Hash fun(TimedVcsCommit commit) {
return commit.getId();
}
});
}
@Nullable
private Condition<Integer> getFilterFromCommits(@Nullable List<Hash> filteredCommits) {
if (filteredCommits == null) {
return null;
}
final Set<Integer> commitSet = ContainerUtil.map2Set(filteredCommits, new Function<Hash, Integer>() {
@Override
public Integer fun(Hash hash) {
return myHashMap.getCommitIndex(hash);
}
});
return new Condition<Integer>() {
@Override
public boolean value(Integer integer) {
return commitSet.contains(integer);
}
};
}
}
@@ -21,7 +21,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel;
import com.intellij.vcs.log.ui.tables.GraphTableModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -48,7 +48,7 @@ public class VcsLogImpl implements VcsLog {
List<Hash> hashes = ContainerUtil.newArrayList();
JBTable table = myUi.getTable();
for (int row : table.getSelectedRows()) {
Hash hash = ((AbstractVcsLogTableModel)table.getModel()).getHashAtRow(row);
Hash hash = ((GraphTableModel)table.getModel()).getHashAtRow(row);
if (hash != null) {
hashes.add(hash);
}
@@ -62,7 +62,7 @@ public class VcsLogImpl implements VcsLog {
List<VcsFullCommitDetails> details = ContainerUtil.newArrayList();
JBTable table = myUi.getTable();
for (int row : table.getSelectedRows()) {
AbstractVcsLogTableModel model = (AbstractVcsLogTableModel)table.getModel();
GraphTableModel model = (GraphTableModel)table.getModel();
VcsFullCommitDetails commitDetails = model.getFullCommitDetails(row);
if (commitDetails == null) {
return ContainerUtil.emptyList();
@@ -64,23 +64,22 @@ public class VcsLogManager implements Disposable {
public JComponent initContent() {
final Map<VirtualFile, VcsLogProvider> logProviders = findLogProviders();
Consumer<DataPack> dataPackUpdateHandler = new Consumer<DataPack>() {
Consumer<VisiblePack> visiblePackConsumer = new Consumer<VisiblePack>() {
@Override
public void consume(final DataPack dataPack) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (!Disposer.isDisposed(myUi)) {
myUi.setDataPack(dataPack);
myProject.getMessageBus().syncPublisher(VcsLogDataHolder.REFRESH_COMPLETED).refresh(dataPack);
public void consume(final VisiblePack pack) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (!Disposer.isDisposed(myUi)) {
myUi.setVisiblePack(pack);
}
}
}
});
});
}
};
VcsLogDataHolder logDataHolder = new VcsLogDataHolder(myProject, this, logProviders, mySettings, dataPackUpdateHandler);
VcsLogDataHolder logDataHolder = new VcsLogDataHolder(myProject, this, logProviders, mySettings, myUiProperties, visiblePackConsumer);
myUi = new VcsLogUiImpl(logDataHolder, myProject, mySettings,
new VcsLogColorManagerImpl(logProviders.keySet()), myUiProperties, EmptyDataPack.getInstance());
new VcsLogColorManagerImpl(logProviders.keySet()), myUiProperties, logDataHolder.getFilterer());
myLogRefresher = new PostponeableLogRefresher(myProject, logDataHolder);
refreshLogOnVcsEvents(logProviders);
logDataHolder.initialize();
@@ -18,7 +18,7 @@ package com.intellij.vcs.log.impl;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.MultiMap;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.graph.GraphFacade;
import com.intellij.vcs.log.graph.VisibleGraph;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -45,16 +45,16 @@ public class VcsLogUtil {
}
@NotNull
public static List<Integer> getVisibleCommits(@NotNull final GraphFacade facade) {
public static List<Integer> getVisibleCommits(@NotNull final VisibleGraph<Integer> visibleGraph) {
return new AbstractList<Integer>() {
@Override
public Integer get(int index) {
return facade.getCommitAtRow(index);
return visibleGraph.getRowInfo(index).getCommit();
}
@Override
public int size() {
return facade.getVisibleCommitCount();
return visibleGraph.getVisibleCommitCount();
}
};
}
@@ -7,20 +7,19 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.util.PairFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.data.*;
import com.intellij.vcs.log.graph.ChangeCursorActionRequest;
import com.intellij.vcs.log.graph.ClickGraphAction;
import com.intellij.vcs.log.graph.*;
import com.intellij.vcs.log.graph.PermanentGraph;
import com.intellij.vcs.log.graph.VisibleGraph;
import com.intellij.vcs.log.graph.actions.GraphAnswer;
import com.intellij.vcs.log.impl.VcsLogImpl;
import com.intellij.vcs.log.ui.frame.MainFrame;
import com.intellij.vcs.log.ui.frame.VcsLogGraphTable;
import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel;
import com.intellij.vcs.log.ui.tables.GraphTableModel;
import gnu.trove.TIntHashSet;
import gnu.trove.TIntProcedure;
import org.jetbrains.annotations.NotNull;
@@ -40,27 +39,44 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
private static final Logger LOG = Logger.getInstance(VcsLogUiImpl.class);
@NotNull private final MainFrame myMainFrame;
@NotNull private final VcsLogDataHolder myLogDataHolder;
@NotNull private final Project myProject;
@NotNull private final VcsLogColorManager myColorManager;
@NotNull private final VcsLogFilterer myFilterer;
@NotNull private final VcsLog myLog;
@NotNull private final VcsLogUiProperties myUiProperties;
@NotNull private final VcsLogFilterer myFilterer;
@NotNull private final Collection<VcsLogFilterChangeListener> myFilterChangeListeners = ContainerUtil.newArrayList();
@NotNull private DataPack myDataPack;
@NotNull private VisiblePack myVisiblePack;
public VcsLogUiImpl(@NotNull VcsLogDataHolder logDataHolder, @NotNull Project project, @NotNull VcsLogSettings settings,
@NotNull VcsLogColorManager manager, @NotNull VcsLogUiProperties uiProperties, @NotNull DataPack initialDataPack) {
@NotNull VcsLogColorManager manager, @NotNull VcsLogUiProperties uiProperties, @NotNull VcsLogFilterer filterer) {
myLogDataHolder = logDataHolder;
myProject = project;
myColorManager = manager;
myUiProperties = uiProperties;
myDataPack = initialDataPack;
Disposer.register(logDataHolder, this);
myFilterer = new VcsLogFilterer(logDataHolder, this);
myFilterer = filterer;
myLog = new VcsLogImpl(logDataHolder, this);
myMainFrame = new MainFrame(logDataHolder, this, project, settings, uiProperties, myLog, initialDataPack);
myVisiblePack = VisiblePack.EMPTY;
myMainFrame = new MainFrame(logDataHolder, this, project, settings, uiProperties, myLog, myVisiblePack);
}
public void setVisiblePack(@NotNull VisiblePack pack) {
ApplicationManager.getApplication().assertIsDispatchThread();
TIntHashSet previouslySelected = getSelectedCommits();
myVisiblePack = pack;
GraphTableModel newModel = new GraphTableModel(myVisiblePack, myLogDataHolder, this);
setModel(newModel, myVisiblePack.getVisibleGraph(), previouslySelected);
myMainFrame.updateDataPack(myVisiblePack);
setLongEdgeVisibility(myUiProperties.areLongEdgesVisible());
fireFilterChangeEvent();
repaintUI();
}
@NotNull
@@ -77,19 +93,20 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
});
}
public void setModel(@NotNull AbstractVcsLogTableModel newModel, @NotNull DataPack newDataPack,
@NotNull TIntHashSet previouslySelectedCommits) {
private void setModel(@NotNull GraphTableModel newModel,
@NotNull VisibleGraph<Integer> newVisibleGraph,
@NotNull TIntHashSet previouslySelectedCommits) {
final VcsLogGraphTable table = getTable();
table.setModel(newModel);
restoreSelection(newModel, newDataPack, previouslySelectedCommits, table);
restoreSelection(newModel, newVisibleGraph, previouslySelectedCommits, table);
table.setPaintBusy(false);
}
private static void restoreSelection(@NotNull AbstractVcsLogTableModel newModel,
@NotNull DataPack newDataPack,
private static void restoreSelection(@NotNull GraphTableModel newModel,
@NotNull VisibleGraph<Integer> newVisibleGraph,
@NotNull TIntHashSet previouslySelectedCommits,
@NotNull final VcsLogGraphTable table) {
TIntHashSet rowsToSelect = findNewRowsToSelect(newModel, newDataPack, previouslySelectedCommits);
TIntHashSet rowsToSelect = findNewRowsToSelect(newModel, newVisibleGraph, previouslySelectedCommits);
rowsToSelect.forEach(new TIntProcedure() {
@Override
public boolean execute(int row) {
@@ -100,8 +117,8 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
}
@NotNull
private static TIntHashSet findNewRowsToSelect(@NotNull AbstractVcsLogTableModel newModel,
@NotNull DataPack dataPack,
private static TIntHashSet findNewRowsToSelect(@NotNull GraphTableModel newModel,
@NotNull VisibleGraph<Integer> visibleGraph,
@NotNull TIntHashSet selectedHashes) {
TIntHashSet rowsToSelect = new TIntHashSet();
if (newModel.getRowCount() == 0) {
@@ -110,10 +127,9 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
// => it has previous values set.
return rowsToSelect;
}
GraphFacade facade = dataPack.getGraphFacade();
for (int row = 0; row < facade.getVisibleCommitCount()
for (int row = 0; row < visibleGraph.getVisibleCommitCount()
&& rowsToSelect.size() < selectedHashes.size(); row++) { //stop iterating if found all hashes
int commit = facade.getCommitAtRow(row);
int commit = visibleGraph.getRowInfo(row).getCommit();
if (selectedHashes.contains(commit)) {
rowsToSelect.add(row);
}
@@ -129,11 +145,11 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
runUnderModalProgress("Expanding linear branches...", new Runnable() {
@Override
public void run() {
final GraphAnswer answer = myDataPack.getGraphFacade().performAction(LinearBranchesExpansionAction.EXPAND);
myVisiblePack.getVisibleGraph().getActionController().setLinearBranchesExpansion(false);
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
handleAnswer(answer);
handleAnswer(null, true);
}
});
}
@@ -144,11 +160,11 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
runUnderModalProgress("Collapsing linear branches...", new Runnable() {
@Override
public void run() {
final GraphAnswer answer = myDataPack.getGraphFacade().performAction(LinearBranchesExpansionAction.COLLAPSE);
myVisiblePack.getVisibleGraph().getActionController().setLinearBranchesExpansion(true);
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
handleAnswer(answer);
handleAnswer(null, true);
}
});
}
@@ -156,80 +172,64 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
}
public void setLongEdgeVisibility(boolean visibility) {
handleAnswer(myDataPack.getGraphFacade().performAction(LongEdgesAction.valueOf(visibility)));
myVisiblePack.getVisibleGraph().getActionController().setLongEdgesHidden(!visibility);
myUiProperties.setLongEdgesVisibility(visibility);
}
public boolean areLongEdgesHidden() {
return myDataPack.getGraphFacade().getInfoProvider().areLongEdgesHidden();
}
public void setBek(boolean bek) {
myUiProperties.setBek(bek);
final BekGraphAction bekGraphAction = new BekGraphAction(bek ? PermanentGraph.SortType.Bek : PermanentGraph.SortType.Normal);
runUnderModalProgress("Apply sort type...", new Runnable() {
@Override
public void run() {
final GraphAnswer answer = myDataPack.getGraphFacade().performAction(bekGraphAction);
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
handleAnswer(answer);
}
});
}
});
myFilterer.onSortTypeChange(bek ? PermanentGraph.SortType.Bek : PermanentGraph.SortType.Normal);
}
public boolean isBek() {
return myUiProperties.isBek();
}
public void click(int rowIndex) {
handleAnswer(myDataPack.getGraphFacade().performAction(new ClickGraphAction(rowIndex, null)));
}
public void jumpToCommit(@NotNull Hash commitHash) {
jumpTo(commitHash, new PairFunction<AbstractVcsLogTableModel, Hash, Integer>() {
jumpTo(commitHash, new PairFunction<GraphTableModel, Hash, Integer>() {
@Override
public Integer fun(AbstractVcsLogTableModel model, Hash hash) {
public Integer fun(GraphTableModel model, Hash hash) {
return model.getRowOfCommit(hash);
}
});
}
public void jumpToCommitByPartOfHash(@NotNull String commitHash) {
jumpTo(commitHash, new PairFunction<AbstractVcsLogTableModel, String, Integer>() {
jumpTo(commitHash, new PairFunction<GraphTableModel, String, Integer>() {
@Override
public Integer fun(AbstractVcsLogTableModel model, String hash) {
public Integer fun(GraphTableModel model, String hash) {
return model.getRowOfCommitByPartOfHash(hash);
}
});
}
public void handleAnswer(@Nullable GraphAnswer answer) {
public void handleAnswer(@Nullable GraphAnswer<Integer> answer, boolean dataCouldChange) {
if (dataCouldChange) {
((AbstractTableModel)(getTable().getModel())).fireTableDataChanged();
}
repaintUI();
if (answer == null) {
return;
}
GraphChange graphChange = answer.getGraphChange();
if (graphChange != null) {
((AbstractTableModel)(getTable().getModel())).fireTableStructureChanged();
}
GraphActionRequest actionRequest = answer.getActionRequest();
if (actionRequest instanceof JumpToRowActionRequest) {
int row = ((JumpToRowActionRequest)actionRequest).getRow();
jumpToRow(row);
if (answer.getCursorToSet() != null) {
myMainFrame.getGraphTable().setCursor(answer.getCursorToSet());
}
else if (actionRequest instanceof ChangeCursorActionRequest) {
myMainFrame.getGraphTable().setCursor(((ChangeCursorActionRequest)actionRequest).getCursor());
if (answer.getCommitToJump() != null) {
int row = myVisiblePack.getVisibleGraph().getVisibleRowIndex(answer.getCommitToJump());
if (row >= 0) {
jumpToRow(row);
}
else {
// TODO wait for the full log and then jump
}
}
}
private <T> void jumpTo(@NotNull final T commitId, @NotNull final PairFunction<AbstractVcsLogTableModel, T, Integer> rowGetter) {
AbstractVcsLogTableModel model = getModel();
private <T> void jumpTo(@NotNull final T commitId, @NotNull final PairFunction<GraphTableModel, T, Integer> rowGetter) {
GraphTableModel model = getModel();
if (model == null) {
return;
}
@@ -252,10 +252,10 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
}
@Nullable
private AbstractVcsLogTableModel getModel() {
private GraphTableModel getModel() {
TableModel model = getTable().getModel();
if (model instanceof AbstractVcsLogTableModel) {
return (AbstractVcsLogTableModel)model;
if (model instanceof GraphTableModel) {
return (GraphTableModel)model;
}
showMessage(MessageType.WARNING, "The log is not ready to search yet");
return null;
@@ -281,61 +281,29 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
}
@NotNull
public VcsLogFilterer getFilterer() {
return myFilterer;
}
@NotNull
public TIntHashSet getSelectedCommits() {
int[] selectedRows = getTable().getSelectedRows();
return getCommitsAtRows(myDataPack.getGraphFacade(), selectedRows);
return getCommitsAtRows(myVisiblePack.getVisibleGraph(), selectedRows);
}
@NotNull
private static TIntHashSet getCommitsAtRows(@NotNull GraphFacade facade, int[] rows) {
private static TIntHashSet getCommitsAtRows(@NotNull VisibleGraph<Integer> graph, int[] rows) {
TIntHashSet commits = new TIntHashSet();
for (int row : rows) {
int commit = facade.getCommitAtRow(row);
if (commit > 0) {
commits.add(commit);
if (row < graph.getVisibleCommitCount()) {
commits.add(graph.getRowInfo(row).getCommit());
}
}
return commits;
}
public void setDataPack(@NotNull DataPack dataPack) {
applyFiltersAndUpdateUi(dataPack);
}
private void applyFiltersAndUpdateUi(@NotNull final DataPack dataPack) {
runUnderModalProgress("Applying filters...", new Runnable() {
public void run() {
final TIntHashSet previouslySelected = getSelectedCommits();
final AbstractVcsLogTableModel newModel = myFilterer.applyFiltersAndUpdateUi(dataPack, getFilters());
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
myDataPack = dataPack;
setModel(newModel, myDataPack, previouslySelected);
myMainFrame.updateDataPack(myDataPack);
setLongEdgeVisibility(myUiProperties.areLongEdgesVisible());
myDataPack.getGraphFacade().performAction(new BekGraphAction(myUiProperties.isBek() ?
PermanentGraph.SortType.Bek : PermanentGraph.SortType.Normal));
fireFilterChangeEvent();
repaintUI();
if (newModel.getRowCount() == 0) { // getValueAt won't be called for empty model => need to explicitly request to load more
newModel.requestToLoadMore(EmptyRunnable.INSTANCE);
}
}
});
}
});
}
public void applyFiltersAndUpdateUi() {
ApplicationManager.getApplication().assertIsDispatchThread();
applyFiltersAndUpdateUi(myDataPack);
myFilterer.onFiltersChange(getFilters());
}
@NotNull
public VcsLogFilterer getFilterer() {
return myFilterer;
}
@NotNull
@@ -358,7 +326,6 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, task, false, null, getMainFrame().getMainComponent());
}
});
repaintUI();
}
public void setBranchesPanelVisible(boolean visible) {
@@ -382,9 +349,9 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
@Override
@NotNull
public DataPack getDataPack() {
public VisiblePack getDataPack() {
ApplicationManager.getApplication().assertIsDispatchThread();
return myDataPack;
return myVisiblePack;
}
@Override
@@ -22,7 +22,6 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.VcsLogBranchFilterImpl;
import com.intellij.vcs.log.data.VcsLogUiProperties;
import com.intellij.vcs.log.impl.VcsLogUtil;
@@ -44,7 +43,7 @@ public class BranchFilterPopupComponent extends MultipleValueFilterPopupComponen
myUiProperties = uiProperties;
}
void updateDataPack(@NotNull DataPack dataPack) {
void updateDataPack(@NotNull VcsLogDataPack dataPack) {
myDataPack = dataPack;
}
@@ -29,7 +29,6 @@ import com.intellij.ui.SearchTextFieldWithStoredHistory;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.*;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.data.VcsLogUiProperties;
import com.intellij.vcs.log.impl.VcsLogFilterCollectionImpl;
@@ -61,7 +60,7 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi {
@NotNull private final StructureFilterPopupComponent myStructureFilterComponent;
public VcsLogClassicFilterUi(@NotNull VcsLogUiImpl ui, @NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiProperties uiProperties,
@NotNull DataPack initialDataPack) {
@NotNull VcsLogDataPack initialDataPack) {
myUi = ui;
myTextFilter = new SearchTextFieldWithStoredHistory("Vcs.Log.Text.Filter.History") {
@@ -91,7 +90,7 @@ public class VcsLogClassicFilterUi implements VcsLogFilterUi {
myActionGroup.add(new FilterActionComponent(myStructureFilterComponent));
}
public void updateDataPack(@NotNull DataPack dataPack) {
public void updateDataPack(@NotNull VcsLogDataPack dataPack) {
myBranchFilterComponent.updateDataPack(dataPack);
}
@@ -33,13 +33,13 @@ import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.LoadingDetails;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.data.VisiblePack;
import com.intellij.vcs.log.printer.idea.PrintParameters;
import com.intellij.vcs.log.ui.VcsLogColorManager;
import com.intellij.vcs.log.ui.render.RefPainter;
import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel;
import com.intellij.vcs.log.ui.tables.GraphTableModel;
import net.miginfocom.swing.MigLayout;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -71,10 +71,10 @@ class DetailsPanel extends JPanel implements ListSelectionListener {
@NotNull private final MessagePanel myMessagePanel;
@NotNull private final JBLoadingPanel myLoadingPanel;
@NotNull private DataPack myDataPack;
@NotNull private VisiblePack myDataPack;
DetailsPanel(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogGraphTable graphTable, @NotNull VcsLogColorManager colorManager,
@NotNull DataPack initialDataPack) {
@NotNull VisiblePack initialDataPack) {
super(new CardLayout());
myLogDataHolder = logDataHolder;
myGraphTable = graphTable;
@@ -111,7 +111,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener {
showMessage("No commits selected");
}
void updateDataPack(@NotNull DataPack dataPack) {
void updateDataPack(@NotNull VisiblePack dataPack) {
myDataPack = dataPack;
}
@@ -127,7 +127,7 @@ class DetailsPanel extends JPanel implements ListSelectionListener {
else {
((CardLayout)getLayout()).show(this, STANDARD_LAYER);
int row = rows[0];
AbstractVcsLogTableModel<?> tableModel = (AbstractVcsLogTableModel)myGraphTable.getModel();
GraphTableModel tableModel = (GraphTableModel)myGraphTable.getModel();
Hash hash = tableModel.getHashAtRow(row);
VcsFullCommitDetails commitData = myLogDataHolder.getCommitDetailsGetter().getCommitData(row, tableModel);
if (commitData == null || hash == null) {
@@ -22,9 +22,9 @@ import com.intellij.vcs.log.VcsLog;
import com.intellij.vcs.log.VcsLogDataKeys;
import com.intellij.vcs.log.VcsLogFilterUi;
import com.intellij.vcs.log.VcsLogSettings;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.data.VcsLogUiProperties;
import com.intellij.vcs.log.data.VisiblePack;
import com.intellij.vcs.log.graph.impl.facade.bek.BekSorter;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import com.intellij.vcs.log.ui.filter.VcsLogClassicFilterUi;
@@ -60,7 +60,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
public MainFrame(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiImpl vcsLogUI, @NotNull Project project,
@NotNull VcsLogSettings settings, @NotNull VcsLogUiProperties uiProperties, @NotNull VcsLog log,
@NotNull DataPack initialDataPack) {
@NotNull VisiblePack initialDataPack) {
// collect info
myLogDataHolder = logDataHolder;
myUI = vcsLogUI;
@@ -124,7 +124,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
* Components may want to update their fields and/or rebuild.
* @param dataPack new data pack.
*/
public void updateDataPack(@NotNull DataPack dataPack) {
public void updateDataPack(@NotNull VisiblePack dataPack) {
myFilterUi.updateDataPack(dataPack);
myDetailsPanel.updateDataPack(dataPack);
myGraphTable.updateDataPack(dataPack);
@@ -336,7 +336,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
@Override
public boolean isSelected(AnActionEvent e) {
return !myUI.areLongEdgesHidden();
return !myUI.getDataPack().getVisibleGraph().getActionController().areLongEdgesHidden();
}
@Override
@@ -27,26 +27,28 @@ import com.intellij.ui.ColoredTableCellRenderer;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.TableScrollingUtil;
import com.intellij.ui.table.JBTable;
import com.intellij.util.PairFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsLogHighlighter;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.graph.*;
import com.intellij.vcs.log.data.VisiblePack;
import com.intellij.vcs.log.graph.ColorGenerator;
import com.intellij.vcs.log.graph.PrintElement;
import com.intellij.vcs.log.graph.actions.GraphAnswer;
import com.intellij.vcs.log.graph.actions.GraphMouseAction;
import com.intellij.vcs.log.printer.idea.GraphCellPainter;
import com.intellij.vcs.log.printer.idea.PositionUtil;
import com.intellij.vcs.log.printer.idea.SimpleGraphCellPainter;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import com.intellij.vcs.log.ui.render.GraphCommitCell;
import com.intellij.vcs.log.ui.render.GraphCommitCellRender;
import com.intellij.vcs.log.ui.tables.AbstractVcsLogTableModel;
import com.intellij.vcs.log.ui.tables.GraphTableModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
@@ -78,14 +80,22 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
@NotNull private final Collection<VcsLogHighlighter> myHighlighters = ContainerUtil.newArrayList();
@NotNull private DataPack myDataPack;
private final GraphCellPainter myGraphCellPainter = new SimpleGraphCellPainter(new com.intellij.vcs.log.printer.idea.ColorGenerator() {
@Override
public Color getColor(int colorId) {
return ColorGenerator.getColor(colorId);
}
});
public VcsLogGraphTable(@NotNull VcsLogUiImpl UI, @NotNull final VcsLogDataHolder logDataHolder, @NotNull DataPack initialDataPack) {
@NotNull private VisiblePack myDataPack;
public VcsLogGraphTable(@NotNull VcsLogUiImpl UI, @NotNull final VcsLogDataHolder logDataHolder, @NotNull VisiblePack initialDataPack) {
super();
myUI = UI;
myLogDataHolder = logDataHolder;
myDataPack = initialDataPack;
myGraphCommitCellRender = new GraphCommitCellRender(myUI.getColorManager(), logDataHolder, myDataPack.getGraphFacade(), this);
myGraphCommitCellRender = new GraphCommitCellRender(myUI.getColorManager(), logDataHolder, myGraphCellPainter,
myDataPack.getVisibleGraph(), this);
setDefaultRenderer(VirtualFile.class, new RootCellRenderer(myUI, myLogDataHolder.isMultiRoot()));
setDefaultRenderer(GraphCommitCell.class, myGraphCommitCellRender);
@@ -95,16 +105,6 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
setShowHorizontalLines(false);
setIntercellSpacing(new Dimension(0, 0));
getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
int selectedRow = getSelectedRow();
if (selectedRow >= 0) {
myUI.click(selectedRow);
}
}
});
MouseAdapter mouseAdapter = new MyMouseAdapter();
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
@@ -127,17 +127,17 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
private void setColumnPreferredSize() {
for (int i = 0; i < getColumnCount(); i++) {
TableColumn column = getColumnModel().getColumn(i);
if (i == AbstractVcsLogTableModel.ROOT_COLUMN) { // thin stripe or nothing
if (i == GraphTableModel.ROOT_COLUMN) { // thin stripe or nothing
int rootWidth = myUI.getColorManager().isMultipleRoots() ? ROOT_INDICATOR_WIDTH : 0;
// NB: all further instructions and their order are important, otherwise the minimum size which is less than 15 won't be applied
column.setMinWidth(rootWidth);
column.setMaxWidth(rootWidth);
column.setPreferredWidth(rootWidth);
}
else if (i == AbstractVcsLogTableModel.COMMIT_COLUMN) { // let commit message occupy as much as possible
else if (i == GraphTableModel.COMMIT_COLUMN) { // let commit message occupy as much as possible
column.setPreferredWidth(Short.MAX_VALUE);
}
else if (i == AbstractVcsLogTableModel.AUTHOR_COLUMN) { // detect author with the longest name
else if (i == GraphTableModel.AUTHOR_COLUMN) { // detect author with the longest name
// to avoid querying the last row (it would lead to full graph loading)
int maxRowsToCheck = Math.min(MAX_ROWS_TO_CALC_WIDTH, getRowCount() - MAX_ROWS_TO_CALC_OFFSET);
if (maxRowsToCheck < 0) { // but if the log is small, check all of them
@@ -147,7 +147,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
column.setMinWidth(Math.min(contentWidth, MAX_DEFAULT_AUTHOR_COLUMN_WIDTH));
column.setWidth(column.getMinWidth());
}
else if (i == AbstractVcsLogTableModel.DATE_COLUMN) { // all dates have nearly equal sizes
else if (i == GraphTableModel.DATE_COLUMN) { // all dates have nearly equal sizes
Font tableFont = UIManager.getFont("Table.font");
column.setMinWidth(getFontMetrics(tableFont).stringWidth("mm" + DateFormatUtil.formatDateTime(new Date())));
column.setWidth(column.getMinWidth());
@@ -172,7 +172,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
if (column < 0 || row < 0) {
return null;
}
if (column == AbstractVcsLogTableModel.ROOT_COLUMN) {
if (column == GraphTableModel.ROOT_COLUMN) {
Object at = getValueAt(row, column);
if (at instanceof VirtualFile) {
return ((VirtualFile)at).getPresentableUrl();
@@ -213,10 +213,10 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
@Nullable
public List<Change> getSelectedChanges() {
TableModel model = getModel();
if (!(model instanceof AbstractVcsLogTableModel)) {
if (!(model instanceof GraphTableModel)) {
return null;
}
List<Change> changes = ((AbstractVcsLogTableModel)model).getSelectedChanges(sortSelectedRows());
List<Change> changes = ((GraphTableModel)model).getSelectedChanges(sortSelectedRows());
return changes == null ? null : CommittedChangesTreeBrowser.zipChanges(changes);
}
@@ -241,7 +241,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
public void performCopy(@NotNull DataContext dataContext) {
List<String> hashes = ContainerUtil.newArrayList();
for (int row : getSelectedRows()) {
Hash hash = ((AbstractVcsLogTableModel)getModel()).getHashAtRow(row);
Hash hash = ((GraphTableModel)getModel()).getHashAtRow(row);
if (hash != null) {
hashes.add(hash.asString());
}
@@ -261,9 +261,9 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
return true;
}
public void updateDataPack(@NotNull DataPack dataPack) {
public void updateDataPack(@NotNull VisiblePack dataPack) {
myDataPack = dataPack;
myGraphCommitCellRender.updateGraphFacade(dataPack.getGraphFacade());
myGraphCommitCellRender.updateVisibleGraph(dataPack.getVisibleGraph());
}
public void addHighlighter(@NotNull VcsLogHighlighter highlighter) {
@@ -281,7 +281,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
public void applyHighlighters(@NotNull Component rendererComponent, int row, boolean selected) {
boolean fgUpdated = false;
for (VcsLogHighlighter highlighter : myHighlighters) {
Color color = highlighter.getForeground(myDataPack.getGraphFacade().getCommitAtRow(row), selected);
Color color = highlighter.getForeground(myDataPack.getVisibleGraph().getRowInfo(row).getCommit(), selected);
if (color != null) {
rendererComponent.setForeground(color);
fgUpdated = true;
@@ -306,12 +306,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
}
if (e.getClickCount() == 1) {
performAction(e, new PairFunction<Integer, Point, GraphAction>() {
@Override
public GraphAction fun(Integer row, Point point) {
return new ClickGraphAction(row, point);
}
});
performAction(e, MyGraphMouseAction.Type.CLICK);
}
}
@@ -321,24 +316,22 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
else {
performAction(e, new PairFunction<Integer, Point, GraphAction>() {
@Override
public GraphAction fun(Integer row, Point point) {
return new MouseOverAction(row, point);
}
});
performAction(e, MyGraphMouseAction.Type.OVER);
}
}
private void performAction(@NotNull MouseEvent e, @NotNull PairFunction<Integer, Point, GraphAction> actionConstructor) {
private void performAction(@NotNull MouseEvent e, @NotNull final MyGraphMouseAction.Type actionType) {
int row = PositionUtil.getRowIndex(e.getPoint());
if (row > getRowCount() - 1) {
return;
}
Point point = calcPoint4Graph(e.getPoint());
GraphFacade graphFacade = myDataPack.getGraphFacade();
GraphAnswer answer = graphFacade.performAction(actionConstructor.fun(row, point));
myUI.handleAnswer(answer);
Collection<PrintElement> printElements = myDataPack.getVisibleGraph().getRowInfo(row).getPrintElements();
PrintElement printElement = myGraphCellPainter.mouseOver(printElements, point.x, point.y);
GraphAnswer<Integer> answer = myDataPack.getVisibleGraph().getActionController().performMouseAction(
new MyGraphMouseAction(printElement, actionType));
myUI.handleAnswer(answer, actionType == MyGraphMouseAction.Type.CLICK && printElement != null);
}
private boolean isAboveLink(MouseEvent e) {
@@ -354,6 +347,28 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
public void mouseExited(MouseEvent e) {
// Do nothing
}
private class MyGraphMouseAction implements GraphMouseAction {
private final PrintElement myPrintElement;
private final Type myActionType;
public MyGraphMouseAction(PrintElement printElement, Type actionType) {
myPrintElement = printElement;
myActionType = actionType;
}
@Nullable
@Override
public PrintElement getAffectedElement() {
return myPrintElement;
}
@NotNull
@Override
public Type getType() {
return myActionType;
}
}
}
@NotNull
@@ -362,7 +377,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
}
private int getXOffset() {
TableColumn rootColumn = getColumnModel().getColumn(AbstractVcsLogTableModel.ROOT_COLUMN);
TableColumn rootColumn = getColumnModel().getColumn(GraphTableModel.ROOT_COLUMN);
return myLogDataHolder.isMultiRoot() ? rootColumn.getWidth() : 0;
}
@@ -1,147 +0,0 @@
package com.intellij.vcs.log.ui.render;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ColoredTableCellRenderer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.graph.PaintInfo;
import com.intellij.vcs.log.ui.VcsLogColorManager;
import com.intellij.vcs.log.ui.frame.VcsLogGraphTable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public abstract class AbstractPaddingCellRender extends ColoredTableCellRenderer {
private static final Logger LOG = Logger.getInstance(AbstractPaddingCellRender.class);
@NotNull private final VcsLogDataHolder myDataHolder;
@NotNull private final VcsLogGraphTable myGraphTable;
@NotNull private final RefPainter myRefPainter;
@NotNull private final IssueLinkRenderer myIssueLinkRenderer;
@Nullable private PaintInfo myGraphImage;
@Nullable private Collection<VcsRef> myRefs;
protected AbstractPaddingCellRender(@NotNull VcsLogColorManager colorManager, @NotNull VcsLogDataHolder dataHolder,
@NotNull VcsLogGraphTable table) {
myDataHolder = dataHolder;
myGraphTable = table;
myRefPainter = new RefPainter(colorManager, false);
myIssueLinkRenderer = new IssueLinkRenderer(dataHolder.getProject(), this);
}
@Nullable
protected abstract PaintInfo getGraphImage(int row);
@Override
protected void customizeCellRenderer(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
return;
}
GraphCommitCell cell = getAssertCommitCell(value);
myGraphImage = getGraphImage(row);
myRefs = cell.getRefsToThisCommit();
int graphPadding;
if (myGraphImage != null) {
graphPadding = myGraphImage.getWidth();
if (graphPadding < 2) { // TODO temporary diagnostics: why does graph sometimes disappear
LOG.error("Too small image width: " + graphPadding);
}
}
else {
graphPadding = 0;
}
int textPadding = graphPadding + calcRefsPadding(myRefs);
setBorder(null);
append("");
appendFixedTextFragmentWidth(textPadding);
myGraphTable.applyHighlighters(this, row, isSelected);
myIssueLinkRenderer.appendTextWithLinks(cell.getText());
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (myRefs != null) {
int graphPadding = myGraphImage != null ? myGraphImage.getWidth() : 0;
drawRefs((Graphics2D)g, myRefs, graphPadding);
}
if (myGraphImage != null) {
UIUtil.drawImage(g, myGraphImage.getImage(), 0, 0, null);
}
else { // TODO temporary diagnostics: why does graph sometimes disappear
LOG.error("Image is null");
}
}
private static GraphCommitCell getAssertCommitCell(Object value) {
assert value instanceof GraphCommitCell : "Value of incorrect class was supplied: " + value;
return (GraphCommitCell)value;
}
protected void drawRefs(@NotNull Graphics2D g2, @NotNull Collection<VcsRef> refs, int padding) {
myRefPainter.drawLabels(g2, collectLabelsForRefs(refs), padding);
}
@NotNull
private Map<String, Color> collectLabelsForRefs(@NotNull Collection<VcsRef> refs) {
if (refs.isEmpty()) {
return Collections.emptyMap();
}
VirtualFile root = refs.iterator().next().getRoot(); // all refs are from the same commit => they have the same root
refs = ContainerUtil.sorted(refs, myDataHolder.getLogProvider(root).getReferenceManager().getComparator());
List<VcsRef> branches = getBranches(refs);
Collection<VcsRef> tags = ContainerUtil.subtract(refs, branches);
return getLabelsForRefs(branches, tags);
}
protected int calcRefsPadding(@NotNull Collection<VcsRef> refs) {
return myRefPainter.padding(collectLabelsForRefs(refs).keySet(), this.getFontMetrics(RefPainter.DEFAULT_FONT));
}
@NotNull
private static Map<String, Color> getLabelsForRefs(@NotNull List<VcsRef> branches, @NotNull Collection<VcsRef> tags) {
Map<String, Color> labels = ContainerUtil.newLinkedHashMap();
for (VcsRef branch : branches) {
labels.put(branch.getName(), branch.getType().getBackgroundColor());
}
if (!tags.isEmpty()) {
VcsRef firstTag = tags.iterator().next();
Color color = firstTag.getType().getBackgroundColor();
if (tags.size() > 1) {
labels.put(firstTag.getName() + " +", color);
}
else {
labels.put(firstTag.getName(), color);
}
}
return labels;
}
private static List<VcsRef> getBranches(Collection<VcsRef> refs) {
return ContainerUtil.filter(refs, new Condition<VcsRef>() {
@Override
public boolean value(VcsRef ref) {
return ref.getType().isBranch();
}
});
}
}
@@ -1,46 +1,199 @@
/*
* Copyright 2000-2013 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.vcs.log.ui.render;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkRenderer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ColoredTableCellRenderer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.graph.GraphFacade;
import com.intellij.vcs.log.graph.PaintInfo;
import com.intellij.vcs.log.graph.PrintElement;
import com.intellij.vcs.log.graph.VisibleGraph;
import com.intellij.vcs.log.printer.idea.GraphCellPainter;
import com.intellij.vcs.log.printer.idea.PrintParameters;
import com.intellij.vcs.log.ui.VcsLogColorManager;
import com.intellij.vcs.log.ui.frame.VcsLogGraphTable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class GraphCommitCellRender extends AbstractPaddingCellRender {
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@NotNull private GraphFacade myGraphFacade;
public class GraphCommitCellRender extends ColoredTableCellRenderer {
public GraphCommitCellRender(@NotNull VcsLogColorManager colorManager, @NotNull VcsLogDataHolder dataHolder,
@NotNull GraphFacade graphFacade, @NotNull VcsLogGraphTable table) {
super(colorManager, dataHolder, table);
myGraphFacade = graphFacade;
private static final Logger LOG = Logger.getInstance(GraphCommitCellRender.class);
@NotNull private final VcsLogDataHolder myDataHolder;
@NotNull private final GraphCellPainter myPainter;
@NotNull private final VcsLogGraphTable myGraphTable;
@NotNull private final RefPainter myRefPainter;
@NotNull private final IssueLinkRenderer myIssueLinkRenderer;
@NotNull private VisibleGraph<Integer> myVisibleGraph;
@Nullable private PaintInfo myGraphImage;
@Nullable private Collection<VcsRef> myRefs;
public GraphCommitCellRender(@NotNull VcsLogColorManager colorManager,
@NotNull VcsLogDataHolder dataHolder,
@NotNull GraphCellPainter painter,
@NotNull VisibleGraph<Integer> graph,
@NotNull VcsLogGraphTable table) {
myDataHolder = dataHolder;
myPainter = painter;
myVisibleGraph = graph;
myGraphTable = table;
myRefPainter = new RefPainter(colorManager, false);
myIssueLinkRenderer = new IssueLinkRenderer(dataHolder.getProject(), this);
}
public void updateVisibleGraph(@NotNull VisibleGraph<Integer> visibleGraph) {
myVisibleGraph = visibleGraph;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (myRefs != null) {
int graphPadding = myGraphImage != null ? myGraphImage.getWidth() : 0;
drawRefs((Graphics2D)g, myRefs, graphPadding);
}
if (myGraphImage != null) {
UIUtil.drawImage(g, myGraphImage.getImage(), 0, 0, null);
}
else { // TODO temporary diagnostics: why does graph sometimes disappear
LOG.error("Image is null");
}
}
@Override
protected void customizeCellRenderer(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value == null) {
return;
}
GraphCommitCell cell = getAssertCommitCell(value);
myGraphImage = getGraphImage(row);
myRefs = cell.getRefsToThisCommit();
int graphPadding;
if (myGraphImage != null) {
graphPadding = myGraphImage.getWidth();
if (graphPadding < 2) { // TODO temporary diagnostics: why does graph sometimes disappear
LOG.error("Too small image width: " + graphPadding);
}
}
else {
graphPadding = 0;
}
int textPadding = graphPadding + calcRefsPadding(myRefs);
setBorder(null);
append("");
appendFixedTextFragmentWidth(textPadding);
myGraphTable.applyHighlighters(this, row, isSelected);
myIssueLinkRenderer.appendTextWithLinks(cell.getText());
}
@Nullable
@Override
protected PaintInfo getGraphImage(int row) {
return myGraphFacade.paint(row);
private PaintInfo getGraphImage(int row) {
Collection<PrintElement> printElements = myVisibleGraph.getRowInfo(row).getPrintElements();
int maxIndex = 0;
for (PrintElement printElement : printElements) {
maxIndex = Math.max(maxIndex, printElement.getPositionInCurrentRow());
}
maxIndex++;
final BufferedImage image =
UIUtil.createImage(PrintParameters.WIDTH_NODE * (maxIndex + 4), PrintParameters.HEIGHT_CELL, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = image.createGraphics();
myPainter.draw(g2, printElements);
final int width = maxIndex * PrintParameters.WIDTH_NODE;
return new PaintInfo(image, width);
}
public void updateGraphFacade(@NotNull GraphFacade graphFacade) {
myGraphFacade = graphFacade;
private static GraphCommitCell getAssertCommitCell(Object value) {
assert value instanceof GraphCommitCell : "Value of incorrect class was supplied: " + value;
return (GraphCommitCell)value;
}
private void drawRefs(@NotNull Graphics2D g2, @NotNull Collection<VcsRef> refs, int padding) {
myRefPainter.drawLabels(g2, collectLabelsForRefs(refs), padding);
}
@NotNull
private Map<String, Color> collectLabelsForRefs(@NotNull Collection<VcsRef> refs) {
if (refs.isEmpty()) {
return Collections.emptyMap();
}
VirtualFile root = refs.iterator().next().getRoot(); // all refs are from the same commit => they have the same root
refs = ContainerUtil.sorted(refs, myDataHolder.getLogProvider(root).getReferenceManager().getComparator());
List<VcsRef> branches = getBranches(refs);
Collection<VcsRef> tags = ContainerUtil.subtract(refs, branches);
return getLabelsForRefs(branches, tags);
}
private int calcRefsPadding(@NotNull Collection<VcsRef> refs) {
return myRefPainter.padding(collectLabelsForRefs(refs).keySet(), this.getFontMetrics(RefPainter.DEFAULT_FONT));
}
@NotNull
private static Map<String, Color> getLabelsForRefs(@NotNull List<VcsRef> branches, @NotNull Collection<VcsRef> tags) {
Map<String, Color> labels = ContainerUtil.newLinkedHashMap();
for (VcsRef branch : branches) {
labels.put(branch.getName(), branch.getType().getBackgroundColor());
}
if (!tags.isEmpty()) {
VcsRef firstTag = tags.iterator().next();
Color color = firstTag.getType().getBackgroundColor();
if (tags.size() > 1) {
labels.put(firstTag.getName() + " +", color);
}
else {
labels.put(firstTag.getName(), color);
}
}
return labels;
}
private static List<VcsRef> getBranches(Collection<VcsRef> refs) {
return ContainerUtil.filter(refs, new Condition<VcsRef>() {
@Override
public boolean value(VcsRef ref) {
return ref.getType().isBranch();
}
});
}
private static class PaintInfo {
private int myWidth;
@NotNull private Image myImage;
PaintInfo(@NotNull Image image, int width) {
myImage = image;
myWidth = width;
}
@NotNull
Image getImage() {
return myImage;
}
/**
* Returns the "interesting" width of the painted image, i.e. the width which the text in the table should be offset by. <br/>
* It can be smaller than the width of {@link #getImage() the image}, because we allow the text to cover part of the graph
* (some diagonal edges, etc.)
*/
int getWidth() {
return myWidth;
}
}
}
@@ -1,185 +0,0 @@
package com.intellij.vcs.log.ui.tables;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.impl.NullVirtualFile;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsShortCommitDetails;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.LoadMoreStage;
import com.intellij.vcs.log.data.LoadingDetails;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class AbstractVcsLogTableModel<CommitColumnClass> extends AbstractTableModel {
public static final int ROOT_COLUMN = 0;
public static final int COMMIT_COLUMN = 1;
public static final int AUTHOR_COLUMN = 2;
public static final int DATE_COLUMN = 3;
private static final int COLUMN_COUNT = DATE_COLUMN + 1;
private static final String[] COLUMN_NAMES = {"", "Subject", "Author", "Date"};
@NotNull private final VcsLogDataHolder myLogDataHolder;
@NotNull protected final VcsLogUiImpl myUi;
@NotNull protected final DataPack myDataPack;
@NotNull private final LoadMoreStage myLoadMoreStage;
@NotNull private final AtomicBoolean myLoadMoreWasRequested = new AtomicBoolean();
protected AbstractVcsLogTableModel(@NotNull VcsLogDataHolder logDataHolder, @NotNull VcsLogUiImpl ui, @NotNull DataPack dataPack,
@NotNull LoadMoreStage loadMoreStage) {
myLogDataHolder = logDataHolder;
myUi = ui;
myDataPack = dataPack;
myLoadMoreStage = loadMoreStage;
}
@Override
public final int getColumnCount() {
return COLUMN_COUNT;
}
@Nullable
protected VcsShortCommitDetails getShortDetails(int rowIndex) {
return myLogDataHolder.getMiniDetailsGetter().getCommitData(rowIndex, this);
}
@Nullable
public VcsFullCommitDetails getFullCommitDetails(int rowIndex) {
return myLogDataHolder.getCommitDetailsGetter().getCommitData(rowIndex, this);
}
/**
* Requests the proper data provider to load more data from the log & recreate the model.
* @param onLoaded will be called upon task completion on the EDT.
*/
public void requestToLoadMore(@NotNull Runnable onLoaded) {
if (myLoadMoreWasRequested.compareAndSet(false, true) // Don't send the request to VCS twice
&& myLoadMoreStage != LoadMoreStage.ALL_REQUESTED) { // or when everything possible is loaded
myUi.getTable().setPaintBusy(true);
myUi.getFilterer().requestVcs(myDataPack, myUi.getFilters(), myLoadMoreStage, onLoaded);
}
}
@NotNull
@Override
public final Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex >= getRowCount() - 1) {
requestToLoadMore(EmptyRunnable.INSTANCE);
}
VcsShortCommitDetails data = getShortDetails(rowIndex);
switch (columnIndex) {
case ROOT_COLUMN:
return getRoot(rowIndex);
case COMMIT_COLUMN:
return getCommitColumnCell(rowIndex, data);
case AUTHOR_COLUMN:
if (data == null) {
return "";
}
else {
return data.getAuthor().getName();
}
case DATE_COLUMN:
if (data == null || data.getTimestamp() < 0) {
return "";
}
else {
return DateFormatUtil.formatDateTime(data.getTimestamp());
}
default:
throw new IllegalArgumentException("columnIndex is " + columnIndex + " > " + (COLUMN_COUNT - 1));
}
}
/**
* Returns true if not all data has been loaded, i.e. there is sense to {@link #requestToLoadMore(Runnable) request more data}.
*/
public boolean canRequestMore() {
return !myUi.getFilters().isEmpty() && myLoadMoreStage != LoadMoreStage.ALL_REQUESTED;
}
/**
* Returns Changes for commits at selected rows.<br/>
* Rows are given in the order as they appear in the table, i. e. in reverse chronological order. <br/>
* Changes can be returned as-is, i.e. with duplicate changes for a single file.
* @return Changes selected in all rows, or null if this data is not ready yet.
*/
@Nullable
public List<Change> getSelectedChanges(@NotNull List<Integer> selectedRows) {
List<Change> changes = new ArrayList<Change>();
for (int row : selectedRows) {
VcsFullCommitDetails commitData = getFullCommitDetails(row);
if (commitData == null || commitData instanceof LoadingDetails) {
return null;
}
changes.addAll(commitData.getChanges());
}
return changes;
}
@NotNull
public abstract VirtualFile getRoot(int rowIndex);
@NotNull
protected abstract CommitColumnClass getCommitColumnCell(int index, @Nullable VcsShortCommitDetails details);
@NotNull
protected abstract Class<CommitColumnClass> getCommitColumnClass();
/**
* Returns the Hash of the commit displayed in the given row.
* May be null if there is no commit in the row
* (such situations may appear, for example, if graph is filtered by branch, as described in IDEA-115442).
*/
@Nullable
public abstract Hash getHashAtRow(int row);
/**
* Returns the row number containing the given commit,
* or -1 if the requested commit is not contained in this table model (possibly because not all data has been loaded).
*/
public abstract int getRowOfCommit(@NotNull Hash hash);
/**
* Returns the number of the first row which contains a commit which hash starts with the given value,
* or -1 if no such commit was found (possibly because not all data has been loaded).
*/
public abstract int getRowOfCommitByPartOfHash(@NotNull String hash);
@Override
public Class<?> getColumnClass(int column) {
switch (column) {
case ROOT_COLUMN:
return VirtualFile.class;
case COMMIT_COLUMN:
return getCommitColumnClass();
case AUTHOR_COLUMN:
return String.class;
case DATE_COLUMN:
return String.class;
default:
throw new IllegalArgumentException("columnIndex is " + column + " > " + (COLUMN_COUNT - 1));
}
}
@Override
public String getColumnName(int column) {
return COLUMN_NAMES[column];
}
}
@@ -1,78 +0,0 @@
/*
* Copyright 2000-2014 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.vcs.log.ui.tables;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.VcsShortCommitDetails;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.LoadMoreStage;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import com.intellij.vcs.log.ui.render.GraphCommitCell;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
public class EmptyTableModel extends AbstractVcsLogTableModel<GraphCommitCell> {
public EmptyTableModel(@NotNull DataPack dataPack, @NotNull VcsLogDataHolder logDataHolder,
@NotNull VcsLogUiImpl ui, @NotNull LoadMoreStage loadMoreStage) {
super(logDataHolder, ui, dataPack, loadMoreStage);
}
@Override
public int getRowCount() {
return 0;
}
@NotNull
@Override
public VirtualFile getRoot(int rowIndex) {
throw new AssertionError("Root shouldn't be requested for empty table model. row: " + rowIndex);
}
@NotNull
@Override
protected GraphCommitCell getCommitColumnCell(int index, @Nullable VcsShortCommitDetails details) {
return new GraphCommitCell("", Collections.<VcsRef>emptyList());
}
@NotNull
@Override
protected Class<GraphCommitCell> getCommitColumnClass() {
return GraphCommitCell.class;
}
@Nullable
@Override
public Hash getHashAtRow(int row) {
return null;
}
@Override
public int getRowOfCommit(@NotNull Hash hash) {
return -1;
}
@Override
public int getRowOfCommitByPartOfHash(@NotNull String hash) {
return -1;
}
}
@@ -2,62 +2,63 @@ package com.intellij.vcs.log.ui.tables;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.vcs.log.Hash;
import com.intellij.vcs.log.VcsFullCommitDetails;
import com.intellij.vcs.log.VcsRef;
import com.intellij.vcs.log.VcsShortCommitDetails;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.LoadMoreStage;
import com.intellij.vcs.log.data.LoadingDetails;
import com.intellij.vcs.log.data.VcsLogDataHolder;
import com.intellij.vcs.log.ui.render.GraphCommitCell;
import com.intellij.vcs.log.data.VisiblePack;
import com.intellij.vcs.log.impl.VcsLogUtil;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import com.intellij.vcs.log.ui.render.GraphCommitCell;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class GraphTableModel extends AbstractVcsLogTableModel<GraphCommitCell> {
public class GraphTableModel extends AbstractTableModel {
private static final Logger LOG = Logger.getInstance(GraphTableModel.class);
@NotNull private final DataPack myDataPack;
@NotNull private final VcsLogDataHolder myDataHolder;
public static final int ROOT_COLUMN = 0;
public static final int COMMIT_COLUMN = 1;
public static final int AUTHOR_COLUMN = 2;
public static final int DATE_COLUMN = 3;
private static final int COLUMN_COUNT = DATE_COLUMN + 1;
private static final String[] COLUMN_NAMES = {"", "Subject", "Author", "Date"};
public GraphTableModel(@NotNull DataPack dataPack, @NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl UI,
@NotNull LoadMoreStage loadMoreStage) {
super(dataHolder, UI, dataPack, loadMoreStage);
@NotNull protected final VcsLogUiImpl myUi;
@NotNull protected final VisiblePack myDataPack;
@NotNull private final VcsLogDataHolder myDataHolder;
@NotNull private final VcsLogDataHolder myLogDataHolder;
private boolean myMoreRequested;
public GraphTableModel(@NotNull VisiblePack dataPack, @NotNull VcsLogDataHolder dataHolder, @NotNull VcsLogUiImpl UI) {
myLogDataHolder = dataHolder;
myUi = UI;
myDataPack = dataPack;
myDataHolder = dataHolder;
}
@Override
public int getRowCount() {
return myDataPack.getGraphFacade().getVisibleCommitCount();
}
@Override
public void requestToLoadMore(@NotNull Runnable onLoaded) {
if (!myDataHolder.isFullLogShowing()) {
myDataHolder.showFullLog(onLoaded);
}
else if (!myUi.getFilters().isEmpty()) {
super.requestToLoadMore(onLoaded);
}
}
@Override
public boolean canRequestMore() {
return !myDataHolder.isFullLogShowing() || super.canRequestMore();
return myDataPack.getVisibleGraph().getVisibleCommitCount();
}
@NotNull
@Override
public VirtualFile getRoot(int rowIndex) {
int head = myDataPack.getGraphFacade().getInfoProvider().getRowInfo(rowIndex).getOneOfHeads();
int head = myDataPack.getVisibleGraph().getRowInfo(rowIndex).getOneOfHeads();
Collection<VcsRef> refs = myDataPack.getRefsModel().refsToCommit(head);
if (refs.isEmpty()) {
LOG.error("No references pointing to head " + head + " identified for commit at row " + rowIndex);
@@ -68,7 +69,6 @@ public class GraphTableModel extends AbstractVcsLogTableModel<GraphCommitCell> {
}
@NotNull
@Override
protected GraphCommitCell getCommitColumnCell(int rowIndex, @Nullable VcsShortCommitDetails details) {
String message = "";
List<VcsRef> refs = Collections.emptyList();
@@ -79,22 +79,14 @@ public class GraphTableModel extends AbstractVcsLogTableModel<GraphCommitCell> {
return new GraphCommitCell(message, refs);
}
@NotNull
@Override
protected Class<GraphCommitCell> getCommitColumnClass() {
return GraphCommitCell.class;
}
@Nullable
@Override
public Hash getHashAtRow(int row) {
return myDataHolder.getHash(myDataPack.getGraphFacade().getCommitAtRow(row));
return myDataHolder.getHash(myDataPack.getVisibleGraph().getRowInfo(row).getCommit());
}
@Override
public int getRowOfCommit(@NotNull final Hash hash) {
final int commitIndex = myDataHolder.getCommitIndex(hash);
return ContainerUtil.indexOf(VcsLogUtil.getVisibleCommits(myDataPack.getGraphFacade()), new Condition<Integer>() {
return ContainerUtil.indexOf(VcsLogUtil.getVisibleCommits(myDataPack.getVisibleGraph()), new Condition<Integer>() {
@Override
public boolean value(Integer integer) {
return integer == commitIndex;
@@ -102,10 +94,112 @@ public class GraphTableModel extends AbstractVcsLogTableModel<GraphCommitCell> {
});
}
@Override
public int getRowOfCommitByPartOfHash(@NotNull String partialHash) {
Hash hash = myDataHolder.findHashByString(partialHash);
Hash hash = myDataHolder.getHashMap().findHashByString(partialHash);
return hash != null ? getRowOfCommit(hash) : -1;
}
@Override
public final int getColumnCount() {
return COLUMN_COUNT;
}
@Nullable
private VcsShortCommitDetails getShortDetails(int rowIndex) {
return myLogDataHolder.getMiniDetailsGetter().getCommitData(rowIndex, this);
}
@Nullable
public VcsFullCommitDetails getFullCommitDetails(int rowIndex) {
return myLogDataHolder.getCommitDetailsGetter().getCommitData(rowIndex, this);
}
/**
* Requests the proper data provider to load more data from the log & recreate the model.
* @param onLoaded will be called upon task completion on the EDT.
*/
public void requestToLoadMore(@NotNull Runnable onLoaded) {
myMoreRequested = true;
myUi.getFilterer().moreCommitsNeeded(onLoaded);
myUi.getTable().setPaintBusy(true);
}
@NotNull
@Override
public final Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex >= getRowCount() - 1 && canRequestMore()) {
requestToLoadMore(EmptyRunnable.INSTANCE);
}
VcsShortCommitDetails data = getShortDetails(rowIndex);
switch (columnIndex) {
case ROOT_COLUMN:
return getRoot(rowIndex);
case COMMIT_COLUMN:
return getCommitColumnCell(rowIndex, data);
case AUTHOR_COLUMN:
if (data == null) {
return "";
}
else {
return data.getAuthor().getName();
}
case DATE_COLUMN:
if (data == null || data.getTimestamp() < 0) {
return "";
}
else {
return DateFormatUtil.formatDateTime(data.getTimestamp());
}
default:
throw new IllegalArgumentException("columnIndex is " + columnIndex + " > " + (COLUMN_COUNT - 1));
}
}
/**
* Returns true if not all data has been loaded, i.e. there is sense to {@link #requestToLoadMore(Runnable) request more data}.
*/
public boolean canRequestMore() {
return !myMoreRequested && myDataPack.canRequestMore();
}
/**
* Returns Changes for commits at selected rows.<br/>
* Rows are given in the order as they appear in the table, i. e. in reverse chronological order. <br/>
* Changes can be returned as-is, i.e. with duplicate changes for a single file.
* @return Changes selected in all rows, or null if this data is not ready yet.
*/
@Nullable
public List<Change> getSelectedChanges(@NotNull List<Integer> selectedRows) {
List<Change> changes = new ArrayList<Change>();
for (int row : selectedRows) {
VcsFullCommitDetails commitData = getFullCommitDetails(row);
if (commitData == null || commitData instanceof LoadingDetails) {
return null;
}
changes.addAll(commitData.getChanges());
}
return changes;
}
@Override
public Class<?> getColumnClass(int column) {
switch (column) {
case ROOT_COLUMN:
return VirtualFile.class;
case COMMIT_COLUMN:
return GraphCommitCell.class;
case AUTHOR_COLUMN:
return String.class;
case DATE_COLUMN:
return String.class;
default:
throw new IllegalArgumentException("columnIndex is " + column + " > " + (COLUMN_COUNT - 1));
}
}
@Override
public String getColumnName(int column) {
return COLUMN_NAMES[column];
}
}
@@ -174,7 +174,8 @@ public class VcsLogRefresherTest extends VcsLogPlatformTest {
private VcsLogRefresherImpl createLoader(DataWaiter dataWaiter) {
myDataHolder = new VcsLogDataHolder(myProject, myProject, myLogProviders,
ServiceManager.getService(myProject, VcsLogSettings.class), dataWaiter);
ServiceManager.getService(myProject, VcsLogSettings.class),
ServiceManager.getService(myProject, VcsLogUiProperties.class), Consumer.EMPTY_CONSUMER);
return new VcsLogRefresherImpl(myProject, myDataHolder.getHashMap(), myLogProviders, myDataHolder.getUserRegistry(), myTopDetailsCache,
dataWaiter, FAILING_EXCEPTION_HANDLER, RECENT_COMMITS_COUNT);
}