package jetbrains.mps.vcs.diff.ui.common; /*Generated by MPS */ import javax.swing.JComponent; import jetbrains.mps.ide.tooltips.TooltipComponent; import java.util.Map; import jetbrains.mps.baseLanguage.tuples.runtime.Tuples; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import java.awt.Dimension; import jetbrains.mps.ide.tooltips.MPSToolTipManager; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.LinkedHashMap; import jetbrains.mps.internal.collections.runtime.ListSequence; import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple; import jetbrains.mps.smodel.ModelAccess; import java.util.HashMap; import jetbrains.mps.internal.collections.runtime.IterableUtils; import jetbrains.mps.internal.collections.runtime.ISelector; import jetbrains.mps.vcs.diff.changes.ModelChange; import java.awt.Graphics; import jetbrains.mps.internal.collections.runtime.IMapping; import javax.swing.JViewport; import java.awt.event.MouseEvent; import java.awt.Point; import jetbrains.mps.internal.collections.runtime.IWhereFilter; public class DiffEditorSeparator extends JComponent implements TooltipComponent { private static final int WIDTH = 30; private ChangeGroupLayout myChangeGroupLayout; private Map<ChangeGroup, Tuples._2<Bounds, Bounds>> myGroupsWithBounds; private Map<ChangeGroup, String> myChangeGroupDescriptions; public DiffEditorSeparator(ChangeGroupLayout changeGroupLayout) { myChangeGroupLayout = changeGroupLayout; ChangeListener viewportListener = new ChangeListener() { public void stateChanged(ChangeEvent e) { invalidateAndRepaint(); } }; getLeftViewport().addChangeListener(viewportListener); getRightViewport().addChangeListener(viewportListener); setMinimumSize(new Dimension(WIDTH, 1)); setPreferredSize(new Dimension(WIDTH, 1)); myChangeGroupLayout.addInvalidateListener(new ChangeGroupInvalidateListener() { public void changeGroupsInvalidated() { invalidateAndRepaint(); } }); MPSToolTipManager.getInstance().registerComponent(this); } private void ensureBoundsCalculated() { if (myGroupsWithBounds != null) { return; } myGroupsWithBounds = MapSequence.fromMap(new LinkedHashMap<ChangeGroup, Tuples._2<Bounds, Bounds>>(16, (float) 0.75, false)); int leftOffset = getOffset(getLeftViewport()); int rightOffset = getOffset(getRightViewport()); for (ChangeGroup group : ListSequence.fromList(myChangeGroupLayout.getChangeGroups())) { int leftStart = (int) group.getLeftBounds().start() + leftOffset; int leftEnd = (int) group.getLeftBounds().end() + leftOffset; int rightStart = (int) group.getRightBounds().start() + rightOffset; int rightEnd = (int) group.getRightBounds().end() + rightOffset; MapSequence.fromMap(myGroupsWithBounds).put(group, MultiTuple.<Bounds,Bounds>from(new Bounds(leftStart, leftEnd), new Bounds(rightStart, rightEnd))); } ModelAccess.instance().runReadAction(new Runnable() { public void run() { myChangeGroupDescriptions = MapSequence.fromMap(new HashMap<ChangeGroup, String>()); for (ChangeGroup group : ListSequence.fromList(myChangeGroupLayout.getChangeGroups())) { MapSequence.fromMap(myChangeGroupDescriptions).put(group, IterableUtils.join(ListSequence.fromList(group.getChanges()).select(new ISelector<ModelChange, String>() { public String select(ModelChange ch) { return ch.getDescription(); } }), "\n\n")); } } }); } @Override protected void paintComponent(Graphics g) { synchronized (this) { ensureBoundsCalculated(); for (IMapping<ChangeGroup, Tuples._2<Bounds, Bounds>> groupWithBounds : MapSequence.fromMap(myGroupsWithBounds)) { Bounds left = groupWithBounds.value()._0(); Bounds right = groupWithBounds.value()._1(); int[] xx = new int[]{0, getWidth(), getWidth(), 0}; int[] yy = new int[]{(int) left.start(), (int) right.start(), (int) right.end(), (int) left.end()}; g.setColor(ChangeColors.get(groupWithBounds.key().getChangeType())); g.fillPolygon(xx, yy, 4); g.setColor(ChangeColors.get(groupWithBounds.key().getChangeType()).darker()); g.drawLine(0, (int) left.start(), getWidth() - 1, (int) right.start()); g.drawLine(0, (int) left.end(), getWidth() - 1, (int) right.end()); } } } private void invalidateAndRepaint() { synchronized (this) { myGroupsWithBounds = null; myChangeGroupDescriptions = null; } repaint(); } private JViewport getLeftViewport() { return myChangeGroupLayout.getLeftComponent().getViewport(); } private JViewport getRightViewport() { return myChangeGroupLayout.getRightComponent().getViewport(); } private int getOffset(JViewport viewport) { return -viewport.getViewPosition().y + myChangeGroupLayout.getEditorVerticalOffset(); } @Override public String getMPSTooltipText(MouseEvent mouseEvent) { synchronized (this) { ensureBoundsCalculated(); final Point p = mouseEvent.getPoint(); IMapping<ChangeGroup, Tuples._2<Bounds, Bounds>> group = MapSequence.fromMap(myGroupsWithBounds).findFirst(new IWhereFilter<IMapping<ChangeGroup, Tuples._2<Bounds, Bounds>>>() { public boolean accept(IMapping<ChangeGroup, Tuples._2<Bounds, Bounds>> g) { Bounds left = g.value()._0(); Bounds right = g.value()._1(); int v1 = vectorProduct((int) left.start(), (int) right.start(), p.x, p.y); int v2 = vectorProduct((int) left.end(), (int) right.end(), p.x, p.y); return v1 > 0 && v2 < 0; } }); if (group == null) { return null; } else { return MapSequence.fromMap(myChangeGroupDescriptions).get(group.key()); } } } public void dispose() { MPSToolTipManager.getInstance().unregisterComponent(this); } private int vectorProduct(int left, int right, int x, int y) { int x1 = getWidth(); int y1 = right - left; int x2 = x; int y2 = y - left; int z = x1 * y2 - x2 * y1; return z; } }