[vcs-log] showing repository names IDEA-105467

some magic to make root column to un-shrink
merge repository cells
tooltip for root column suggests to collapse/expand
This commit is contained in:
Julia Beliaeva
2014-10-13 16:21:59 +04:00
parent b02671ec52
commit 405feed9af
5 changed files with 167 additions and 29 deletions
@@ -96,16 +96,30 @@ public class TableScrollingUtil {
}
private static int getLeadingRow(JTable table,Rectangle visibleRect) {
Point leadingPoint;
return table.rowAtPoint(getLeadingPoint(table, visibleRect));
}
private static Point getLeadingPoint(JTable table, Rectangle visibleRect) {
if (table.getComponentOrientation().isLeftToRight()) {
return new Point(visibleRect.x, visibleRect.y);
}
else {
return new Point(visibleRect.x + visibleRect.width,
visibleRect.y);
}
}
public static int getReadableRow(JTable table, int maximumHiddenPart) {
Rectangle visibleRect = table.getVisibleRect();
Point leadingPoint = getLeadingPoint(table, visibleRect);
int row = table.rowAtPoint(leadingPoint);
int column = table.columnAtPoint(leadingPoint);
if (leadingPoint.y - table.getCellRect(row, column, true).getY() <= maximumHiddenPart) {
return row;
} else {
return Math.min(row + 1, table.getRowCount() - 1); // just in case
}
if (table.getComponentOrientation().isLeftToRight()) {
leadingPoint = new Point(visibleRect.x, visibleRect.y);
}
else {
leadingPoint = new Point(visibleRect.x + visibleRect.width,
visibleRect.y);
}
return table.rowAtPoint(leadingPoint);
}
private static int getTrailingRow(JTable table,Rectangle visibleRect) {
@@ -43,6 +43,7 @@ public class VcsLogUiProperties implements PersistentStateComponent<VcsLogUiProp
public boolean SHOW_DETAILS = true;
public boolean LONG_EDGES_VISIBLE = false;
public boolean BEK_SORT = false;
public boolean SHOW_ROOT_NAMES = false;
public Deque<UserGroup> RECENTLY_FILTERED_USER_GROUPS = new ArrayDeque<UserGroup>();
public Deque<UserGroup> RECENTLY_FILTERED_BRANCH_GROUPS = new ArrayDeque<UserGroup>();
}
@@ -126,6 +127,14 @@ public class VcsLogUiProperties implements PersistentStateComponent<VcsLogUiProp
myState.BEK_SORT = isBek;
}
public boolean isShowRootNames() {
return myState.SHOW_ROOT_NAMES;
}
public void setShowRootNames(boolean isShowRootNames) {
myState.SHOW_ROOT_NAMES = isShowRootNames;
}
public static class UserGroup {
public List<String> users = new ArrayList<String>();
@@ -185,6 +185,14 @@ public class VcsLogUiImpl implements VcsLogUi, Disposable {
return myUiProperties.isBek();
}
public void setShowRootNames(boolean isShowRootNames) {
myUiProperties.setShowRootNames(isShowRootNames);
}
public boolean isShowRootNames() {
return myUiProperties.isShowRootNames();
}
public void jumpToCommit(@NotNull Hash commitHash) {
jumpTo(commitHash, new PairFunction<GraphTableModel, Hash, Integer>() {
@Override
@@ -91,7 +91,7 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
myToolbar = createActionsToolbar();
myDetailsSplitter = new Splitter(true, 0.7f);
myDetailsSplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myGraphTable));
myDetailsSplitter.setFirstComponent(setupScrolledGraph());
setupDetailsSplitter(myUiProperties.isShowDetails());
JComponent toolbars = new JPanel(new BorderLayout());
@@ -156,6 +156,12 @@ public class MainFrame extends JPanel implements TypeSafeDataProvider {
myDetailsSplitter.setSecondComponent(state ? myDetailsPanel : null);
}
private JScrollPane setupScrolledGraph() {
JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myGraphTable);
myGraphTable.viewportSet(scrollPane.getViewport());
return scrollPane;
}
private static void setDefaultEmptyText(ChangesBrowser changesBrowser) {
changesBrowser.getViewer().setEmptyText("");
}
@@ -18,14 +18,17 @@ package com.intellij.vcs.log.ui.frame;
import com.intellij.ide.CopyProvider;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser;
import com.intellij.openapi.vcs.changes.issueLinks.TableLinkMouseListener;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.ColoredTableCellRenderer;
import com.intellij.ui.JBColor;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.TableScrollingUtil;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.table.JBTable;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.DateFormatUtil;
@@ -49,6 +52,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
@@ -67,6 +74,7 @@ import static com.intellij.vcs.log.printer.idea.PrintParameters.HEIGHT_CELL;
public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, CopyProvider {
private static final int ROOT_INDICATOR_WIDTH = 5;
private static final int ROOT_NAME_MAX_WIDTH = 200;
private static final int MAX_DEFAULT_AUTHOR_COLUMN_WIDTH = 200;
private static final int MAX_ROWS_TO_CALC_WIDTH = 1000;
private static final int MAX_ROWS_TO_CALC_OFFSET = 100;
@@ -97,7 +105,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
myGraphCommitCellRender = new GraphCommitCellRender(myUI.getColorManager(), logDataHolder, myGraphCellPainter,
myDataPack.getVisibleGraph(), this);
setDefaultRenderer(VirtualFile.class, new RootCellRenderer(myUI, myLogDataHolder.isMultiRoot()));
setDefaultRenderer(VirtualFile.class, new RootCellRenderer(myUI));
setDefaultRenderer(GraphCommitCell.class, myGraphCommitCellRender);
setDefaultRenderer(String.class, new StringCellRenderer());
@@ -127,12 +135,8 @@ 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 == 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);
if (i == GraphTableModel.ROOT_COLUMN) { // thin stripe, or root name, or nothing
setRootColumnSize(column);
}
else if (i == GraphTableModel.COMMIT_COLUMN) { // let commit message occupy as much as possible
column.setPreferredWidth(Short.MAX_VALUE);
@@ -155,6 +159,33 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
}
}
private void setRootColumnSize(TableColumn column) {
int rootWidth;
if (!myUI.getColorManager().isMultipleRoots()) {
rootWidth = 0;
}
else if (!myUI.isShowRootNames()) {
rootWidth = ROOT_INDICATOR_WIDTH;
}
else {
rootWidth = Math.min(calculateMaxRootWidth(), ROOT_NAME_MAX_WIDTH);
}
// 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);
}
private int calculateMaxRootWidth() {
int width = 0;
for (VirtualFile file : myLogDataHolder.getRoots()) {
Font tableFont = UIManager.getFont("Table.font");
width = Math.max(getFontMetrics(tableFont).stringWidth(file.getName() + " "), width);
}
return width;
}
private int calcMaxContentColumnWidth(int columnIndex, int maxRowsToCheck) {
int maxWidth = 0;
for (int row = 0; row < maxRowsToCheck; row++) {
@@ -175,7 +206,7 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
if (column == GraphTableModel.ROOT_COLUMN) {
Object at = getValueAt(row, column);
if (at instanceof VirtualFile) {
return ((VirtualFile)at).getPresentableUrl();
return "<html><b>" + ((VirtualFile)at).getPresentableUrl() + "</b><br/>Click to " + (myUI.isShowRootNames() ? "collapse" : "expand") + "</html>";
}
}
return null;
@@ -292,6 +323,19 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
}
}
public void viewportSet(JViewport viewport) {
viewport.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
TableModel model = getModel();
if (model instanceof AbstractTableModel) {
Couple<Integer> visibleRows = TableScrollingUtil.getVisibleRows(VcsLogGraphTable.this);
((AbstractTableModel)model).fireTableChanged(new TableModelEvent(model, visibleRows.first - 1, visibleRows.second, GraphTableModel.ROOT_COLUMN));
}
}
});
}
private class MyMouseAdapter extends MouseAdapter {
private final TableLinkMouseListener myLinkListener;
@@ -306,6 +350,8 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
}
if (e.getClickCount() == 1) {
if (expandOrCollapseRoots(e)) return;
performAction(e, MyGraphMouseAction.Type.CLICK);
}
}
@@ -320,6 +366,17 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
}
}
private boolean expandOrCollapseRoots(MouseEvent e) {
int column = convertColumnIndexToModel(columnAtPoint(e.getPoint()));
if (column == GraphTableModel.ROOT_COLUMN && myLogDataHolder.isMultiRoot()) {
myUI.setShowRootNames(!myUI.isShowRootNames());
setColumnPreferredSize();
setRootColumnSize(getColumnModel().getColumn(column));
return true;
}
return false;
}
private void performAction(@NotNull MouseEvent e, @NotNull final MyGraphMouseAction.Type actionType) {
int row = PositionUtil.getRowIndex(e.getPoint());
if (row > getRowCount() - 1) {
@@ -381,34 +438,78 @@ public class VcsLogGraphTable extends JBTable implements TypeSafeDataProvider, C
return myLogDataHolder.isMultiRoot() ? rootColumn.getWidth() : 0;
}
private static class RootCellRenderer extends JPanel implements TableCellRenderer {
private static class RootCellRenderer extends JBLabel implements TableCellRenderer {
@NotNull private final VcsLogUiImpl myUi;
@NotNull private Color myColor = UIUtil.getTableBackground();
private boolean myHasBorder;
RootCellRenderer(@NotNull VcsLogUiImpl ui, boolean multiRoot) {
RootCellRenderer(@NotNull VcsLogUiImpl ui) {
super("", CENTER);
myUi = ui;
int rootWidth = multiRoot ? ROOT_INDICATOR_WIDTH : 0;
setPreferredSize(new Dimension(rootWidth, -1));
}
@Override
protected void paintComponent(Graphics g) {
g.setColor(myColor);
g.fillRect(0, 0, ROOT_INDICATOR_WIDTH - 1, HEIGHT_CELL);
UIUtil.drawLine((Graphics2D)g, ROOT_INDICATOR_WIDTH - 1, 0, ROOT_INDICATOR_WIDTH - 1, HEIGHT_CELL, null,
myUi.getColorManager().getRootIndicatorBorder());
int width = getWidth();
if (myHasBorder) {
g.fillRect(0, 0, width - 1, HEIGHT_CELL);
UIUtil.drawLine((Graphics2D)g, width - 1, 0, width - 1, HEIGHT_CELL, null, myUi.getColorManager().getRootIndicatorBorder());
}
else {
g.fillRect(0, 0, width, HEIGHT_CELL);
}
super.paintComponent(g);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
String text;
Color color;
if (value instanceof VirtualFile) {
myColor = myUi.getColorManager().getRootColor((VirtualFile)value);
VirtualFile root = (VirtualFile)value;
int readableRow = TableScrollingUtil.getReadableRow(table, Math.round(HEIGHT_CELL * 0.5f));
if (row < readableRow) {
text = "";
} else if (row == 0 || !value.equals(table.getModel().getValueAt(row - 1, column)) || readableRow == row) {
text = root.getName();
} else {
text = "";
}
color = myUi.getColorManager().getRootColor(root);
}
else {
myColor = UIUtil.getTableBackground(isSelected);
text = null;
color = UIUtil.getTableBackground(isSelected);
}
if (myUi.isShowRootNames()) {
// pale colors, honors selection, does not have border
if (isSelected) {
myColor = UIUtil.getTableBackground(isSelected);
}
else {
//we create JBColor later
//noinspection UseJBColor
Color transparentColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), 50);
myColor = new JBColor(transparentColor, transparentColor);
}
setForeground(UIUtil.getTableForeground(isSelected));
setText(text);
myHasBorder = false;
}
else {
// bright colors, does not know about the selection, has border
setForeground(UIUtil.getTableForeground(false));
myColor = color;
setText("");
myHasBorder = true;
}
return this;
}
}