blob: b787c337d46c467048fcd43aa5033de012052a13 [file] [log] [blame]
/*
* Copyright 2000-2013 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.editor.impl;
import com.intellij.codeInsight.daemon.GutterMark;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.ex.MarkupModelEx;
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.Consumer;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
/**
* User: cdr
*/
abstract class RangeHighlighterData {
private final MarkupModel myModel;
private TextAttributes myTextAttributes;
private LineMarkerRenderer myLineMarkerRenderer;
private Color myErrorStripeColor;
private Color myLineSeparatorColor;
private SeparatorPlacement mySeparatorPlacement;
private GutterIconRenderer myGutterIconRenderer;
private Object myErrorStripeTooltip;
private MarkupEditorFilter myFilter = MarkupEditorFilter.EMPTY;
private CustomHighlighterRenderer myCustomRenderer;
int myLine; // for PersistentRangeHighlighterImpl only
private LineSeparatorRenderer myLineSeparatorRenderer;
private byte myFlags;
RangeHighlighterData(@NotNull MarkupModel model,
@NotNull HighlighterTargetArea target,
TextAttributes textAttributes) {
myTextAttributes = textAttributes;
setFlag(TARGET_AREA_IS_EXACT_FLAG, target == HighlighterTargetArea.EXACT_RANGE);
myModel = model;
if (textAttributes != null) {
myErrorStripeColor = textAttributes.getErrorStripeColor();
}
}
private static final int AFTER_END_OF_LINE_FLAG = 0;
private static final int ERROR_STRIPE_IS_THIN_FLAG = 1;
private static final int TARGET_AREA_IS_EXACT_FLAG = 2;
private static final int IN_BATCH_CHANGE_FLAG = 3;
private static final int CHANGED_FLAG = 4;
private static final int RENDERERS_CHANGED_FLAG = 5;
@MagicConstant(intValues = {AFTER_END_OF_LINE_FLAG, ERROR_STRIPE_IS_THIN_FLAG, TARGET_AREA_IS_EXACT_FLAG, IN_BATCH_CHANGE_FLAG, CHANGED_FLAG, RENDERERS_CHANGED_FLAG})
@interface FlagConstant {}
private boolean isFlagSet(@FlagConstant int flag) {
int state = myFlags >> flag;
return (state & 1) != 0;
}
private void setFlag(@FlagConstant int flag, boolean value) {
assert flag < 8;
int state = value ? 1 : 0;
myFlags = (byte)(myFlags & ~(1 << flag) | state << flag);
}
@NotNull
public abstract RangeHighlighterEx getRangeHighlighter();
public TextAttributes getTextAttributes() {
return myTextAttributes;
}
public void setTextAttributes(TextAttributes textAttributes) {
TextAttributes old = myTextAttributes;
myTextAttributes = textAttributes;
if (!Comparing.equal(old, textAttributes)) {
fireChanged(false);
}
}
@NotNull
public HighlighterTargetArea getTargetArea() {
return isFlagSet(TARGET_AREA_IS_EXACT_FLAG) ? HighlighterTargetArea.EXACT_RANGE : HighlighterTargetArea.LINES_IN_RANGE;
}
public LineMarkerRenderer getLineMarkerRenderer() {
return myLineMarkerRenderer;
}
public void setLineMarkerRenderer(LineMarkerRenderer renderer) {
LineMarkerRenderer old = myLineMarkerRenderer;
myLineMarkerRenderer = renderer;
if (!Comparing.equal(old, renderer)) {
fireChanged(true);
}
}
public CustomHighlighterRenderer getCustomRenderer() {
return myCustomRenderer;
}
public void setCustomRenderer(CustomHighlighterRenderer renderer) {
CustomHighlighterRenderer old = myCustomRenderer;
myCustomRenderer = renderer;
if (!Comparing.equal(old, renderer)) {
fireChanged(true);
}
}
public GutterIconRenderer getGutterIconRenderer() {
return myGutterIconRenderer;
}
public void setGutterIconRenderer(GutterIconRenderer renderer) {
GutterMark old = myGutterIconRenderer;
myGutterIconRenderer = renderer;
if (!Comparing.equal(old, renderer)) {
fireChanged(true);
}
}
public Color getErrorStripeMarkColor() {
return myErrorStripeColor;
}
public void setErrorStripeMarkColor(Color color) {
Color old = myErrorStripeColor;
myErrorStripeColor = color;
if (!Comparing.equal(old, color)) {
fireChanged(false);
}
}
public Object getErrorStripeTooltip() {
return myErrorStripeTooltip;
}
public void setErrorStripeTooltip(Object tooltipObject) {
ApplicationManager.getApplication().assertIsDispatchThread();
Object old = myErrorStripeTooltip;
myErrorStripeTooltip = tooltipObject;
if (!Comparing.equal(old, tooltipObject)) {
fireChanged(false);
}
}
public boolean isThinErrorStripeMark() {
return isFlagSet(ERROR_STRIPE_IS_THIN_FLAG);
}
public void setThinErrorStripeMark(boolean value) {
ApplicationManager.getApplication().assertIsDispatchThread();
boolean old = isThinErrorStripeMark();
setFlag(ERROR_STRIPE_IS_THIN_FLAG, value);
if (old != value) {
fireChanged(false);
}
}
public Color getLineSeparatorColor() {
return myLineSeparatorColor;
}
public void setLineSeparatorColor(Color color) {
Color old = myLineSeparatorColor;
myLineSeparatorColor = color;
if (!Comparing.equal(old, color)) {
fireChanged(false);
}
}
public SeparatorPlacement getLineSeparatorPlacement() {
return mySeparatorPlacement;
}
public void setLineSeparatorPlacement(@Nullable SeparatorPlacement placement) {
SeparatorPlacement old = mySeparatorPlacement;
mySeparatorPlacement = placement;
if (!Comparing.equal(old, placement)) {
fireChanged(false);
}
}
public void setEditorFilter(@NotNull MarkupEditorFilter filter) {
myFilter = filter;
fireChanged(false);
}
@NotNull
public MarkupEditorFilter getEditorFilter() {
return myFilter;
}
public boolean isAfterEndOfLine() {
return isFlagSet(AFTER_END_OF_LINE_FLAG);
}
public void setAfterEndOfLine(boolean afterEndOfLine) {
boolean old = isAfterEndOfLine();
setFlag(AFTER_END_OF_LINE_FLAG, afterEndOfLine);
if (old != afterEndOfLine) {
fireChanged(false);
}
}
private void fireChanged(boolean renderersChanged) {
if (myModel instanceof MarkupModelEx) {
if (isFlagSet(IN_BATCH_CHANGE_FLAG)) {
setFlag(CHANGED_FLAG, true);
if (renderersChanged) {
setFlag(RENDERERS_CHANGED_FLAG, true);
}
}
else {
((MarkupModelEx)myModel).fireAttributesChanged(getRangeHighlighter(), renderersChanged);
}
}
}
public int getAffectedAreaStartOffset() {
int startOffset = getRangeHighlighter().getStartOffset();
if (getTargetArea() == HighlighterTargetArea.EXACT_RANGE) return startOffset;
Document document = myModel.getDocument();
int textLength = document.getTextLength();
if (startOffset >= textLength) return textLength;
return document.getLineStartOffset(document.getLineNumber(startOffset));
}
public int getAffectedAreaEndOffset() {
int endOffset = getRangeHighlighter().getEndOffset();
if (getTargetArea() == HighlighterTargetArea.EXACT_RANGE) return endOffset;
Document document = myModel.getDocument();
int textLength = document.getTextLength();
if (endOffset >= textLength) return endOffset;
return Math.min(textLength, document.getLineEndOffset(document.getLineNumber(endOffset)) + 1);
}
enum ChangeResult { NOT_CHANGED, MINOR_CHANGE, RENDERERS_CHANGED }
@NotNull
ChangeResult changeAttributesInBatch(@NotNull Consumer<RangeHighlighterEx> change) {
assert !isFlagSet(IN_BATCH_CHANGE_FLAG);
assert !isFlagSet(CHANGED_FLAG);
setFlag(IN_BATCH_CHANGE_FLAG, true);
setFlag(RENDERERS_CHANGED_FLAG, false);
ChangeResult result;
try {
change.consume(getRangeHighlighter());
}
finally {
setFlag(IN_BATCH_CHANGE_FLAG, false);
boolean changed = isFlagSet(CHANGED_FLAG);
boolean renderersChanged = isFlagSet(RENDERERS_CHANGED_FLAG);
result = changed ? renderersChanged ? ChangeResult.RENDERERS_CHANGED : ChangeResult.MINOR_CHANGE : ChangeResult.NOT_CHANGED;
setFlag(CHANGED_FLAG, false);
setFlag(RENDERERS_CHANGED_FLAG, false);
}
return result;
}
public MarkupModel getMarkupModel() {
return myModel;
}
public void setLineSeparatorRenderer(LineSeparatorRenderer renderer) {
LineSeparatorRenderer old = myLineSeparatorRenderer;
myLineSeparatorRenderer = renderer;
if (!Comparing.equal(old, renderer)) {
fireChanged(true);
}
}
public LineSeparatorRenderer getLineSeparatorRenderer() {
return myLineSeparatorRenderer;
}
}