diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.form b/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.form
index 3ec5a7f5550a..0836f2c101b0 100644
--- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.form
+++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.form
@@ -1,6 +1,6 @@
diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.java
index 19c3aee9c104..bb3fffe711db 100644
--- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.java
+++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorAppearanceConfigurable.java
@@ -48,6 +48,7 @@ public class EditorAppearanceConfigurable extends CompositeConfigurable getRegisteredSoftWraps();
+
+ /**
+ * Asks to paint drawing of target type at the given graphics buffer at the given position.
+ *
+ * @param g target graphics buffer to draw in
+ * @param drawingType target drawing type
+ * @param x target 'x' coordinate to use
+ * @param y target 'y' coordinate to use
+ * @param lineHeight line height used at editor
+ * @return painted drawing width
+ */
+ int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight);
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ArrowPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ArrowPainter.java
new file mode 100644
index 000000000000..a211348e6d3d
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ArrowPainter.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2000-2010 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.openapi.util.Computable;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+
+/**
+ * Encapsulates logic of drawing arrows at graphics buffer (primary usage is to draw tabulation symbols representation arrows).
+ *
+ * @author Denis Zhdanov
+ * @since Jul 2, 2010 11:35:23 AM
+ */
+public class ArrowPainter {
+
+ private final ColorHolder myColorHolder;
+ private final Computable myHeightProvider;
+
+ public ArrowPainter(@NotNull ColorHolder colorHolder, @NotNull Computable heightProvider) {
+ myColorHolder = colorHolder;
+ myHeightProvider = heightProvider;
+ }
+
+ /**
+ * Paints arrow at the given graphics buffer using given coordinate parameters.
+ *
+ * @param g target graphics buffer to use
+ * @param y defines baseline of the row where the arrow should be painted
+ * @param start starting 'x' position to use during drawing
+ * @param stop ending 'x' position to use during drawing
+ */
+ public void paint(Graphics g, int y, int start, int stop) {
+ stop -= g.getFontMetrics().charWidth(' ') / 2;
+ Color oldColor = g.getColor();
+ g.setColor(myColorHolder.getColor());
+ final int height = myHeightProvider.compute();
+ final int halfHeight = height / 2;
+ int mid = y - halfHeight;
+ int top = y - height;
+ UIUtil.drawLine(g, start, mid, stop, mid);
+ UIUtil.drawLine(g, stop, y, stop, top);
+ g.fillPolygon(new int[]{stop - halfHeight, stop - halfHeight, stop}, new int[]{y, y - height, y - halfHeight}, 3);
+ g.setColor(oldColor);
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
index 3310c6c80f17..b62353d3467d 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretModelImpl.java
@@ -308,17 +308,13 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
myOffset = myEditor.logicalPositionToOffset(myLogicalCaret);
LOG.assertTrue(myOffset >= 0 && myOffset <= myEditor.getDocument().getTextLength());
- int caretOffset = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line, 0)));
- int caretLine = doc.getLineNumber(caretOffset);
- myVisualLineStart = doc.getLineStartOffset(caretLine);
- myVisualLineEnd = doc.getLineEndOffset(caretLine) + 1;
+ myVisualLineStart = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line, 0)));
+ myVisualLineEnd = myEditor.logicalPositionToOffset(myEditor.visualToLogicalPosition(new VisualPosition(myVisibleCaret.line + 1, 0)));
myEditor.updateCaretCursor();
requestRepaint(oldInfo);
- if (oldCaretPosition.column + oldCaretPosition.softWrapColumnDiff != myLogicalCaret.column + myLogicalCaret.softWrapColumnDiff
- || oldCaretPosition.line + oldCaretPosition.softWrapLinesBeforeCurrentLogicalLine != myLogicalCaret.line + myLogicalCaret.softWrapLinesBeforeCurrentLogicalLine)
- {
+ if (!oldCaretPosition.toVisualPosition().equals(myLogicalCaret.toVisualPosition())) {
CaretEvent event = new CaretEvent(myEditor, oldCaretPosition, myLogicalCaret);
for (CaretListener listener : myCaretListeners) {
listener.caretPositionChanged(event);
@@ -366,18 +362,6 @@ public class CaretModelImpl implements CaretModel, PrioritizedDocumentListener {
return myOffset;
}
- /**
- * There is a possible case that single logical line is spread to more than one visual lines because of soft wraps. This method
- * allows to receive information about vertical range occupied by the active logical line, i.e. it identifies
- * 'y' coordinate of the first visual line that corresponds to the logical line and total height
- * of all visual lines that correspond to the active logical line.
- *
- * @return object that encapsulates information about visual vertical range occupied by the current logical line on a screen
- */
- public VerticalInfo getVisualCaretInfo() {
- return myCaretInfo;
- }
-
public int getVisualLineStart() {
return myVisualLineStart;
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ColorHolder.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ColorHolder.java
new file mode 100644
index 000000000000..c2bda05bf4c9
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ColorHolder.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2010 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.openapi.editor.Editor;
+import com.intellij.openapi.editor.colors.ColorKey;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+
+import java.awt.*;
+
+/**
+ * Defines common contract for target {@link Color} retrieving.
+ *
+ * @author Denis Zhdanov
+ * @since Jul 2, 2010 11:12:07 AM
+ */
+public abstract class ColorHolder {
+
+ /**
+ * @return target {@link Color} object managed by the current holder
+ */
+ public abstract Color getColor();
+
+ /**
+ * Factory method for creating color holder that always returns given {@link Color} object.
+ *
+ * @param color target color to use
+ * @return color holder that uses given color all the time
+ */
+ public static ColorHolder byColor(Color color) {
+ return new StaticColorHolder(color);
+ }
+
+ /**
+ * Shortcut for calling {@link #byColorsScheme(EditorColorsScheme, ColorKey)} with colors scheme retrieved from the given editor.
+ *
+ * @param editor target colors scheme holder
+ * @param key target color identifier within the colors scheme
+ * @return color holder that delegate target color retrieval to the colors scheme associated
+ * with the given editor using given color key
+ */
+ public static ColorHolder byColorsScheme(Editor editor, ColorKey key) {
+ return new ColorSchemeBasedHolder(editor.getColorsScheme(), key);
+ }
+
+ /**
+ * Factory method for creating color holder that delegate target color retrieval to the given colors scheme using given color key.
+ *
+ * @param scheme target colors scheme to use
+ * @param key target color identifier
+ * @return color holder that delegate target color retrieval to the given colors scheme using given color key
+ */
+ public static ColorHolder byColorsScheme(EditorColorsScheme scheme, ColorKey key) {
+ return new ColorSchemeBasedHolder(scheme, key);
+ }
+
+ private static class StaticColorHolder extends ColorHolder {
+
+ private final Color myColor;
+
+ StaticColorHolder(Color color) {
+ myColor = color;
+ }
+
+ @Override
+ public Color getColor() {
+ return myColor;
+ }
+ }
+
+ private static class ColorSchemeBasedHolder extends ColorHolder {
+
+ private final EditorColorsScheme myScheme;
+ private final ColorKey myKey;
+
+ ColorSchemeBasedHolder(EditorColorsScheme scheme, ColorKey key) {
+ myScheme = scheme;
+ myKey = key;
+ }
+
+ @Override
+ public Color getColor() {
+ return myScheme.getColor(myKey);
+ }
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
index b540e898fe13..578bd2b92b99 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
@@ -258,11 +258,12 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
}
private void paintCaretRowBackground(final Graphics g, final int x, final int width) {
- CaretModelImpl.VerticalInfo caretInfo = myEditor.getCaretModel().getVisualCaretInfo();
+ final VisualPosition visCaret = myEditor.getCaretModel().getVisualPosition();
Color caretRowColor = myEditor.getColorsScheme().getColor(EditorColors.CARET_ROW_COLOR);
if (caretRowColor != null) {
g.setColor(caretRowColor);
- g.fillRect(x, caretInfo.y, width, caretInfo.height);
+ final Point caretPoint = myEditor.visualPositionToXY(visCaret);
+ g.fillRect(x, caretPoint.y, width, myEditor.getLineHeight());
}
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
index 1a58ca8e889b..182cd653dd90 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
@@ -49,6 +49,7 @@ import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.highlighter.HighlighterClient;
import com.intellij.openapi.editor.impl.event.MarkupModelEvent;
import com.intellij.openapi.editor.impl.event.MarkupModelListener;
+import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressManager;
@@ -154,6 +155,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private MyEditable myEditable;
private EditorColorsScheme myScheme;
+ private ArrowPainter myTabPainter;
private final boolean myIsViewer;
private final SelectionModelImpl mySelectionModel;
private final EditorMarkupModelImpl myMarkupModel;
@@ -178,6 +180,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private boolean myUnderRepainting;
private EditorHighlighter myHighlighter;
+ private final TextDrawingCallback myTextDrawingCallback = new MyTextDrawingCallback();
private int myScrollBarOrientation;
private boolean myMousePressedInsideSelection;
@@ -237,11 +240,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
ourCaretBlinkingCommand.start();
}
-
public EditorImpl(Document document, boolean viewer, Project project) {
myProject = project;
myDocument = (DocumentImpl)document;
myScheme = new MyColorSchemeDelegate();
+ initTabPainter();
myIsViewer = viewer;
mySettings = new SettingsImpl(this);
@@ -474,6 +477,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
myEditorComponent.repaint();
+ initTabPainter();
updateCaretCursor();
if (myInitialMouseEvent != null) {
@@ -481,6 +485,18 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
}
+ private void initTabPainter() {
+ myTabPainter = new ArrowPainter(
+ ColorHolder.byColorsScheme(myScheme, EditorColors.WHITESPACES_COLOR),
+ new Computable() {
+ @Override
+ public Integer compute() {
+ return getCharHeight();
+ }
+ }
+ );
+ }
+
public void release() {
if (isReleased) {
LOG.error("Double release. First released at: =====\n" + myReleasedAt+"\n======");
@@ -1382,6 +1398,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return getBackgroundIgnoreForced();
}
+ @Override
+ public TextDrawingCallback getTextDrawingCallback() {
+ return myTextDrawingCallback;
+ }
+
private Color getBackgroundColor(final TextAttributes attributes) {
final Color attrColor = attributes.getBackgroundColor();
return attrColor == myScheme.getDefaultBackground() ? getBackgroundColor() : attrColor;
@@ -1543,6 +1564,14 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
CharSequence text = myDocument.getCharsNoThreadCheck();
int lastLineIndex = Math.max(0, myDocument.getLineCount() - 1);
+ // There is a possible case that we work with visual line that is introduced by soft wrap. That implies that additional
+ // space is reserved before the document text represented at the current line (soft wrap drawing is located there).
+ // Hence, we need to take that into consideration during determining if new soft wrap should be introduced.
+ // Example:
+ // 111 222 333 <- soft wrap is here
+ // soft wrap drawing -> 444 555 <- we need to consider soft wrap drawing on a start of the line during checking if we need to wrap 555.
+ boolean onSoftWrapIntroducedVisualLine = false;
+
outer:
while (!iterationState.atEnd() && !lIterator.atEnd()) {
int hEnd = iterationState.getEndOffset();
@@ -1571,6 +1600,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
position.x = 0;
if (position.y > clip.y + clip.height) break;
position.y += lineHeight;
+ onSoftWrapIntroducedVisualLine = false;
start = lEnd;
}
@@ -1583,7 +1613,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
else {
TextChange softWrap = getSoftWrapModel().getSoftWrap(start);
- if (softWrap == null && getSoftWrapModel().shouldWrap(myDocument.getRawChars(), start, hEnd, position)) {
+ if (softWrap == null
+ && getSoftWrapModel().shouldWrap(g, myDocument.getRawChars(), start, hEnd, position.x, onSoftWrapIntroducedVisualLine))
+ {
softWrap = getSoftWrapModel().wrap(myDocument.getRawChars(), start, hEnd);
}
if (softWrap != null) {
@@ -1593,7 +1625,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
int softWrapEnd = 0;
while (softWrapEnd < softWrapText.length()) {
for (; softWrapEnd < softWrapText.length() && softWrapText.charAt(softWrapEnd) != '\n'; softWrapEnd++) ;
- if (softWrapStart >= softWrapText.length() || softWrapEnd >= softWrapText.length()) {
+ if (softWrapEnd >= softWrapText.length()) {
position.x = drawBackground(
g, backColor, softWrapText.subSequence(softWrapStart, softWrapText.length()), position, fontType, defaultBackground, clip
);
@@ -1607,6 +1639,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
if (position.y > clip.y + clip.height) break outer;
position.x = 0;
position.y += lineHeight;
+ onSoftWrapIntroducedVisualLine = true;
softWrapStart = softWrapEnd + 1;
softWrapEnd = softWrapStart;
}
@@ -1839,7 +1872,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
// to the first soft wrap symbol that is not drawn yet.
int softWrapSegmentStartIndex = 0;
for (int i = 0; i < softWrapChars.length; i++) {
- // Delay soft wraps symbols drawing until EOL if found.
+ // Delay soft wraps symbols drawing until EOL is found.
if (softWrapChars[i] != '\n') {
continue;
}
@@ -1857,6 +1890,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
g, softWrapChars, softWrapSegmentStartIndex, i, position, clip, currentColor, effectType, fontType, currentColor
);
}
+ mySoftWrapModel.paint(g, SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED, position.x, position.y, getLineHeight());
// Reset 'x' coordinate because of new line start.
position.x = 0;
@@ -1877,6 +1911,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
fontType, currentColor
);
}
+ position.x += mySoftWrapModel.paint(g, SoftWrapDrawingType.AFTER_SOFT_WRAP_LINE_FEED, position.x, position.y, getLineHeight());
activeSoftWrapProcessed = true;
}
@@ -1964,7 +1999,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
if (count > 0) {
final int lastCount = count - 1;
final Color lastColor = color[lastCount];
- if (_data == myLastData && _start == ends[lastCount] && (_color == null || lastColor == null || _color == lastColor)) {
+ if (_data == myLastData && _start == ends[lastCount] && (_color == null || lastColor == null || _color == lastColor)
+ && _y == y[lastCount] /* there is a possible case that vertical position is adjusted because of soft wrap */)
+ {
ends[lastCount] = _end;
if (lastColor == null) color[lastCount] = _color;
return;
@@ -2195,17 +2232,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private void drawTabPlacer(Graphics g, int y, int start, int stop) {
if (mySettings.isWhitespacesShown()) {
- stop -= g.getFontMetrics().charWidth(' ') / 2;
- Color oldColor = g.getColor();
- g.setColor(myScheme.getColor(EditorColors.WHITESPACES_COLOR));
- final int charHeight = getCharHeight();
- final int halfCharHeight = charHeight / 2;
- int mid = y - halfCharHeight;
- int top = y - charHeight;
- UIUtil.drawLine(g, start, mid, stop, mid);
- UIUtil.drawLine(g, stop, y, stop, top);
- g.fillPolygon(new int[]{stop - halfCharHeight, stop - halfCharHeight, stop}, new int[]{y, y - charHeight, y - halfCharHeight}, 3);
- g.setColor(oldColor);
+ myTabPainter.paint(g, y, start, stop);
}
}
@@ -2215,23 +2242,27 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
else {
FontInfo fnt = EditorUtil.fontForChar(data[start], fontType, this);
- CachedFontContent cache = null;
- for (CachedFontContent fontCache : myFontCache) {
- if (fontCache.myFontType == fnt) {
- cache = fontCache;
- break;
- }
- }
- if (cache == null) {
- cache = new CachedFontContent(fnt);
- myFontCache.add(cache);
- }
-
- myLastCache = cache;
- cache.addContent(g, data, start, end, x, y, color);
+ drawCharsCached(g, data, start, end, x, y, fnt, color);
}
}
+ private void drawCharsCached(Graphics g, char[] data, int start, int end, int x, int y, FontInfo fnt, Color color) {
+ CachedFontContent cache = null;
+ for (CachedFontContent fontCache : myFontCache) {
+ if (fontCache.myFontType == fnt) {
+ cache = fontCache;
+ break;
+ }
+ }
+ if (cache == null) {
+ cache = new CachedFontContent(fnt);
+ myFontCache.add(cache);
+ }
+
+ myLastCache = cache;
+ cache.addContent(g, data, start, end, x, y, color);
+ }
+
private static boolean spacesOnly(char[] chars, int start, int end) {
for (int i = start; i < end; i++) {
if (chars[i] != ' ') return false;
@@ -4722,4 +4753,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
myOldHeight = getHeight();
}
}
+
+ private class MyTextDrawingCallback implements TextDrawingCallback {
+ @Override
+ public void drawChars(Graphics g, char[] data, int start, int end, int x, int y, Color color, FontInfo fontInfo) {
+ drawCharsCached(g, data, start, end, x, y, fontInfo, color);
+ }
+ }
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java
index 17085851d78e..4967097f74c8 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java
@@ -73,6 +73,7 @@ public class SettingsImpl implements EditorSettings {
private Boolean myIsRenameVariablesInplace = null;
private Boolean myIsRefrainFromScrolling = null;
private Boolean myUseSoftWraps = null;
+ private Boolean myIsSoftWrapsShown = null;
public boolean isRightMarginShown() {
return myIsRightMarginShown != null
@@ -396,6 +397,16 @@ public class SettingsImpl implements EditorSettings {
fireEditorRefresh();
}
+ @Override
+ public boolean isSoftWrapsShown() {
+ return myIsSoftWrapsShown != null ? myIsWhitespacesShown.booleanValue() : EditorSettingsExternalizable.getInstance().isSoftWrapsShown();
+ }
+
+ @Override
+ public void setShowSoftWraps(boolean show) {
+ myIsWhitespacesShown = Boolean.valueOf(show);
+ }
+
private void fireEditorRefresh() {
if (myEditor != null) {
myEditor.reinitSettings();
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
index f7580cb0ca4f..a2b7733b7d6b 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
@@ -23,7 +23,7 @@ import com.intellij.openapi.editor.event.VisibleAreaListener;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.SoftWrapModelEx;
import com.intellij.openapi.editor.ex.util.EditorUtil;
-import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage;
+import com.intellij.openapi.editor.impl.softwrap.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.text.CharArrayUtil;
import gnu.trove.TIntHashSet;
@@ -64,6 +64,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
private final CharBuffer myCharBuffer = CharBuffer.allocate(1);
private final SoftWrapsStorage myStorage;
+ private final SoftWrapPainter myPainter;
/**
* Holds logical lines where soft wraps should be removed.
@@ -93,12 +94,13 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
private int myActive;
public SoftWrapModelImpl(@NotNull EditorEx editor) {
- this(editor, new SoftWrapsStorage());
+ this(editor, new SoftWrapsStorage(), new CompositeSoftWrapPainter(editor));
}
- public SoftWrapModelImpl(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage) {
+ public SoftWrapModelImpl(@NotNull EditorEx editor, @NotNull SoftWrapsStorage storage, @NotNull SoftWrapPainter painter) {
myEditor = editor;
myStorage = storage;
+ myPainter = painter;
}
public boolean isSoftWrappingEnabled() {
@@ -155,13 +157,16 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
* Allows to answer if symbols of the given char array located at [start; end) interval should be soft wrapped,
* i.e. represented on a next line.
*
- * @param chars symbols holder
- * @param start target symbols sub-sequence start within the given char array (inclusive)
- * @param end target symbols sub-sequence end within the given char array (exclusive)
- * @param position current drawing position
- * @return true if target symbols sub-sequence should be soft-wrapped; false otherwise
+ * @param g graphics buffer being used
+ * @param chars symbols holder
+ * @param start target symbols sub-sequence start within the given char array (inclusive)
+ * @param end target symbols sub-sequence end within the given char array (exclusive)
+ * @param x 'x' coordinate of the current drawing position
+ * @param onSoftWrapIntroducedVisualLine defines if current visual position belongs to soft wrap-introduced visual line
+ * @return true if target symbols sub-sequence should be soft-wrapped;
+ * false otherwise
*/
- public boolean shouldWrap(char[] chars, int start, int end, Point position) {
+ public boolean shouldWrap(@NotNull Graphics g, @NotNull char[] chars, int start, int end, int x, boolean onSoftWrapIntroducedVisualLine) {
if (!isSoftWrappingEnabled()) {
return false;
}
@@ -178,7 +183,11 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
}
//TODO den implement
- return position.x + (end - start) * 7 > myRightEdgeLocation;
+ int xAfterCharsDrawing = x + myPainter.getMinDrawingWidth(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED) + (end - start) * 7;
+ if (onSoftWrapIntroducedVisualLine) {
+ xAfterCharsDrawing += myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP_LINE_FEED);
+ }
+ return xAfterCharsDrawing > myRightEdgeLocation;
}
private static boolean containsOnlyWhiteSpaces(char[] chars, int start, int end) {
@@ -243,6 +252,11 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
myDirtyLines.clear();
}
+ @Override
+ public int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) {
+ return myPainter.paint(g, drawingType, x, y, lineHeight);
+ }
+
@NotNull
public LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual) {
if (myActive > 0 || !isSoftWrappingEnabled() || myStorage.isEmpty() || myEditor.getDocument().getTextLength() <= 0) {
@@ -706,7 +720,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
Document document = myEditor.getDocument();
- if (softWrapIndex >= softWraps.size() || document.getTextLength() <= 0) {
+ if (softWrapIndex >= softWraps.size() || document.getTextLength() <= 0 || change.getStart() >= document.getTextLength()) {
return;
}
int firstChangedLine = document.getLineNumber(change.getStart());
@@ -723,7 +737,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx {
// Add modified soft wraps.
for (TextChange softWrap : toModify) {
- softWraps.add(softWrap.advance(change.getDiff()));
+ myStorage.storeSoftWrap(softWrap.advance(change.getDiff()));
}
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/TextDrawingCallback.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/TextDrawingCallback.java
new file mode 100644
index 000000000000..45a7f3631636
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/TextDrawingCallback.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-2010 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 java.awt.*;
+
+/**
+ * IDEA editors use highly-optimized drawing algorithm that is tuned for painting large amounts of text data.
+ *
+ * However, there is a possible case that particular editor extension or third-party component may want to draw text at
+ * IDEA editor on it own. It's not supposed to do that directly, contrary, it's expected to delegate the processing
+ * to the code that knows how to do that.
+ *
+ * Current interface defines a contract for such a text drawing delegation task.
+ *
+ * @author Denis Zhdanov
+ * @since Jul 1, 2010 8:01:30 PM
+ */
+public interface TextDrawingCallback {
+
+ /**
+ * Asks to draw symbols from [start; end) range of given char array at given graphics buffer using given
+ * font info and color.
+ *
+ * @param g graphics buffer to use
+ * @param data target symbols holder
+ * @param start start offset within the symbols holder to use (inclusive)
+ * @param end end offset within the symbols holder to use (inclusive)
+ * @param x 'x' coordinate to use as a start position at the given graphics buffer
+ * @param y 'y' coordinate to use as a start position at the given graphics buffer
+ * @param fontInfo font info to use during drawing target text at the given graphics buffer
+ * @param color color to use during drawing target text at the given graphics buffer
+ */
+ void drawChars(Graphics g, char[] data, int start, int end, int x, int y, Color color, FontInfo fontInfo);
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/ArrowSoftWrapPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/ArrowSoftWrapPainter.java
new file mode 100644
index 000000000000..eed5d982730d
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/ArrowSoftWrapPainter.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2000-2010 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.softwrap;
+
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.ex.util.EditorUtil;
+import com.intellij.openapi.editor.impl.ArrowPainter;
+import com.intellij.openapi.editor.impl.ColorHolder;
+import com.intellij.openapi.util.Computable;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+
+/**
+ * {@link SoftWrapPainter} implementation that draws arrows in soft wrap location.
+ *
+ * Primary idea is to use dedicated unicode symbols as soft wrap drawings and this class is introduced only as a part
+ * of defensive programming - there is unlikely case that local client environment doesn't have a font that is able to
+ * represent target unicode symbol. We draw an arrow manually then (platform-independent approach).
+ *
+ * @author Denis Zhdanov
+ * @since Jul 2, 2010 11:31:36 AM
+ */
+public class ArrowSoftWrapPainter implements SoftWrapPainter {
+
+ private final HeightProvider myHeightProvider = new HeightProvider();
+ private final Editor myEditor;
+ private final ArrowPainter myArrowPainter;
+ private int myMinWidth = -1;
+
+ public ArrowSoftWrapPainter(Editor editor) {
+ myEditor = editor;
+ myArrowPainter = new ArrowPainter(ColorHolder.byColor(myEditor.getColorsScheme().getDefaultForeground()), myHeightProvider);
+ }
+
+ @Override
+ public int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) {
+ myHeightProvider.myHeight = lineHeight / 2;
+
+ int start;
+ int end;
+ int result;
+ switch (drawingType) {
+ case BEFORE_SOFT_WRAP_LINE_FEED:
+ start = x;
+ end = myEditor.getScrollingModel().getVisibleArea().width;
+ result = end - start;
+ break;
+ case AFTER_SOFT_WRAP_LINE_FEED:
+ start = 0;
+ end = x;
+ result = 0;
+ break;
+ default: throw new IllegalStateException("Soft wrap arrow painting is not set up for drawing type " + drawingType);
+ }
+ myArrowPainter.paint(g, y + lineHeight - g.getFontMetrics().getDescent(), start, end);
+ return result;
+ }
+
+ @Override
+ public int getDrawingHorizontalOffset(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) {
+ switch (drawingType) {
+ case BEFORE_SOFT_WRAP_LINE_FEED: return myEditor.getScrollingModel().getVisibleArea().width - x;
+ case AFTER_SOFT_WRAP_LINE_FEED: return 0;
+ default: throw new IllegalStateException("Soft wrap arrow painting is not set up for drawing type " + drawingType);
+ }
+ }
+
+ @Override
+ public int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType) {
+ if (myMinWidth < 0) {
+ // We need to reserve a minimal space required for representing arrow before soft wrap-introduced line feed.
+ myMinWidth = EditorUtil.charWidth('a', Font.PLAIN, myEditor);
+ }
+ return myMinWidth;
+ }
+
+ @Override
+ public boolean canUse() {
+ return true;
+ }
+
+ private static class HeightProvider implements Computable {
+
+ public int myHeight;
+
+ @Override
+ public Integer compute() {
+ return myHeight;
+ }
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/CompositeSoftWrapPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/CompositeSoftWrapPainter.java
new file mode 100644
index 000000000000..78be12630843
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/CompositeSoftWrapPainter.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2000-2010 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.softwrap;
+
+import com.intellij.openapi.editor.LogicalPosition;
+import com.intellij.openapi.editor.VisualPosition;
+import com.intellij.openapi.editor.colors.EditorColors;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.editor.impl.ColorHolder;
+import com.intellij.openapi.editor.impl.TextDrawingCallback;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType.AFTER_SOFT_WRAP_LINE_FEED;
+import static com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED;
+import static java.util.Arrays.asList;
+
+/**
+ * Composite (in terms of GoF patterns) object for {@link SoftWrapPainter} objects.
+ *
+ * Not thread-safe.
+ *
+ * @author Denis Zhdanov
+ * @since Jul 2, 2010 10:20:14 AM
+ */
+public class CompositeSoftWrapPainter implements SoftWrapPainter {
+
+ private static final List