2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.openapi.vcs.ex;
18 import com.intellij.codeInsight.hint.EditorFragmentComponent;
19 import com.intellij.codeInsight.hint.HintManagerImpl;
20 import com.intellij.openapi.actionSystem.*;
21 import com.intellij.openapi.actionSystem.ex.ActionUtil;
22 import com.intellij.openapi.diff.DiffColors;
23 import com.intellij.openapi.editor.Document;
24 import com.intellij.openapi.editor.Editor;
25 import com.intellij.openapi.editor.EditorFactory;
26 import com.intellij.openapi.editor.ScrollType;
27 import com.intellij.openapi.editor.colors.EditorColors;
28 import com.intellij.openapi.editor.colors.EditorColorsManager;
29 import com.intellij.openapi.editor.colors.EditorColorsScheme;
30 import com.intellij.openapi.editor.ex.DocumentEx;
31 import com.intellij.openapi.editor.ex.EditorEx;
32 import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
33 import com.intellij.openapi.editor.highlighter.EditorHighlighter;
34 import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
35 import com.intellij.openapi.editor.markup.ActiveGutterRenderer;
36 import com.intellij.openapi.editor.markup.LineMarkerRenderer;
37 import com.intellij.openapi.editor.markup.TextAttributes;
38 import com.intellij.openapi.fileEditor.FileDocumentManager;
39 import com.intellij.openapi.vcs.actions.ShowNextChangeMarkerAction;
40 import com.intellij.openapi.vcs.actions.ShowPrevChangeMarkerAction;
41 import com.intellij.openapi.vfs.VirtualFile;
42 import com.intellij.ui.ColoredSideBorder;
43 import com.intellij.ui.HintHint;
44 import com.intellij.ui.HintListener;
45 import com.intellij.ui.LightweightHint;
46 import com.intellij.util.ui.UIUtil;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
52 import java.awt.event.MouseAdapter;
53 import java.awt.event.MouseEvent;
54 import java.util.EventObject;
55 import java.util.List;
60 public class LineStatusTrackerDrawing {
61 private LineStatusTrackerDrawing() {
64 static TextAttributes getAttributesFor(final Range range) {
65 return new TextAttributes() {
67 public Color getErrorStripeColor() {
68 return getDiffColor(range);
73 private static void paintGutterFragment(final Editor editor, final Graphics g, final Rectangle r, final Range range) {
74 final EditorGutterComponentEx gutter = ((EditorEx)editor).getGutterComponentEx();
75 Color gutterColor = getDiffGutterColor(range);
76 Color borderColor = getDiffGutterBorderColor();
78 final int x = r.x + r.width - 3;
79 final int endX = gutter.getWhitespaceSeparatorOffset();
81 if (range.getInnerRanges() == null) { // actual painter
83 paintRect(g, gutterColor, borderColor, x, r.y, endX, r.y + r.height);
86 paintTriangle(g, gutterColor, borderColor, x, endX, r.y);
89 else { // registry: diff.status.tracker.smart
90 if (range.getType() == Range.DELETED) {
91 final int y = lineToY(editor, range.getLine1());
92 paintTriangle(g, gutterColor, borderColor, x, endX, y);
95 final int y = lineToY(editor, range.getLine1());
96 int endY = lineToY(editor, range.getLine2());
98 List<Range.InnerRange> innerRanges = range.getInnerRanges();
99 for (Range.InnerRange innerRange : innerRanges) {
100 if (innerRange.getType() == Range.DELETED) continue;
102 int start = lineToY(editor, innerRange.getLine1());
103 int end = lineToY(editor, innerRange.getLine2());
105 paintRect(g, getDiffColor(innerRange), null, x, start, endX, end);
108 for (int i = 0; i < innerRanges.size(); i++) {
109 Range.InnerRange innerRange = innerRanges.get(i);
110 if (innerRange.getType() != Range.DELETED) continue;
116 start = lineToY(editor, innerRange.getLine1());
117 end = lineToY(editor, innerRange.getLine2()) + 5;
119 else if (i == innerRanges.size() - 1) {
120 start = lineToY(editor, innerRange.getLine1()) - 5;
121 end = lineToY(editor, innerRange.getLine2());
124 start = lineToY(editor, innerRange.getLine1()) - 3;
125 end = lineToY(editor, innerRange.getLine2()) + 3;
128 paintRect(g, getDiffColor(innerRange), null, x, start, endX, end);
131 paintRect(g, null, borderColor, x, y, endX, endY);
136 private static int lineToY(@NotNull Editor editor, int line) {
137 Document document = editor.getDocument();
138 if (line >= document.getLineCount()) {
139 int y = lineToY(editor, document.getLineCount() - 1);
140 return y + editor.getLineHeight() * (line - document.getLineCount() + 1);
142 return editor.logicalPositionToXY(editor.offsetToLogicalPosition(document.getLineStartOffset(line))).y;
145 private static void paintRect(@NotNull Graphics g, @Nullable Color color, @Nullable Color borderColor, int x1, int y1, int x2, int y2) {
148 g.fillRect(x1, y1, x2 - x1, y2 - y1);
150 if (borderColor != null) {
151 g.setColor(borderColor);
152 UIUtil.drawLine(g, x1, y1, x2 - 1, y1);
153 UIUtil.drawLine(g, x1, y1, x1, y2 - 1);
154 UIUtil.drawLine(g, x1, y2 - 1, x2 - 1, y2 - 1);
158 private static void paintTriangle(@NotNull Graphics g, @Nullable Color color, @Nullable Color borderColor, int x1, int x2, int y) {
161 final int[] xPoints = new int[]{x1, x1, x2};
162 final int[] yPoints = new int[]{y - size, y + size, y};
166 g.fillPolygon(xPoints, yPoints, xPoints.length);
168 if (borderColor != null) {
169 g.setColor(borderColor);
170 g.drawPolygon(xPoints, yPoints, xPoints.length);
174 public static LineMarkerRenderer createRenderer(final Range range, final LineStatusTracker tracker) {
175 return new ActiveGutterRenderer() {
176 public void paint(final Editor editor, final Graphics g, final Rectangle r) {
177 paintGutterFragment(editor, g, r, range);
180 public void doAction(final Editor editor, final MouseEvent e) {
182 final JComponent comp = (JComponent)e.getComponent(); // shall be EditorGutterComponent, cast is safe.
183 final JLayeredPane layeredPane = comp.getRootPane().getLayeredPane();
184 final Point point = SwingUtilities.convertPoint(comp, ((EditorEx)editor).getGutterComponentEx().getWidth(), e.getY(), layeredPane);
185 showActiveHint(range, editor, point, tracker);
188 public boolean canDoAction(final MouseEvent e) {
189 final EditorGutterComponentEx gutter = (EditorGutterComponentEx)e.getComponent();
190 return e.getX() > gutter.getLineMarkerAreaOffset() + gutter.getIconsAreaWidth();
195 public static void showActiveHint(final Range range, final Editor editor, final Point point, final LineStatusTracker tracker) {
196 final DefaultActionGroup group = new DefaultActionGroup();
198 final ShowPrevChangeMarkerAction localShowPrevAction = new ShowPrevChangeMarkerAction(tracker.getPrevRange(range), tracker, editor);
199 final ShowNextChangeMarkerAction localShowNextAction = new ShowNextChangeMarkerAction(tracker.getNextRange(range), tracker, editor);
200 final RollbackLineStatusRangeAction rollback = new RollbackLineStatusRangeAction(tracker, range, editor);
201 final ShowLineStatusRangeDiffAction showDiff = new ShowLineStatusRangeDiffAction(tracker, range, editor);
202 final CopyLineStatusRangeAction copyRange = new CopyLineStatusRangeAction(tracker, range);
204 group.add(localShowPrevAction);
205 group.add(localShowNextAction);
208 group.add(copyRange);
211 final JComponent editorComponent = editor.getComponent();
212 EmptyAction.setupAction(localShowPrevAction, "VcsShowPrevChangeMarker", editorComponent);
213 EmptyAction.setupAction(localShowNextAction, "VcsShowNextChangeMarker", editorComponent);
214 EmptyAction.setupAction(rollback, IdeActions.SELECTED_CHANGES_ROLLBACK, editorComponent);
215 EmptyAction.setupAction(showDiff, "ChangesView.Diff", editorComponent);
216 EmptyAction.setupAction(copyRange, IdeActions.ACTION_COPY, editorComponent);
219 final JComponent toolbar =
220 ActionManager.getInstance().createActionToolbar(ActionPlaces.FILEHISTORY_VIEW_TOOLBAR, group, true).getComponent();
222 final Color background = ((EditorEx)editor).getBackgroundColor();
223 final Color foreground = editor.getColorsScheme().getColor(EditorColors.CARET_COLOR);
224 toolbar.setBackground(background);
227 .setBorder(new ColoredSideBorder(foreground, foreground, range.getType() != Range.INSERTED ? null : foreground, foreground, 1));
229 final JPanel component = new JPanel(new BorderLayout());
230 component.setOpaque(false);
232 final JPanel toolbarPanel = new JPanel(new BorderLayout());
233 toolbarPanel.setOpaque(false);
234 toolbarPanel.add(toolbar, BorderLayout.WEST);
235 JPanel emptyPanel = new JPanel();
236 emptyPanel.setOpaque(false);
237 toolbarPanel.add(emptyPanel, BorderLayout.CENTER);
238 MouseAdapter listener = new MouseAdapter() {
240 public void mousePressed(MouseEvent e) {
241 editor.getContentComponent().dispatchEvent(SwingUtilities.convertMouseEvent(e.getComponent(), e, editor.getContentComponent()));
245 public void mouseClicked(MouseEvent e) {
246 editor.getContentComponent().dispatchEvent(SwingUtilities.convertMouseEvent(e.getComponent(), e, editor.getContentComponent()));
249 public void mouseReleased(final MouseEvent e) {
250 editor.getContentComponent().dispatchEvent(SwingUtilities.convertMouseEvent(e.getComponent(), e, editor.getContentComponent()));
253 emptyPanel.addMouseListener(listener);
255 component.add(toolbarPanel, BorderLayout.NORTH);
258 if (range.getType() != Range.INSERTED) {
259 final DocumentEx doc = (DocumentEx)tracker.getVcsDocument();
260 final EditorEx uEditor = (EditorEx)EditorFactory.getInstance().createViewer(doc, tracker.getProject());
261 final EditorHighlighter highlighter =
262 EditorHighlighterFactory.getInstance().createEditorHighlighter(tracker.getProject(), getFileName(tracker.getDocument()));
263 uEditor.setHighlighter(highlighter);
265 final EditorFragmentComponent editorFragmentComponent =
266 EditorFragmentComponent.createEditorFragmentComponent(uEditor, range.getVcsLine1(), range.getVcsLine2(), false, false);
268 component.add(editorFragmentComponent, BorderLayout.CENTER);
270 EditorFactory.getInstance().releaseEditor(uEditor);
274 final List<AnAction> actionList = ActionUtil.getActions(editorComponent);
275 final LightweightHint lightweightHint = new LightweightHint(component);
276 HintListener closeListener = new HintListener() {
277 public void hintHidden(final EventObject event) {
278 actionList.remove(rollback);
279 actionList.remove(showDiff);
280 actionList.remove(copyRange);
281 actionList.remove(localShowPrevAction);
282 actionList.remove(localShowNextAction);
285 lightweightHint.addHintListener(closeListener);
287 HintManagerImpl.getInstanceImpl().showEditorHint(lightweightHint, editor, point,
288 HintManagerImpl.HIDE_BY_ANY_KEY | HintManagerImpl.HIDE_BY_TEXT_CHANGE |
289 HintManagerImpl.HIDE_BY_SCROLLING,
290 -1, false, new HintHint(editor, point));
292 if (!lightweightHint.isVisible()) {
293 closeListener.hintHidden(null);
297 private static String getFileName(final Document document) {
298 final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
299 if (file == null) return "";
300 return file.getName();
303 public static void moveToRange(final Range range, final Editor editor, final LineStatusTracker tracker) {
304 final Document document = tracker.getDocument();
305 final int lastOffset = document.getLineStartOffset(Math.min(range.getLine2(), document.getLineCount() - 1));
306 editor.getCaretModel().moveToOffset(lastOffset);
307 editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
309 showHint(range, editor, tracker);
312 public static void showHint(final Range range, final Editor editor, final LineStatusTracker tracker) {
313 editor.getScrollingModel().runActionOnScrollingFinished(new Runnable() {
315 Point p = editor.visualPositionToXY(editor.getCaretModel().getVisualPosition());
316 final JComponent editorComponent = editor.getContentComponent();
317 final JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane();
318 p = SwingUtilities.convertPoint(editorComponent, 0, p.y, layeredPane);
319 showActiveHint(range, editor, p, tracker);
325 private static Color getDiffColor(@NotNull Range.InnerRange range) {
326 // TODO: we should move color settings from Colors-General to Colors-Diff
327 final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
328 switch (range.getType()) {
330 return globalScheme.getColor(EditorColors.ADDED_LINES_COLOR);
332 return globalScheme.getColor(EditorColors.DELETED_LINES_COLOR);
334 return globalScheme.getColor(EditorColors.MODIFIED_LINES_COLOR);
336 return globalScheme.getColor(EditorColors.WHITESPACES_MODIFIED_LINES_COLOR);
344 private static Color getDiffColor(@NotNull Range range) {
345 final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
346 switch (range.getType()) {
348 return globalScheme.getAttributes(DiffColors.DIFF_INSERTED).getErrorStripeColor();
350 return globalScheme.getAttributes(DiffColors.DIFF_DELETED).getErrorStripeColor();
352 return globalScheme.getAttributes(DiffColors.DIFF_MODIFIED).getErrorStripeColor();
360 private static Color getDiffGutterColor(@NotNull Range range) {
361 final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
362 switch (range.getType()) {
364 return globalScheme.getColor(EditorColors.ADDED_LINES_COLOR);
366 return globalScheme.getColor(EditorColors.DELETED_LINES_COLOR);
368 return globalScheme.getColor(EditorColors.MODIFIED_LINES_COLOR);
376 private static Color getDiffGutterBorderColor() {
377 final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
378 return globalScheme.getColor(EditorColors.BORDER_LINES_COLOR);