mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-53596 Soft wrap for editors
1. Added ability to provide graphical indication of soft wrap start and end; 2. Added ability to customise if soft wrap start and end should be graphically animated for non-active lines; 3. Only single active visual row is highlighed now (used to highlight whole logical row);
This commit is contained in:
+13
-5
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.application.options.editor.EditorAppearanceConfigurable">
|
||||
<grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="11" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
|
||||
<grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="12" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
|
||||
<margin top="4" left="4" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
@@ -10,7 +10,7 @@
|
||||
<children>
|
||||
<vspacer id="f3f11">
|
||||
<constraints>
|
||||
<grid row="10" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
<grid row="11" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="ef15e" class="javax.swing.JCheckBox" binding="myCbBlinkCaret">
|
||||
@@ -57,7 +57,7 @@
|
||||
</component>
|
||||
<component id="d097a" class="javax.swing.JCheckBox" binding="myShowVerticalIndentGuidesCheckBox" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
<grid row="8" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Show vertical indent guides"/>
|
||||
@@ -76,7 +76,7 @@
|
||||
</component>
|
||||
<grid id="7b00c" binding="myAddonPanel" layout-manager="GridBagLayout">
|
||||
<constraints>
|
||||
<grid row="9" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
|
||||
<grid row="10" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="8" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@@ -100,12 +100,20 @@
|
||||
</component>
|
||||
<component id="f1b45" class="javax.swing.JCheckBox" binding="myCbShowIconsInGutter">
|
||||
<constraints>
|
||||
<grid row="8" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
<grid row="9" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/ApplicationBundle" key="checkbox.show.small.icons.in.gutter"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="98de9" class="javax.swing.JCheckBox" binding="myCbShowSoftwraps">
|
||||
<constraints>
|
||||
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/ApplicationBundle" key="checkbox.show.softwraps"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
|
||||
+4
@@ -48,6 +48,7 @@ public class EditorAppearanceConfigurable extends CompositeConfigurable<UnnamedC
|
||||
private JCheckBox myCbRightMargin;
|
||||
private JCheckBox myCbShowLineNumbers;
|
||||
private JCheckBox myCbShowWhitespaces;
|
||||
private JCheckBox myCbShowSoftwraps;
|
||||
private JTextField myBlinkIntervalField;
|
||||
private JPanel myAddonPanel;
|
||||
private JCheckBox myCbShowMethodSeparators;
|
||||
@@ -82,6 +83,7 @@ public class EditorAppearanceConfigurable extends CompositeConfigurable<UnnamedC
|
||||
myCbShowLineNumbers.setSelected(editorSettings.isLineNumbersShown());
|
||||
myCbBlockCursor.setSelected(editorSettings.isBlockCursor());
|
||||
myCbShowWhitespaces.setSelected(editorSettings.isWhitespacesShown());
|
||||
myCbShowSoftwraps.setSelected(editorSettings.isSoftWrapsShown());
|
||||
myShowVerticalIndentGuidesCheckBox.setSelected(editorSettings.isIndentGuidesShown());
|
||||
myAntialiasingInEditorCheckBox.setSelected(UISettings.getInstance().ANTIALIASING_IN_EDITOR);
|
||||
myCbShowIconsInGutter.setSelected(DaemonCodeAnalyzerSettings.getInstance().SHOW_SMALL_ICONS_IN_GUTTER);
|
||||
@@ -103,6 +105,7 @@ public class EditorAppearanceConfigurable extends CompositeConfigurable<UnnamedC
|
||||
editorSettings.setRightMarginShown(myCbRightMargin.isSelected());
|
||||
editorSettings.setLineNumbersShown(myCbShowLineNumbers.isSelected());
|
||||
editorSettings.setWhitespacesShown(myCbShowWhitespaces.isSelected());
|
||||
editorSettings.setSoftwrapsShown(myCbShowSoftwraps.isSelected());
|
||||
editorSettings.setIndentGuidesShown(myShowVerticalIndentGuidesCheckBox.isSelected());
|
||||
|
||||
EditorOptionsPanel.reinitAllEditors();
|
||||
@@ -135,6 +138,7 @@ public class EditorAppearanceConfigurable extends CompositeConfigurable<UnnamedC
|
||||
|
||||
isModified |= isModified(myCbShowLineNumbers, editorSettings.isLineNumbersShown());
|
||||
isModified |= isModified(myCbShowWhitespaces, editorSettings.isWhitespacesShown());
|
||||
isModified |= isModified(myCbShowSoftwraps, editorSettings.isSoftWrapsShown());
|
||||
isModified |= isModified(myShowVerticalIndentGuidesCheckBox, editorSettings.isIndentGuidesShown());
|
||||
isModified |= isModified(myCbShowMethodSeparators, DaemonCodeAnalyzerSettings.getInstance().SHOW_METHOD_SEPARATORS);
|
||||
isModified |= isModified(myCbShowIconsInGutter, DaemonCodeAnalyzerSettings.getInstance().SHOW_SMALL_ICONS_IN_GUTTER);
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.intellij.openapi.editor.ex.*;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.editor.impl.TextDrawingCallback;
|
||||
import com.intellij.openapi.editor.markup.MarkupModel;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
@@ -183,6 +184,11 @@ public class EditorWindow implements EditorEx, UserDataHolderEx {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextDrawingCallback getTextDrawingCallback() {
|
||||
return myDelegate.getTextDrawingCallback();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SelectionModel getSelectionModel() {
|
||||
return mySelectionModelDelegate;
|
||||
|
||||
@@ -102,7 +102,7 @@ public interface CaretModel {
|
||||
int getVisualLineStart();
|
||||
|
||||
/**
|
||||
* @return document offset for the end of the logical line where caret is located
|
||||
* @return document offset that points to the first symbol shown at the next visual line after the one with caret on it
|
||||
*/
|
||||
int getVisualLineEnd();
|
||||
|
||||
|
||||
@@ -100,4 +100,7 @@ public interface EditorSettings {
|
||||
|
||||
boolean isUseSoftWraps();
|
||||
void setUseSoftWraps(boolean use);
|
||||
|
||||
boolean isSoftWrapsShown();
|
||||
void setShowSoftWraps(boolean show);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.impl.TextDrawingCallback;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -123,4 +124,6 @@ public interface EditorEx extends Editor {
|
||||
int calcColumnNumber(CharSequence text, int start, int offset, int tabSize);
|
||||
|
||||
int calcColumnNumber(int offset, int lineIndex);
|
||||
|
||||
TextDrawingCallback getTextDrawingCallback();
|
||||
}
|
||||
|
||||
+9
@@ -55,6 +55,7 @@ public class EditorSettingsExternalizable implements NamedJDOMExternalizable, Ex
|
||||
|
||||
public boolean IS_BLOCK_CURSOR = false;
|
||||
public boolean IS_WHITESPACES_SHOWN = false;
|
||||
public boolean IS_SOFTWRAPS_SHOWN = false;
|
||||
public boolean IS_INDENT_GUIDES_SHOWN = true;
|
||||
public boolean IS_ANIMATED_SCROLLING = true;
|
||||
public boolean IS_CAMEL_WORDS = false;
|
||||
@@ -313,6 +314,14 @@ public class EditorSettingsExternalizable implements NamedJDOMExternalizable, Ex
|
||||
myOptions.IS_WHITESPACES_SHOWN = val;
|
||||
}
|
||||
|
||||
public boolean isSoftWrapsShown() {
|
||||
return myOptions.IS_SOFTWRAPS_SHOWN;
|
||||
}
|
||||
|
||||
public void setSoftwrapsShown(boolean val) {
|
||||
myOptions.IS_SOFTWRAPS_SHOWN = val;
|
||||
}
|
||||
|
||||
public boolean isIndentGuidesShown() {
|
||||
return myOptions.IS_INDENT_GUIDES_SHOWN;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.SoftWrapModel;
|
||||
import com.intellij.openapi.editor.TextChange;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -69,4 +71,16 @@ public interface SoftWrapModelEx extends SoftWrapModel {
|
||||
* @return unmodifiable collection of soft wraps currently registered within the current model
|
||||
*/
|
||||
List<TextChange> 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 <code>'x'</code> coordinate to use
|
||||
* @param y target <code>'y'</code> 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);
|
||||
}
|
||||
|
||||
@@ -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<Integer> myHeightProvider;
|
||||
|
||||
public ArrowPainter(@NotNull ColorHolder colorHolder, @NotNull Computable<Integer> 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 <code>'x'</code> position to use during drawing
|
||||
* @param stop ending <code>'x'</code> 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
* <code>'y'</code> 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Integer>() {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
+26
-12
@@ -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 <code>[start; end)</code> 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 <code>true</code> if target symbols sub-sequence should be soft-wrapped; <code>false</code> 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 <code>'x'</code> coordinate of the current drawing position
|
||||
* @param onSoftWrapIntroducedVisualLine defines if current visual position belongs to soft wrap-introduced visual line
|
||||
* @return <code>true</code> if target symbols sub-sequence should be soft-wrapped;
|
||||
* <code>false</code> 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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* 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 <code>[start; end)</code> 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 <code>'x'</code> coordinate to use as a start position at the given graphics buffer
|
||||
* @param y <code>'y'</code> 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);
|
||||
}
|
||||
+105
@@ -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.
|
||||
* <p/>
|
||||
* 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<Integer> {
|
||||
|
||||
public int myHeight;
|
||||
|
||||
@Override
|
||||
public Integer compute() {
|
||||
return myHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
+140
@@ -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.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Jul 2, 2010 10:20:14 AM
|
||||
*/
|
||||
public class CompositeSoftWrapPainter implements SoftWrapPainter {
|
||||
|
||||
private static final List<Map<SoftWrapDrawingType, Character>> 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.
|
||||
* <p/>
|
||||
* 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 <K, V> Map<K, V> asMap(Iterable<K> keys, Iterable<V> values) throws IllegalArgumentException {
|
||||
Map<K, V> result = new HashMap<K,V>();
|
||||
Iterator<K> keyIterator = keys.iterator();
|
||||
Iterator<V> 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;
|
||||
}
|
||||
}
|
||||
+26
@@ -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
|
||||
}
|
||||
+84
@@ -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.
|
||||
* <p/>
|
||||
* For example we may want to draw an arrow just before and after soft wrap-introduced line feed:
|
||||
* <p/>
|
||||
* <pre>
|
||||
* This is a long text 
|
||||
* that is soft-wrapped
|
||||
* </pre>
|
||||
* <p/>
|
||||
* 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 <code>'x'</code> coordinate to use
|
||||
* @param y target <code>'y'</code> 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 <code>'x'</code> coordinate if drawing of the given
|
||||
* type is performed at the given graphics buffer.
|
||||
* <p/>
|
||||
* 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 <code>'x'</code> coordinate to use
|
||||
* @param y target <code>'y'</code> 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 <code>true</code> if current painter can be used at local environment; <code>false</code> otherwise
|
||||
*/
|
||||
boolean canUse();
|
||||
}
|
||||
+116
@@ -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.
|
||||
* <p/>
|
||||
* Not thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since Jul 1, 2010 5:30:43 PM
|
||||
*/
|
||||
public class TextBasedSoftWrapPainter implements SoftWrapPainter {
|
||||
|
||||
private final Map<SoftWrapDrawingType, char[]> mySymbols = new EnumMap<SoftWrapDrawingType, char[]>(SoftWrapDrawingType.class);
|
||||
private final Map<SoftWrapDrawingType, FontInfo> myFonts = new EnumMap<SoftWrapDrawingType, FontInfo>(SoftWrapDrawingType.class);
|
||||
private final Map<SoftWrapDrawingType, Integer> myWidths = new EnumMap<SoftWrapDrawingType, Integer>(SoftWrapDrawingType.class);
|
||||
private final Map<SoftWrapDrawingType, Integer> myVGaps = new EnumMap<SoftWrapDrawingType, Integer>(SoftWrapDrawingType.class);
|
||||
|
||||
private final TextDrawingCallback myDrawingCallback;
|
||||
private final ColorHolder myColorHolder;
|
||||
private final boolean myCanUse;
|
||||
|
||||
public TextBasedSoftWrapPainter(Map<SoftWrapDrawingType, Character> 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 <code>true</code> if target font that is capable to display all unicode symbols used by the current painter is found;
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private boolean init(Map<SoftWrapDrawingType, Character> 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<SoftWrapDrawingType, Character> 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;
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user