blob: f2fcc6a5e9833a4d86269d3ecdd2125382b3e931 [file] [log] [blame]
/*
* Copyright 2000-2009 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.openapi.diff.impl.highlighting;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diff.actions.MergeActionGroup;
import com.intellij.openapi.diff.actions.MergeOperations;
import com.intellij.openapi.diff.impl.DiffLineMarkerRenderer;
import com.intellij.openapi.diff.impl.DiffUtil;
import com.intellij.openapi.diff.impl.EditorSource;
import com.intellij.openapi.diff.impl.fragments.Fragment;
import com.intellij.openapi.diff.impl.fragments.InlineFragment;
import com.intellij.openapi.diff.impl.fragments.LineFragment;
import com.intellij.openapi.diff.impl.util.GutterActionRenderer;
import com.intellij.openapi.diff.impl.util.TextDiffType;
import com.intellij.openapi.diff.impl.util.TextDiffTypeEnum;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.Consumer;
import com.intellij.util.containers.HashSet;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public abstract class DiffMarkup implements EditorSource, Disposable {
private static final Logger LOG = Logger.getInstance(DiffMarkup.class);
private static final int LAYER = HighlighterLayer.SELECTION - 1;
private final ArrayList<RangeHighlighter> myExtraHighLighters = new ArrayList<RangeHighlighter>();
private final ArrayList<RangeHighlighter> myHighLighters = new ArrayList<RangeHighlighter>();
private final HashSet<RangeHighlighter> myActionHighlighters = new HashSet<RangeHighlighter>();
@Nullable private final Project myProject;
private final List<Disposable> myDisposables = new ArrayList<Disposable>();
private boolean myDisposed = false;
protected DiffMarkup(@Nullable Project project, @NotNull Disposable parentDisposable) {
myProject = project;
Disposer.register(parentDisposable, this);
}
@Nullable
private MarkupModel getMarkupModel() {
Editor editor = getEditor();
return editor == null ? null : editor.getMarkupModel();
}
public void highlightText(@NotNull Fragment fragment, @Nullable GutterIconRenderer gutterIconRenderer) {
MarkupModel markupModel = getMarkupModel();
EditorEx editor = getEditor();
TextDiffTypeEnum diffTypeEnum = fragment.getType();
if (diffTypeEnum == null || markupModel == null || editor == null) {
return;
}
TextDiffType type = fragment instanceof LineFragment
? DiffUtil.makeTextDiffType((LineFragment)fragment)
: TextDiffType.create(diffTypeEnum);
final TextRange range = fragment.getRange(getSide());
final TextAttributes attributes = type.getTextAttributes(editor);
if (attributes == null) {
return;
}
RangeHighlighter rangeMarker;
if (range.getLength() == 0) {
final int offset = range.getStartOffset();
rangeMarker = markupModel.addRangeHighlighter(offset, offset, LAYER,
attributes, HighlighterTargetArea.EXACT_RANGE);
rangeMarker.setCustomRenderer(new CustomHighlighterRenderer() {
@Override
public void paint(@NotNull Editor ed, @NotNull RangeHighlighter highlighter, @NotNull Graphics g) {
g.setColor(attributes.getBackgroundColor());
Point point = ed.logicalPositionToXY(ed.offsetToLogicalPosition(highlighter.getStartOffset()));
int endy = point.y + ed.getLineHeight() - 1;
g.drawLine(point.x, point.y, point.x, endy);
g.drawLine(point.x - 1, point.y, point.x - 1, endy);
}
});
}
else {
rangeMarker = markupModel.addRangeHighlighter(range.getStartOffset(), range.getEndOffset(), LAYER,
attributes, HighlighterTargetArea.EXACT_RANGE);
}
if (gutterIconRenderer != null) {
rangeMarker.setGutterIconRenderer(gutterIconRenderer);
}
setLineMarkerRenderer(rangeMarker, fragment, type);
setErrorStripes(rangeMarker, fragment, attributes.getErrorStripeColor());
saveHighlighter(rangeMarker);
}
private static void setErrorStripes(@NotNull RangeHighlighter rangeMarker, @NotNull Fragment fragment, @Nullable Color stripeBarColor) {
if (DiffUtil.isInlineWrapper(fragment)) {
rangeMarker.setErrorStripeMarkColor(null);
}
else {
if (stripeBarColor != null) {
rangeMarker.setErrorStripeMarkColor(stripeBarColor);
rangeMarker.setThinErrorStripeMark(true);
}
}
}
private static void setLineMarkerRenderer(RangeHighlighter rangeMarker, Fragment fragment, TextDiffType type) {
if (!(fragment instanceof InlineFragment)) {
rangeMarker.setLineMarkerRenderer(DiffLineMarkerRenderer.createInstance(type));
}
}
public void addLineMarker(int line, @Nullable TextDiffType type, SeparatorPlacement separatorPlacement) {
RangeHighlighter marker = createLineMarker(type, line, separatorPlacement);
if (marker != null) {
saveHighlighter(marker);
}
}
void setSeparatorMarker(int line, Consumer<Integer> consumer) {
EditorEx editor = getEditor();
MarkupModel markupModel = getMarkupModel();
if (editor == null || markupModel == null) {
return;
}
RangeHighlighter marker = markupModel.addLineHighlighter(line, LAYER, null);
marker.setLineSeparatorPlacement(SeparatorPlacement.TOP);
final FragmentBoundRenderer renderer = new FragmentBoundRenderer(editor.getLineHeight(), editor, consumer);
marker.setLineSeparatorColor(renderer.getColor());
marker.setLineSeparatorRenderer(renderer);
marker.setLineMarkerRenderer(renderer);
myExtraHighLighters.add(marker);
}
@Nullable
private RangeHighlighter createLineMarker(@Nullable final TextDiffType type, int line, SeparatorPlacement placement) {
MarkupModel markupModel = getMarkupModel();
Document document = getDocument();
if (markupModel == null || document == null || type == null) {
return null;
}
final Color color = getLineSeparatorColorForType(type);
if (color == null) {
return null;
}
RangeHighlighter marker = markupModel.addLineHighlighter(line, LAYER, null);
marker.setLineSeparatorColor(color);
marker.setLineSeparatorPlacement(placement);
marker.setLineSeparatorRenderer(new LineSeparatorRenderer() {
@Override
public void drawLine(Graphics g, int x1, int x2, int y) {
if (type.isInlineWrapper()) {
UIUtil.drawLine((Graphics2D)g, x1, y, x2, y, null, DiffUtil.getFramingColor(color));
}
else {
DiffUtil.drawDoubleShadowedLine((Graphics2D)g, x1, x2, y, color);
}
}
});
return marker;
}
@Nullable
private Color getLineSeparatorColorForType(@NotNull TextDiffType type) {
EditorEx editor = getEditor();
if (editor == null) {
return null;
}
return type.getPolygonColor(editor);
}
private void saveHighlighter(@NotNull RangeHighlighter marker) {
myHighLighters.add(marker);
}
@Nullable
public Document getDocument() {
EditorEx editor = getEditor();
return editor == null ? null : editor.getDocument();
}
public void addAction(@Nullable MergeOperations.Operation operation, int lineStartOffset) {
RangeHighlighter highlighter = createAction(operation, lineStartOffset);
if (highlighter != null) {
myActionHighlighters.add(highlighter);
}
}
@Nullable
private RangeHighlighter createAction(@Nullable MergeOperations.Operation operation, int lineStartOffset) {
MarkupModel markupModel = getMarkupModel();
if (operation == null || markupModel == null) {
return null;
}
RangeHighlighter highlighter = markupModel.addRangeHighlighter(lineStartOffset, lineStartOffset, HighlighterLayer.ADDITIONAL_SYNTAX,
new TextAttributes(null, null, null, null, Font.PLAIN),
HighlighterTargetArea.LINES_IN_RANGE);
final MergeActionGroup.OperationAction action = new MergeActionGroup.OperationAction(operation);
highlighter.setGutterIconRenderer(new GutterActionRenderer(action));
return highlighter;
}
public void resetHighlighters() {
removeHighlighters(myHighLighters);
removeHighlighters(myActionHighlighters);
for (RangeHighlighter highLighter : myExtraHighLighters) {
highLighter.dispose();
}
myExtraHighLighters.clear();
}
private void removeHighlighters(@NotNull Collection<RangeHighlighter> highlighters) {
MarkupModel markupModel = getMarkupModel();
if (markupModel != null) {
for (RangeHighlighter highlighter : highlighters) {
highlighter.dispose();
}
}
highlighters.clear();
}
@Nullable
protected Project getProject() {
return myProject;
}
protected void runRegisteredDisposables() {
resetHighlighters();
for (Disposable runnable : myDisposables) {
Disposer.dispose(runnable);
}
myDisposables.clear();
}
public void addDisposable(@NotNull Disposable disposable) {
Disposer.register(this, disposable);
myDisposables.add(disposable);
}
@Nullable
public String getText() {
Document document = getDocument();
return document == null ? null : document.getText();
}
protected final boolean isDisposed() {
return myDisposed;
}
public final void dispose() {
if (isDisposed()) {
return;
}
onDisposed();
myDisposed = true;
}
protected void onDisposed() {
}
public void removeActions() {
removeHighlighters(myActionHighlighters);
}
}