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 @@
- + @@ -10,7 +10,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -76,7 +76,7 @@ - + @@ -100,12 +100,20 @@ - + + + + + + + + + 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> SYMBOLS = asList( + asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP_LINE_FEED), + asList('\uE48B', '\uE48C')), + asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP_LINE_FEED), + asList('\u2926', '\u2925')), + asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP_LINE_FEED), + asList('\u21B2', '\u21B3')), + asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP_LINE_FEED), + asList('\u2936', '\u2937')), + asMap(asList(BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP_LINE_FEED), + asList('\u21A9', '\u21AA')) + ); + + private final EditorEx myEditor; + private SoftWrapPainter myDelegate; + + /** + * There is a possible case that particular symbols configured to be used as a soft wrap drawings are not supported by + * available fonts. We would like to try another symbols then. + *

+ * Current field points to the index of symbols collection from {@link #SYMBOLS} tried last time. + */ + private int mySymbolsDrawingIndex = -1; + + public CompositeSoftWrapPainter(EditorEx editor) { + myEditor = editor; + } + + @Override + public int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { + initDelegateIfNecessary(); + if (!myEditor.getSettings().isSoftWrapsShown()) { + int visualLine = y / lineHeight; + LogicalPosition position = myEditor.visualToLogicalPosition(new VisualPosition(visualLine, 0)); + if (position.line != myEditor.getCaretModel().getLogicalPosition().line) { + return myDelegate.getDrawingHorizontalOffset(g, drawingType, x, y, lineHeight); + } + } + return myDelegate.paint(g, drawingType, x, y, lineHeight); + } + + @Override + public int getDrawingHorizontalOffset(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { + initDelegateIfNecessary(); + return myDelegate.getDrawingHorizontalOffset(g, drawingType, x, y, lineHeight); + } + + @Override + public int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType) { + initDelegateIfNecessary(); + return myDelegate.getMinDrawingWidth(drawingType); + } + + @Override + public boolean canUse() { + return true; + } + + private void initDelegateIfNecessary() { + if (myDelegate != null && myDelegate.canUse()) { + return; + } + if (++mySymbolsDrawingIndex < SYMBOLS.size()) { + TextDrawingCallback callback = myEditor.getTextDrawingCallback(); + ColorHolder colorHolder = ColorHolder.byColorsScheme(myEditor, EditorColors.RIGHT_MARGIN_COLOR); + myDelegate = new TextBasedSoftWrapPainter(SYMBOLS.get(mySymbolsDrawingIndex), myEditor, callback, colorHolder); + initDelegateIfNecessary(); + return; + } + myDelegate = new ArrowSoftWrapPainter(myEditor); + } + + private static Map asMap(Iterable keys, Iterable values) throws IllegalArgumentException { + Map result = new HashMap(); + Iterator keyIterator = keys.iterator(); + Iterator valueIterator = values.iterator(); + while (keyIterator.hasNext()) { + if (!valueIterator.hasNext()) { + throw new IllegalArgumentException( + String.format("Can't build for the given data. Reason: number of keys differs from number of values. " + + "Keys: %s, values: %s", keys, values) + ); + } + result.put(keyIterator.next(), valueIterator.next()); + } + + if (valueIterator.hasNext()) { + throw new IllegalArgumentException( + String.format("Can't build for the given data. Reason: number of keys differs from number of values. " + + "Keys: %s, values: %s", keys, values) + ); + } + return result; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDrawingType.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDrawingType.java new file mode 100644 index 000000000000..1224006dc01d --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapDrawingType.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * Enumerates types soft wrap-related drawings supported by {@link SoftWrapPainter}. + * + * @author Denis Zhdanov + * @since Jul 1, 2010 5:19:45 PM + */ +public enum SoftWrapDrawingType { + BEFORE_SOFT_WRAP_LINE_FEED, AFTER_SOFT_WRAP_LINE_FEED +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapPainter.java new file mode 100644 index 000000000000..5233a63f56df --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapPainter.java @@ -0,0 +1,84 @@ +/* + * 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 org.jetbrains.annotations.NotNull; + +import java.awt.*; + +/** + * Defines contract for the service that manages soft wrap-related graphical effects. + *

+ * For example we may want to draw an arrow just before and after soft wrap-introduced line feed: + *

+ *

+ *     This is a long text 
+ *         that is soft-wrapped
+ * 
+ *

+ * Implementations of this interface are not obliged to be thread-safe. + * + * @author Denis Zhdanov + * @since Jul 1, 2010 5:02:37 PM + */ +public interface SoftWrapPainter { + + /** + * 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 horizontal offset introduced to the given 'x' coordinate after target drawing painting + */ + int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight); + + /** + * Allows to ask about horizontal offset to be applied to the given 'x' coordinate if drawing of the given + * type is performed at the given graphics buffer. + *

+ * Generally, this method is useful when we don't want to perform actual drawing for now but want to reserve + * a space necessary to do that in future. I.e. the aim is to avoid horizontal movement of already drawn content + * when the drawing is actually performed. + * + * @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 horizontal offset that would be introduced if the drawing is performed + */ + int getDrawingHorizontalOffset(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight); + + /** + * Allows to ask for the minimal width in pixels required for painting of the given type. + * + * @param drawingType target drawing type + * @return width in pixels required for the painting of the given type + */ + int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType); + + /** + * Allows to answer if it's possible to use current painter implementation at local environment (e.g. there is a possible + * case that particular painter that exploits unicode symbols for drawing can't be used because there is no font + * at local environment that knows how to draw target symbols). + * + * @return true if current painter can be used at local environment; false otherwise + */ + boolean canUse(); +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/TextBasedSoftWrapPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/TextBasedSoftWrapPainter.java new file mode 100644 index 000000000000..fd68ccc36163 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/TextBasedSoftWrapPainter.java @@ -0,0 +1,116 @@ +/* + * 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.impl.ColorHolder; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.util.EditorUtil; +import com.intellij.openapi.editor.impl.FontInfo; +import com.intellij.openapi.editor.impl.TextDrawingCallback; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.util.EnumMap; +import java.util.Map; + +/** + * {@link SoftWrapPainter} implementation that uses target unicode symbols as soft wrap drawings. + *

+ * Not thread-safe. + * + * @author Denis Zhdanov + * @since Jul 1, 2010 5:30:43 PM + */ +public class TextBasedSoftWrapPainter implements SoftWrapPainter { + + private final Map mySymbols = new EnumMap(SoftWrapDrawingType.class); + private final Map myFonts = new EnumMap(SoftWrapDrawingType.class); + private final Map myWidths = new EnumMap(SoftWrapDrawingType.class); + private final Map myVGaps = new EnumMap(SoftWrapDrawingType.class); + + private final TextDrawingCallback myDrawingCallback; + private final ColorHolder myColorHolder; + private final boolean myCanUse; + + public TextBasedSoftWrapPainter(Map symbols, Editor editor, TextDrawingCallback drawingCallback, + ColorHolder colorHolder) + throws IllegalArgumentException + { + if (symbols.size() != SoftWrapDrawingType.values().length) { + throw new IllegalArgumentException( + String.format("Can't create text-based soft wrap painter. Reason: given 'drawing type -> symbol' mappings " + + "are incomplete - expected size %d but got %d (%s)", SoftWrapDrawingType.values().length, symbols.size(), symbols) + ); + } + myDrawingCallback = drawingCallback; + myColorHolder = colorHolder; + myCanUse = init(symbols, editor); + } + + @Override + public int paint(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { + char[] buffer = mySymbols.get(drawingType); + FontInfo fontInfo = myFonts.get(drawingType); + int vGap = myVGaps.get(drawingType); + myDrawingCallback.drawChars(g, buffer, 0, buffer.length, x, y + lineHeight - vGap, myColorHolder.getColor(), fontInfo); + return getMinDrawingWidth(drawingType); + } + + @Override + public int getDrawingHorizontalOffset(@NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { + return getMinDrawingWidth(drawingType); + } + + @Override + public int getMinDrawingWidth(@NotNull SoftWrapDrawingType drawingType) { + return myWidths.get(drawingType); + } + + @Override + public boolean canUse() { + return myCanUse; + } + + /** + * Tries to find fonts that are capable to display all unicode symbols used by the current painter. + * + * @param symbols target symbols to use for drawing + * @param editor editor to use during font lookup + * @return true if target font that is capable to display all unicode symbols used by the current painter is found; + * false otherwise + */ + private boolean init(Map symbols, Editor editor) { + // We use dummy component here in order to being able to work with font metrics. + JLabel component = new JLabel(); + + for (Map.Entry entry : symbols.entrySet()) { + FontInfo fontInfo = EditorUtil.fontForChar(entry.getValue(), Font.PLAIN, editor); + if (!fontInfo.canDisplay(entry.getValue())) { + return false; + } + char[] buffer = new char[1]; + buffer[0] = entry.getValue(); + mySymbols.put(entry.getKey(), buffer); + myFonts.put(entry.getKey(), fontInfo); + FontMetrics metrics = component.getFontMetrics(fontInfo.getFont()); + myWidths.put(entry.getKey(), metrics.charWidth(buffer[0])); + int vGap = metrics.getDescent(); + myVGaps.put(entry.getKey(), vGap); + } + return true; + } +} diff --git a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/SoftWrapModelImplTest.java b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/SoftWrapModelImplTest.java index d1c97b570825..d85bee6e0b7f 100644 --- a/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/SoftWrapModelImplTest.java +++ b/platform/platform-impl/testSrc/com/intellij/openapi/editor/impl/SoftWrapModelImplTest.java @@ -18,6 +18,7 @@ package com.intellij.openapi.editor.impl; import com.intellij.mock.MockFoldRegion; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.editor.impl.softwrap.SoftWrapPainter; import com.intellij.openapi.editor.impl.softwrap.SoftWrapsStorage; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; @@ -191,7 +192,9 @@ public class SoftWrapModelImplTest { }); }}); - myModel = new SoftWrapModelImpl(myEditor, myStorage); + SoftWrapPainter painter = myMockery.mock(SoftWrapPainter.class); + + myModel = new SoftWrapModelImpl(myEditor, myStorage, painter); } @After diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 8676e25d3ca4..48fdec32a60c 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -262,6 +262,7 @@ tab.editor.settings.appearance=Appearance groupbox.display=Display checkbox.smooth.scrolling=Smooth scrolling checkbox.show.whitespaces=Show whitespaces +checkbox.show.softwraps=Show soft wraps checkbox.show.method.separators=Show method separators checkbox.show.small.icons.in.gutter=Show icons preview in gutter for small icons (Java) checkbox.show.line.numbers=Show line numbers