ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / ex / LineStatusTracker.java
blob44c0946f3b13dd74af9b094f90a9a6db4555b569
1 /*
2 * Copyright 2000-2009 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.application.ApplicationManager;
22 import com.intellij.openapi.command.CommandProcessor;
23 import com.intellij.openapi.command.undo.UndoManager;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.diff.*;
26 import com.intellij.openapi.editor.Document;
27 import com.intellij.openapi.editor.Editor;
28 import com.intellij.openapi.editor.EditorFactory;
29 import com.intellij.openapi.editor.ScrollType;
30 import com.intellij.openapi.editor.colors.*;
31 import com.intellij.openapi.editor.event.DocumentAdapter;
32 import com.intellij.openapi.editor.event.DocumentEvent;
33 import com.intellij.openapi.editor.ex.DocumentEx;
34 import com.intellij.openapi.editor.ex.EditorEx;
35 import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
36 import com.intellij.openapi.editor.highlighter.EditorHighlighter;
37 import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
38 import com.intellij.openapi.editor.markup.*;
39 import com.intellij.openapi.fileEditor.FileDocumentManager;
40 import com.intellij.openapi.ide.CopyPasteManager;
41 import com.intellij.openapi.project.DumbAware;
42 import com.intellij.openapi.project.Project;
43 import com.intellij.openapi.util.IconLoader;
44 import com.intellij.openapi.util.TextRange;
45 import com.intellij.openapi.util.text.StringUtil;
46 import com.intellij.openapi.vcs.VcsBundle;
47 import com.intellij.openapi.vcs.actions.ShowNextChangeMarkerAction;
48 import com.intellij.openapi.vcs.actions.ShowPrevChangeMarkerAction;
49 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
50 import com.intellij.openapi.vfs.VirtualFile;
51 import com.intellij.ui.HintListener;
52 import com.intellij.ui.LightweightHint;
53 import com.intellij.ui.SideBorder2;
54 import com.intellij.util.ui.UIUtil;
55 import org.jetbrains.annotations.NotNull;
56 import org.jetbrains.annotations.Nullable;
58 import javax.swing.*;
59 import java.awt.*;
60 import java.awt.datatransfer.StringSelection;
61 import java.awt.event.MouseEvent;
62 import java.util.*;
63 import java.util.List;
65 /**
66 * author: lesya
68 public class LineStatusTracker {
69 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.ex.LineStatusTracker");
70 private final Document myDocument;
71 private final Document myUpToDateDocument;
72 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) private List<Range> myRanges = new ArrayList<Range>();
73 private final Project myProject;
74 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) private int myHighlighterCount = 0;
76 private MyDocumentListener myDocumentListener;
78 private boolean myIsReleased = false;
79 private boolean myIsInitialized = false;
80 private boolean myBulkUpdate;
82 public LineStatusTracker(Document document, Document upToDateDocument, Project project) {
83 myDocument = document;
84 myUpToDateDocument = upToDateDocument;
85 myUpToDateDocument.putUserData(UndoManager.DONT_RECORD_UNDO, Boolean.TRUE);
86 myProject = project;
89 public synchronized void initialize(@NotNull final String upToDateContent) {
90 if (myIsReleased) return;
91 LOG.assertTrue(!myIsInitialized);
92 try {
93 ApplicationManager.getApplication().runWriteAction(new Runnable() {
94 public void run() {
95 myUpToDateDocument.replaceString(0, myUpToDateDocument.getTextLength(), StringUtil.convertLineSeparators(upToDateContent));
97 });
99 myUpToDateDocument.setReadOnly(true);
100 reinstallRanges();
102 myDocumentListener = new MyDocumentListener();
103 myDocument.addDocumentListener(myDocumentListener);
105 finally {
106 myIsInitialized = true;
110 private synchronized void reinstallRanges() {
111 reinstallRanges(new RangesBuilder(myDocument, myUpToDateDocument).getRanges());
114 private void reinstallRanges(List<Range> ranges) {
115 removeHighlighters();
116 myRanges = ranges;
117 addHighlighters();
120 private void addHighlighters() {
121 for (Range range : myRanges) {
122 if (!range.hasHighlighter()) range.setHighlighter(createHighlighter(range));
126 @SuppressWarnings({"AutoBoxing"})
127 synchronized private RangeHighlighter createHighlighter(Range range) {
128 int first =
129 range.getOffset1() >= myDocument.getLineCount() ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset1());
131 int second =
132 range.getOffset2() >= myDocument.getLineCount() ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset2());
135 RangeHighlighter highlighter = myDocument.getMarkupModel(myProject)
136 .addRangeHighlighter(first, second, HighlighterLayer.FIRST - 1, null, HighlighterTargetArea.LINES_IN_RANGE);
137 myHighlighterCount++;
138 TextAttributes attr = getAttributesFor(range);
139 highlighter.setErrorStripeMarkColor(attr.getErrorStripeColor());
140 highlighter.setThinErrorStripeMark(true);
141 highlighter.setGreedyToLeft(true);
142 highlighter.setGreedyToRight(true);
143 highlighter.setLineMarkerRenderer(createRenderer(range));
144 highlighter.setEditorFilter(MarkupEditorFilterFactory.createIsNotDiffFilter());
145 final int line1 = myDocument.getLineNumber(first);
146 final int line2 = myDocument.getLineNumber(second);
147 final String tooltip;
148 if (line1 == line2) {
149 tooltip = VcsBundle.message("tooltip.text.line.changed", line1);
151 else {
152 tooltip = VcsBundle.message("tooltip.text.lines.changed", line1, line2);
155 highlighter.setErrorStripeTooltip(tooltip);
156 return highlighter;
160 private void removeHighlighters() {
161 for (Range oldRange : myRanges) {
162 removeHighlighter(oldRange.getHighlighter());
163 oldRange.setHighlighter(null);
167 synchronized void removeHighlighter(RangeHighlighter highlighter) {
168 if (highlighter == null) return;
169 MarkupModel markupModel = myDocument.getMarkupModel(myProject);
170 //noinspection ConstantConditions
171 if (markupModel == null) return;
172 markupModel.removeHighlighter(highlighter);
173 myHighlighterCount--;
176 private static TextAttributesKey getDiffColor(Range range) {
177 switch (range.getType()) {
178 case Range.INSERTED:
179 return DiffColors.DIFF_INSERTED;
180 case Range.DELETED:
181 return DiffColors.DIFF_DELETED;
182 case Range.MODIFIED:
183 return DiffColors.DIFF_MODIFIED;
184 default:
185 assert false;
186 return null;
190 private static ColorKey getEditorColorNameFor(Range range) {
191 switch (range.getType()) {
192 case Range.MODIFIED:
193 return EditorColors.MODIFIED_LINES_COLOR;
194 default:
195 return EditorColors.ADDED_LINES_COLOR;
199 private static TextAttributes getAttributesFor(Range range) {
200 EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
201 Color color = globalScheme.getColor(getEditorColorNameFor(range));
202 TextAttributes textAttributes = new TextAttributes(null, color, null, EffectType.BOXED, Font.PLAIN);
203 textAttributes.setErrorStripeColor(color);
204 return textAttributes;
207 private static void paintGutterFragment(Editor editor, Graphics g, Rectangle r, TextAttributesKey diffAttributeKey) {
208 EditorGutterComponentEx gutter = ((EditorEx)editor).getGutterComponentEx();
209 g.setColor(editor.getColorsScheme().getAttributes(diffAttributeKey).getBackgroundColor());
210 int endX = gutter.getWhitespaceSeparatorOffset();
211 int x = r.x + r.width - 2;
212 int width = endX - x;
213 if (r.height > 0) {
214 g.fillRect(x, r.y + 2, width, r.height - 4);
215 g.setColor(gutter.getFoldingColor(false));
216 UIUtil.drawLine(g, x, r.y + 2, x + width, r.y + 2);
217 UIUtil.drawLine(g, x, r.y + 2, x, r.y + r.height - 3);
218 UIUtil.drawLine(g, x, r.y + r.height - 3, x + width, r.y + r.height - 3);
220 else {
221 int[] xPoints = new int[]{x,
223 x + width - 1};
224 int[] yPoints = new int[]{r.y - 4,
225 r.y + 4,
226 r.y};
227 g.fillPolygon(xPoints, yPoints, 3);
229 g.setColor(gutter.getFoldingColor(false));
230 g.drawPolygon(xPoints, yPoints, 3);
235 private LineMarkerRenderer createRenderer(final Range range) {
236 return new ActiveGutterRenderer() {
237 public void paint(Editor editor, Graphics g, Rectangle r) {
238 paintGutterFragment(editor, g, r, getDiffColor(range));
241 public void doAction(Editor editor, MouseEvent e) {
242 e.consume();
243 JComponent comp = (JComponent)e.getComponent(); // shall be EditorGutterComponent, cast is safe.
244 JLayeredPane layeredPane = comp.getRootPane().getLayeredPane();
245 Point point = SwingUtilities.convertPoint(comp, ((EditorEx)editor).getGutterComponentEx().getWidth(), e.getY(), layeredPane);
246 showActiveHint(range, editor, point);
249 public boolean canDoAction(final MouseEvent e) {
250 final EditorGutterComponentEx gutter = (EditorGutterComponentEx)e.getComponent();
251 return e.getX() > gutter.getLineMarkerAreaOffset() + gutter.getIconsAreaWidth();
256 public synchronized void release() {
257 try {
258 if (!myIsInitialized) return;
259 LOG.assertTrue(!myIsReleased);
261 removeHighlighters();
262 if (myDocumentListener != null) {
263 myDocument.removeDocumentListener(myDocumentListener);
264 myDocumentListener = null;
267 finally {
268 myIsReleased = true;
272 public Document getDocument() {
273 return myDocument;
276 public VirtualFile getVirtualFile() {
277 return FileDocumentManager.getInstance().getFile(getDocument());
280 public List<Range> getRanges() {
281 return myRanges;
284 public Document getUpToDateDocument() {
285 return myUpToDateDocument;
288 public void startBulkUpdate() {
289 myBulkUpdate = true;
290 for (Range oldRange : myRanges) {
291 removeHighlighter(oldRange.getHighlighter());
292 oldRange.setHighlighter(null);
294 myRanges.clear();
297 public void finishBulkUpdate() {
298 if (myBulkUpdate) {
299 myBulkUpdate = false;
300 reinstallRanges();
304 private class MyDocumentListener extends DocumentAdapter {
305 private int myFirstChangedLine;
306 private int myUpToDateFirstLine;
307 private int myUpToDateLastLine;
308 private int myLastChangedLine;
309 private int myLinesBeforeChange;
311 public void beforeDocumentChange(DocumentEvent e) {
312 if (myBulkUpdate) return;
313 myFirstChangedLine = myDocument.getLineNumber(e.getOffset());
314 myLastChangedLine = myDocument.getLineNumber(e.getOffset() + e.getOldLength());
315 if (StringUtil.endsWithChar(e.getOldFragment(), '\n')) myLastChangedLine++;
317 myLinesBeforeChange = myDocument.getLineNumber(e.getOffset() + e.getOldLength()) - myDocument.getLineNumber(e.getOffset());
319 Range firstChangedRange = getLastRangeBeforeLine(myFirstChangedLine);
321 if (firstChangedRange == null) {
322 myUpToDateFirstLine = myFirstChangedLine;
324 else if (firstChangedRange.containsLine(myFirstChangedLine)) {
325 myFirstChangedLine = firstChangedRange.getOffset1();
326 myUpToDateFirstLine = firstChangedRange.getUOffset1();
328 else {
329 myUpToDateFirstLine = firstChangedRange.getUOffset2() + (myFirstChangedLine - firstChangedRange.getOffset2());
332 Range myLastChangedRange = getLastRangeBeforeLine(myLastChangedLine);
334 if (myLastChangedRange == null) {
335 myUpToDateLastLine = myLastChangedLine;
337 else if (myLastChangedRange.containsLine(myLastChangedLine)) {
338 myUpToDateLastLine = myLastChangedRange.getUOffset2();
339 myLastChangedLine = myLastChangedRange.getOffset2();
341 else {
342 myUpToDateLastLine = myLastChangedRange.getUOffset2() + (myLastChangedLine - myLastChangedRange.getOffset2());
347 @Nullable
348 private Range getLastRangeBeforeLine(int line) {
349 Range result = null;
350 for (Range range : myRanges) {
351 if (range.isMoreThen(line)) return result;
352 result = range;
354 return result;
357 public void documentChanged(DocumentEvent e) {
358 if (myBulkUpdate) return;
360 int line = myDocument.getLineNumber(e.getOffset() + e.getNewLength());
361 int linesAfterChange = line - myDocument.getLineNumber(e.getOffset());
362 int linesShift = linesAfterChange - myLinesBeforeChange;
364 List<Range> rangesAfterChange = getRangesAfter(myLastChangedLine);
365 List<Range> rangesBeforeChange = getRangesBefore(myFirstChangedLine);
367 List<Range> changedRanges = getChangedRanges(myFirstChangedLine, myLastChangedLine);
369 int newSize = rangesBeforeChange.size() + changedRanges.size() + rangesAfterChange.size();
370 if (myRanges.size() != newSize) {
371 LOG.info("Ranges: " + myRanges + "; first changed line: " + myFirstChangedLine + "; last changed line: " + myLastChangedLine);
372 LOG.assertTrue(false);
376 myLastChangedLine += linesShift;
379 List<Range> newChangedRanges = getNewChangedRanges();
381 shiftRanges(rangesAfterChange, linesShift);
383 if (!changedRanges.equals(newChangedRanges)) {
384 replaceRanges(changedRanges, newChangedRanges);
386 myRanges = new ArrayList<Range>();
388 myRanges.addAll(rangesBeforeChange);
389 myRanges.addAll(newChangedRanges);
390 myRanges.addAll(rangesAfterChange);
392 if (myHighlighterCount != myRanges.size()) {
393 LOG.assertTrue(false, "Highlighters: " + myHighlighterCount + ", ranges: " + myRanges.size());
396 myRanges = mergeRanges(myRanges);
398 for (Range range : myRanges) {
399 if (!range.hasHighlighter()) range.setHighlighter(createHighlighter(range));
403 if (myHighlighterCount != myRanges.size()) {
404 LOG.assertTrue(false, "Highlighters: " + myHighlighterCount + ", ranges: " + myRanges.size());
410 private List<Range> getNewChangedRanges() {
411 List<String> lines = new DocumentWrapper(myDocument).getLines(myFirstChangedLine, myLastChangedLine);
412 List<String> uLines = new DocumentWrapper(myUpToDateDocument)
413 .getLines(myUpToDateFirstLine, myUpToDateLastLine);
414 return new RangesBuilder(lines, uLines, myFirstChangedLine, myUpToDateFirstLine).getRanges();
417 private List<Range> mergeRanges(List<Range> ranges) {
418 ArrayList<Range> result = new ArrayList<Range>();
419 Iterator<Range> iterator = ranges.iterator();
420 if (!iterator.hasNext()) return result;
421 Range prev = iterator.next();
422 while (iterator.hasNext()) {
423 Range range = iterator.next();
424 if (prev.canBeMergedWith(range)) {
425 prev = prev.mergeWith(range, LineStatusTracker.this);
427 else {
428 result.add(prev);
429 prev = range;
432 result.add(prev);
433 return result;
436 private void replaceRanges(List<Range> rangesInChange, List<Range> newRangesInChange) {
437 for (Range range : rangesInChange) {
438 removeHighlighter(range.getHighlighter());
439 range.setHighlighter(null);
441 for (Range range : newRangesInChange) {
442 range.setHighlighter(createHighlighter(range));
446 private void shiftRanges(List<Range> rangesAfterChange, int shift) {
447 for (final Range aRangesAfterChange : rangesAfterChange) {
448 aRangesAfterChange.shift(shift);
454 private List<Range> getChangedRanges(int from, int to) {
455 return getChangedRanges(myRanges, from, to);
458 public static List<Range> getChangedRanges(List<Range> ranges, int from, int to) {
459 ArrayList<Range> result = new ArrayList<Range>();
460 for (Range range : ranges) {
461 if (range.getOffset1() <= to && range.getOffset2() >= from) result.add(range);
462 // if (range.getOffset1() > to) break;
464 return result;
467 private List<Range> getRangesBefore(int line) {
468 return getRangesBefore(myRanges, line);
472 public static List<Range> getRangesBefore(List<Range> ranges, int line) {
473 ArrayList<Range> result = new ArrayList<Range>();
475 for (Range range : ranges) {
476 if (range.getOffset2() < line) result.add(range);
477 //if (range.getOffset2() > line) break;
479 return result;
482 private List<Range> getRangesAfter(int line) {
483 return getRangesAfter(myRanges, line);
486 public static List<Range> getRangesAfter(List<Range> ranges, int line) {
487 ArrayList<Range> result = new ArrayList<Range>();
488 for (Range range : ranges) {
489 if (range.getOffset1() > line) result.add(range);
491 return result;
494 public void moveToRange(final Range range, final Editor editor) {
495 final int firstOffset = myDocument.getLineStartOffset(Math.min(range.getOffset1(), myDocument.getLineCount() - 1));
496 editor.getCaretModel().moveToOffset(firstOffset);
497 editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
499 editor.getScrollingModel().runActionOnScrollingFinished(new Runnable() {
500 public void run() {
501 Point p = editor.visualPositionToXY(editor.offsetToVisualPosition(firstOffset));
502 JComponent editorComponent = editor.getContentComponent();
503 JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane();
504 p = SwingUtilities.convertPoint(editorComponent, 0, p.y, layeredPane);
505 showActiveHint(range, editor, p);
510 @Nullable
511 private Range getNextRange(Range range) {
512 int index = myRanges.indexOf(range);
513 if (index == myRanges.size() - 1) return null;
514 return myRanges.get(index + 1);
517 @Nullable
518 private Range getPrevRange(Range range) {
519 int index = myRanges.indexOf(range);
520 if (index <= 0) return null;
521 return myRanges.get(index - 1);
524 @Nullable
525 public Range getNextRange(int line) {
526 final Range currentRange = getRangeForLine(line);
527 if (currentRange != null) {
528 return getNextRange(currentRange);
531 for (Range range : myRanges) {
532 if (line > range.getOffset1() || line > range.getOffset2()) {
533 continue;
535 return range;
537 return null;
540 @Nullable
541 public Range getPrevRange(int line) {
542 final Range currentRange = getRangeForLine(line);
543 if (currentRange != null) {
544 return getPrevRange(currentRange);
547 for (ListIterator<Range> iterator = myRanges.listIterator(myRanges.size()); iterator.hasPrevious();) {
548 Range range = iterator.previous();
549 if (range.getOffset1() > line) {
550 continue;
552 return range;
554 return null;
557 @Nullable
558 public Range getRangeForLine(final int line) {
559 for (final Range range : myRanges) {
560 if (range.getType() == Range.DELETED && line == range.getOffset1()) {
561 return range;
563 else if (line >= range.getOffset1() && line < range.getOffset2()) {
564 return range;
567 return null;
570 public static abstract class MyAction extends AnAction implements DumbAware {
571 protected final LineStatusTracker myLineStatusTracker;
572 protected final Range myRange;
574 protected MyAction(String text, Icon icon, LineStatusTracker lineStatusTracker, Range range) {
575 super(text, null, icon);
576 myLineStatusTracker = lineStatusTracker;
577 myRange = range;
580 public void update(AnActionEvent e) {
581 e.getPresentation().setEnabled(isEnabled());
584 public abstract boolean isEnabled();
586 protected int getMyRangeIndex() {
587 List<Range> ranges = myLineStatusTracker.getRanges();
588 for (int i = 0; i < ranges.size(); i++) {
589 Range range = ranges.get(i);
590 if (range.getOffset1() == myRange.getOffset1() && range.getOffset2() == myRange.getOffset2()) {
591 return i;
594 return -1;
598 public static class RollbackAction extends LineStatusTracker.MyAction {
599 public RollbackAction(LineStatusTracker lineStatusTracker, Range range, Editor editor) {
600 super(VcsBundle.message("action.name.rollback"), IconLoader.getIcon("/actions/reset.png"), lineStatusTracker, range);
603 public boolean isEnabled() {
604 return true;
607 public void actionPerformed(AnActionEvent e) {
608 CommandProcessor.getInstance().executeCommand(myLineStatusTracker.getProject(), new Runnable() {
609 public void run() {
610 ApplicationManager.getApplication().runWriteAction(new Runnable() {
611 public void run() {
612 if (!myLineStatusTracker.getDocument().isWritable()) {
613 final ReadonlyStatusHandler.OperationStatus operationStatus = ReadonlyStatusHandler
614 .getInstance(myLineStatusTracker.getProject()).ensureFilesWritable(myLineStatusTracker.getVirtualFile());
615 if (operationStatus.hasReadonlyFiles()) return;
617 myLineStatusTracker.rollbackChanges(myRange);
621 }, VcsBundle.message("command.name.rollback.change"), null);
626 public void rollbackChanges(Range range) {
627 TextRange currentTextRange = getCurrentTextRange(range);
629 if (range.getType() == Range.INSERTED) {
630 myDocument
631 .replaceString(currentTextRange.getStartOffset(), Math.min(currentTextRange.getEndOffset() + 1, myDocument.getTextLength()), "");
633 else if (range.getType() == Range.DELETED) {
634 String upToDateContent = getUpToDateContent(range);
635 myDocument.insertString(currentTextRange.getStartOffset(), upToDateContent);
637 else {
639 String upToDateContent = getUpToDateContent(range);
640 myDocument.replaceString(currentTextRange.getStartOffset(), Math.min(currentTextRange.getEndOffset() + 1, myDocument.getTextLength()),
641 upToDateContent);
645 public String getUpToDateContent(Range range) {
646 TextRange textRange = getUpToDateRange(range);
647 final int startOffset = textRange.getStartOffset();
648 final int endOffset = Math.min(textRange.getEndOffset() + 1, myUpToDateDocument.getTextLength());
649 return myUpToDateDocument.getCharsSequence().subSequence(startOffset, endOffset).toString();
652 private Project getProject() {
653 return myProject;
656 public class ShowDiffAction extends LineStatusTracker.MyAction {
657 public ShowDiffAction(LineStatusTracker lineStatusTracker, Range range, Editor editor) {
658 super(VcsBundle.message("action.name.show.difference"), IconLoader.getIcon("/actions/diff.png"), lineStatusTracker, range);
661 public boolean isEnabled() {
662 return isModifiedRange() || isDeletedRange();
665 private boolean isDeletedRange() {
666 return myRange.getType() == Range.DELETED;
669 private boolean isModifiedRange() {
670 return myRange.getType() == Range.MODIFIED;
673 public void actionPerformed(AnActionEvent e) {
674 DiffManager.getInstance().getDiffTool().show(createDiffData());
677 private DiffRequest createDiffData() {
678 return new DiffRequest(myLineStatusTracker.getProject()) {
679 public DiffContent[] getContents() {
680 return new DiffContent[]{createDiffContent(myLineStatusTracker.getUpToDateDocument(),
681 myLineStatusTracker.getUpToDateRange(myRange), null),
682 createDiffContent(myLineStatusTracker.getDocument(), myLineStatusTracker.getCurrentTextRange(myRange),
683 myLineStatusTracker.getVirtualFile())};
686 public String[] getContentTitles() {
687 return new String[]{VcsBundle.message("diff.content.title.up.to.date"),
688 VcsBundle.message("diff.content.title.current.range")};
691 public String getWindowTitle() {
692 return VcsBundle.message("dialog.title.diff.for.range");
697 private DiffContent createDiffContent(final Document uDocument, TextRange textRange, VirtualFile file) {
698 DiffContent diffContent = new DocumentContent(myProject, uDocument);
699 return new FragmentContent(diffContent, textRange, myLineStatusTracker.getProject(), file);
703 public class CopyAction extends MyAction {
704 protected CopyAction(LineStatusTracker lineStatusTracker, Range range) {
705 super(VcsBundle.message("action.name.copy.old.text"), IconLoader.getIcon("/actions/copy.png"), lineStatusTracker, range);
708 public boolean isEnabled() {
709 return myRange.getType() == Range.DELETED || myRange.getType() == Range.MODIFIED;
712 public void actionPerformed(final AnActionEvent e) {
713 final String content = myLineStatusTracker.getUpToDateContent(myRange);
714 CopyPasteManager.getInstance().setContents(new StringSelection(content));
718 private TextRange getCurrentTextRange(Range range) {
719 return getRange(range.getType(), range.getOffset1(), range.getOffset2(), Range.DELETED, myDocument);
722 private TextRange getUpToDateRange(Range range) {
723 return getRange(range.getType(), range.getUOffset1(), range.getUOffset2(), Range.INSERTED, myUpToDateDocument);
726 private static TextRange getRange(byte rangeType, int offset1, int offset2, byte emptyRangeCondition, Document document) {
727 if (rangeType == emptyRangeCondition) {
728 int lineStartOffset;
729 if (offset1 == 0) {
730 lineStartOffset = 0;
732 else {
733 lineStartOffset = document.getLineEndOffset(offset1 - 1);
735 //if (lineStartOffset > 0) lineStartOffset--;
736 return new TextRange(lineStartOffset, lineStartOffset);
739 else {
740 int startOffset = document.getLineStartOffset(offset1);
741 int endOffset = document.getLineEndOffset(offset2 - 1);
742 if (startOffset > 0) {
743 startOffset--;
744 endOffset--;
746 return new TextRange(startOffset, endOffset);
751 public void showActiveHint(Range range, final Editor editor, Point point) {
753 DefaultActionGroup group = new DefaultActionGroup();
755 final AnAction globalShowNextAction = ActionManager.getInstance().getAction("VcsShowNextChangeMarker");
756 final AnAction globalShowPrevAction = ActionManager.getInstance().getAction("VcsShowPrevChangeMarker");
758 final ShowPrevChangeMarkerAction localShowPrevAction = new ShowPrevChangeMarkerAction(getPrevRange(range), this, editor);
759 final ShowNextChangeMarkerAction localShowNextAction = new ShowNextChangeMarkerAction(getNextRange(range), this, editor);
761 JComponent editorComponent = editor.getComponent();
763 localShowNextAction.registerCustomShortcutSet(localShowNextAction.getShortcutSet(), editorComponent);
764 localShowPrevAction.registerCustomShortcutSet(localShowPrevAction.getShortcutSet(), editorComponent);
766 group.add(localShowPrevAction);
767 group.add(localShowNextAction);
769 localShowNextAction.copyFrom(globalShowNextAction);
770 localShowPrevAction.copyFrom(globalShowPrevAction);
772 group.add(new RollbackAction(this, range, editor));
773 group.add(new ShowDiffAction(this, range, editor));
774 group.add(new CopyAction(this, range));
776 final List<AnAction> actionList = (List<AnAction>)editorComponent.getClientProperty(AnAction.ourClientProperty);
778 actionList.remove(globalShowPrevAction);
779 actionList.remove(globalShowNextAction);
781 JComponent toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.FILEHISTORY_VIEW_TOOLBAR, group, true).getComponent();
783 final Color background = ((EditorEx)editor).getBackroundColor();
784 final Color foreground = editor.getColorsScheme().getColor(EditorColors.CARET_COLOR);
785 toolbar.setBackground(background);
787 toolbar.setBorder(new SideBorder2(foreground, foreground, range.getType() != Range.INSERTED ? null : foreground, foreground, 1));
789 JPanel component = new JPanel(new BorderLayout());
790 component.setOpaque(false);
792 JPanel toolbarPanel = new JPanel(new BorderLayout());
793 toolbarPanel.setOpaque(false);
794 toolbarPanel.add(toolbar, BorderLayout.WEST);
795 component.add(toolbarPanel, BorderLayout.NORTH);
797 if (range.getType() != Range.INSERTED) {
798 DocumentEx doc = (DocumentEx)myUpToDateDocument;
799 EditorEx uEditor = (EditorEx)EditorFactory.getInstance().createViewer(doc, myProject);
800 EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, getFileName());
801 uEditor.setHighlighter(highlighter);
803 EditorFragmentComponent editorFragmentComponent =
804 EditorFragmentComponent.createEditorFragmentComponent(uEditor, range.getUOffset1(), range.getUOffset2(), false, false);
806 component.add(editorFragmentComponent, BorderLayout.CENTER);
807 EditorFactory.getInstance().releaseEditor(uEditor);
810 LightweightHint lightweightHint = new LightweightHint(component);
811 lightweightHint.addHintListener(new HintListener() {
812 public void hintHidden(EventObject event) {
813 actionList.remove(localShowPrevAction);
814 actionList.remove(localShowNextAction);
815 actionList.add(globalShowPrevAction);
816 actionList.add(globalShowNextAction);
820 HintManagerImpl.getInstanceImpl().showEditorHint(lightweightHint, editor, point, HintManagerImpl.HIDE_BY_ANY_KEY | HintManagerImpl.HIDE_BY_TEXT_CHANGE |
821 HintManagerImpl.HIDE_BY_OTHER_HINT | HintManagerImpl.HIDE_BY_SCROLLING,
822 -1, false);
825 private String getFileName() {
826 VirtualFile file = FileDocumentManager.getInstance().getFile(myDocument);
827 if (file == null) return "";
828 return file.getName();
831 public static LineStatusTracker createOn(Document doc, String upToDateContent, Project project) {
832 Document document = EditorFactory.getInstance().createDocument(StringUtil.convertLineSeparators(upToDateContent));
833 final LineStatusTracker tracker = new LineStatusTracker(doc, document, project);
834 tracker.initialize(upToDateContent);
835 return tracker;
838 public static LineStatusTracker createOn(Document doc, Project project) {
839 Document document = EditorFactory.getInstance().createDocument("");
840 return new LineStatusTracker(doc, document, project);