| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Created by IntelliJ IDEA. |
| * User: max |
| * Date: Apr 19, 2002 |
| * Time: 2:56:43 PM |
| * To change template for new class use |
| * Code Style | Class Templates options (Tools | IDE Options). |
| */ |
| package com.intellij.openapi.editor.impl; |
| |
| import com.intellij.codeInsight.hint.*; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.ui.UISettings; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ex.ApplicationManagerEx; |
| import com.intellij.openapi.application.impl.ApplicationImpl; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.command.UndoConfirmationPolicy; |
| import com.intellij.openapi.editor.*; |
| import com.intellij.openapi.editor.actionSystem.DocCommandGroupId; |
| import com.intellij.openapi.editor.ex.*; |
| import com.intellij.openapi.editor.markup.ErrorStripeRenderer; |
| import com.intellij.openapi.editor.markup.RangeHighlighter; |
| import com.intellij.openapi.ui.MessageType; |
| import com.intellij.openapi.ui.popup.Balloon; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.ProperTextRange; |
| import com.intellij.ui.*; |
| import com.intellij.ui.awt.RelativePoint; |
| import com.intellij.util.Processor; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.ui.ButtonlessScrollBarUI; |
| import com.intellij.util.ui.GraphicsUtil; |
| import com.intellij.util.ui.UIUtil; |
| import gnu.trove.THashSet; |
| import gnu.trove.TIntIntHashMap; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.plaf.ScrollBarUI; |
| import java.awt.*; |
| import java.awt.event.MouseEvent; |
| import java.awt.event.MouseListener; |
| import java.awt.event.MouseMotionListener; |
| import java.awt.geom.AffineTransform; |
| import java.awt.image.BufferedImage; |
| import java.util.*; |
| import java.util.List; |
| import java.util.Queue; |
| |
| public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMarkupModel { |
| private static final TooltipGroup ERROR_STRIPE_TOOLTIP_GROUP = new TooltipGroup("ERROR_STRIPE_TOOLTIP_GROUP", 0); |
| private static final Icon ERRORS_FOUND_ICON = AllIcons.General.ErrorsFound; |
| private static final int ERROR_ICON_WIDTH = ERRORS_FOUND_ICON.getIconWidth(); |
| private static final int ERROR_ICON_HEIGHT = ERRORS_FOUND_ICON.getIconHeight(); |
| private static final int PREFERRED_WIDTH = ERROR_ICON_WIDTH + 3; |
| private final EditorImpl myEditor; |
| // null renderer means we should not show traffic light icon |
| private ErrorStripeRenderer myErrorStripeRenderer; |
| private final List<ErrorStripeListener> myErrorMarkerListeners = ContainerUtil.createLockFreeCopyOnWriteList(); |
| |
| private boolean dimensionsAreValid; |
| private int myEditorScrollbarTop = -1; |
| private int myEditorTargetHeight = -1; |
| private int myEditorSourceHeight = -1; |
| private ProperTextRange myDirtyYPositions; |
| private static final ProperTextRange WHOLE_DOCUMENT = new ProperTextRange(0, 0); |
| |
| @NotNull private ErrorStripTooltipRendererProvider myTooltipRendererProvider = new BasicTooltipRendererProvider(); |
| |
| private int myMinMarkHeight = 3; |
| private static final int myPreviewLines = 5;// Actually preview has myPreviewLines * 2 + 1 lines (above + below + current one) |
| private LightweightHint myEditorPreviewHint = null; |
| private final EditorFragmentRenderer myEditorFragmentRenderer; |
| |
| EditorMarkupModelImpl(@NotNull EditorImpl editor) { |
| super(editor.getDocument()); |
| myEditor = editor; |
| myEditorFragmentRenderer = new EditorFragmentRenderer(); |
| } |
| |
| private int offsetToLine(int offset, Document document) { |
| if (offset < 0) { |
| return 0; |
| } |
| if (offset > document.getTextLength()) { |
| return document.getLineCount(); |
| } |
| return myEditor.offsetToVisualLine(offset); |
| } |
| |
| public void repaintVerticalScrollBar() { |
| myEditor.getVerticalScrollBar().repaint(); |
| } |
| |
| void recalcEditorDimensions() { |
| EditorImpl.MyScrollBar scrollBar = myEditor.getVerticalScrollBar(); |
| int scrollBarHeight = scrollBar.getSize().height; |
| |
| myEditorScrollbarTop = scrollBar.getDecScrollButtonHeight()/* + 1*/; |
| int editorScrollbarBottom = scrollBar.getIncScrollButtonHeight(); |
| myEditorTargetHeight = scrollBarHeight - myEditorScrollbarTop - editorScrollbarBottom; |
| myEditorSourceHeight = myEditor.getPreferredHeight(); |
| |
| dimensionsAreValid = scrollBarHeight != 0; |
| } |
| |
| public void repaintTrafficLightIcon() { |
| MyErrorPanel errorPanel = getErrorPanel(); |
| if (errorPanel != null) { |
| errorPanel.myErrorStripeButton.repaint(); |
| errorPanel.repaintTrafficTooltip(); |
| } |
| } |
| |
| private static class PositionedStripe { |
| private final Color color; |
| private int yEnd; |
| private final boolean thin; |
| private final int layer; |
| |
| private PositionedStripe(Color color, int yEnd, boolean thin, int layer) { |
| this.color = color; |
| this.yEnd = yEnd; |
| this.thin = thin; |
| this.layer = layer; |
| } |
| } |
| |
| private boolean showToolTipByMouseMove(final MouseEvent e) { |
| MouseEvent me = new MouseEvent(e.getComponent(), e.getID(), e.getWhen(), e.getModifiers(), 0, e.getY() + 1, e.getClickCount(), |
| e.isPopupTrigger()); |
| |
| final int line = getLineByEvent(e); |
| Rectangle area = myEditor.getScrollingModel().getVisibleArea(); |
| //int realY = (int)(((float)e.getY() / e.getComponent().getHeight()) * myEditor.getContentComponent().getHeight()); |
| int realY = myEditor.getLineHeight() * line; |
| boolean isVisible = area.contains(area.x, realY);//area.y < realY && area.y + area.height > realY; |
| |
| TooltipRenderer bigRenderer; |
| if (!ApplicationManager.getApplication().isInternal() || isVisible) { |
| final Set<RangeHighlighter> highlighters = new THashSet<RangeHighlighter>(); |
| getNearestHighlighters(this, me.getY(), highlighters); |
| getNearestHighlighters((MarkupModelEx)DocumentMarkupModel.forDocument(myEditor.getDocument(), getEditor().getProject(), true), me.getY(), highlighters); |
| if (highlighters.isEmpty()) return false; |
| |
| int y = e.getY(); |
| RangeHighlighter nearest = getNearestRangeHighlighter(e); |
| if (nearest != null) { |
| ProperTextRange range = offsetsToYPositions(nearest.getStartOffset(), nearest.getEndOffset()); |
| int eachStartY = range.getStartOffset(); |
| int eachEndY = range.getEndOffset(); |
| y = eachStartY + (eachEndY - eachStartY) / 2; |
| } |
| me = new MouseEvent(e.getComponent(), e.getID(), e.getWhen(), e.getModifiers(), me.getX(), y + 1, e.getClickCount(), |
| e.isPopupTrigger()); |
| bigRenderer = myTooltipRendererProvider.calcTooltipRenderer(highlighters); |
| if (bigRenderer != null) { |
| HintHint hint = new HintHint(me).setAwtTooltip(true).setPreferredPosition(Balloon.Position.atLeft).setShowImmediately(true).setAnimationEnabled(false); |
| showTooltip(me, bigRenderer, hint); |
| return true; |
| } |
| return false; |
| } else { |
| final List<RangeHighlighterEx> highlighters = new ArrayList<RangeHighlighterEx>(); |
| collectRangeHighlighters(this, line, highlighters); |
| collectRangeHighlighters((MarkupModelEx)DocumentMarkupModel.forDocument(myEditor.getDocument(), getEditor().getProject(), true), line, |
| highlighters); |
| myEditorFragmentRenderer.update(line, highlighters); |
| HintHint hint = new HintHint(me).setAwtTooltip(true).setPreferredPosition(Balloon.Position.atLeft).setShowImmediately(true) |
| .setAnimationEnabled(false); |
| myEditorFragmentRenderer.show(myEditor, me.getPoint(), true, ERROR_STRIPE_TOOLTIP_GROUP, hint); |
| return true; |
| } |
| } |
| |
| private int getLineByEvent(MouseEvent e) { |
| int line = myEditor.offsetToLogicalLine(yPositionToOffset(e.getY(), true)); |
| int foldingLineDecrement = 0; |
| FoldRegion[] regions = myEditor.getFoldingModel().getAllFoldRegions(); |
| for (FoldRegion region : regions) { |
| if (region.isExpanded()) continue; |
| int startFoldingLine = myEditor.offsetToLogicalLine(region.getStartOffset()); |
| int endFoldingLine = myEditor.offsetToLogicalLine(region.getEndOffset()); |
| if (startFoldingLine <= line) { |
| foldingLineDecrement += endFoldingLine - startFoldingLine; |
| } |
| } |
| return Math.min(myEditor.getVisibleLineCount(), Math.max(0, line - foldingLineDecrement)); |
| } |
| |
| private void collectRangeHighlighters(MarkupModelEx markupModel, final int currentLine, final Collection<RangeHighlighterEx> highlighters) { |
| int startOffset = myEditor.getDocument().getLineStartOffset(Math.max(0, currentLine - myPreviewLines)); |
| int endOffset = myEditor.getDocument().getLineEndOffset(Math.min(myEditor.getDocument().getLineCount() -1 , currentLine + myPreviewLines)); |
| markupModel.processRangeHighlightersOverlappingWith(startOffset, endOffset, new Processor<RangeHighlighterEx>() { |
| @Override |
| public boolean process(RangeHighlighterEx highlighter) { |
| if (highlighter.getErrorStripeMarkColor() != null) { |
| int startLine = offsetToLine(highlighter.getStartOffset(), myEditor.getDocument()); |
| int endLine = offsetToLine(highlighter.getStartOffset(), myEditor.getDocument()); |
| if (startLine <= currentLine + myPreviewLines && endLine >= currentLine - myPreviewLines) { |
| highlighters.add(highlighter); |
| } |
| } |
| return true; |
| } |
| }); |
| } |
| |
| @Nullable |
| private RangeHighlighter getNearestRangeHighlighter(final MouseEvent e) { |
| List<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>(); |
| getNearestHighlighters(this, e.getY(), highlighters); |
| getNearestHighlighters((MarkupModelEx)DocumentMarkupModel.forDocument(myEditor.getDocument(), myEditor.getProject(), true), e.getY(), |
| highlighters); |
| RangeHighlighter nearestMarker = null; |
| int yPos = 0; |
| for (RangeHighlighter highlighter : highlighters) { |
| final int newYPos = offsetsToYPositions(highlighter.getStartOffset(), highlighter.getEndOffset()).getStartOffset(); |
| |
| if (nearestMarker == null || Math.abs(yPos - e.getY()) > Math.abs(newYPos - e.getY())) { |
| nearestMarker = highlighter; |
| yPos = newYPos; |
| } |
| } |
| return nearestMarker; |
| } |
| |
| private void getNearestHighlighters(MarkupModelEx markupModel, |
| final int y, |
| final Collection<RangeHighlighter> nearest) { |
| int startOffset = yPositionToOffset(y - myMinMarkHeight, true); |
| int endOffset = yPositionToOffset(y + myMinMarkHeight, false); |
| markupModel.processRangeHighlightersOverlappingWith(startOffset, endOffset, new Processor<RangeHighlighterEx>() { |
| @Override |
| public boolean process(RangeHighlighterEx highlighter) { |
| if (highlighter.getErrorStripeMarkColor() != null) { |
| ProperTextRange range = offsetsToYPositions(highlighter.getStartOffset(), highlighter.getEndOffset()); |
| if (y >= range.getStartOffset() - myMinMarkHeight * 2 && |
| y <= range.getEndOffset() + myMinMarkHeight * 2) { |
| nearest.add(highlighter); |
| } |
| } |
| return true; |
| } |
| }); |
| } |
| |
| private void doClick(final MouseEvent e) { |
| RangeHighlighter marker = getNearestRangeHighlighter(e); |
| int offset; |
| LogicalPosition logicalPositionToScroll = null; |
| if (marker == null) { |
| if (myEditorPreviewHint != null) { |
| logicalPositionToScroll = myEditor.visualToLogicalPosition(new VisualPosition(myEditorFragmentRenderer.myStartLine, 0)); |
| offset = myEditor.getDocument().getLineStartOffset(logicalPositionToScroll.line); |
| } else { |
| return; |
| } |
| } else { |
| offset = marker.getStartOffset(); |
| } |
| |
| final Document doc = myEditor.getDocument(); |
| if (doc.getLineCount() > 0 && myEditorPreviewHint == null) { |
| // Necessary to expand folded block even if navigating just before one |
| // Very useful when navigating to first unused import statement. |
| int lineEnd = doc.getLineEndOffset(doc.getLineNumber(offset)); |
| myEditor.getCaretModel().moveToOffset(lineEnd); |
| } |
| |
| myEditor.getCaretModel().moveToOffset(offset); |
| myEditor.getSelectionModel().removeSelection(); |
| ScrollingModel scrollingModel = myEditor.getScrollingModel(); |
| scrollingModel.disableAnimation(); |
| if (logicalPositionToScroll != null) { |
| int lineY = myEditor.logicalPositionToXY(logicalPositionToScroll).y; |
| int relativePopupOffset = myEditorFragmentRenderer.myRelativeY; |
| scrollingModel.scrollVertically(lineY - relativePopupOffset); |
| } |
| else { |
| scrollingModel.scrollToCaret(ScrollType.CENTER); |
| } |
| scrollingModel.enableAnimation(); |
| if (marker != null) { |
| fireErrorMarkerClicked(marker, e); |
| } |
| } |
| |
| @Override |
| public void setErrorStripeVisible(boolean val) { |
| if (val) { |
| myEditor.getVerticalScrollBar().setPersistentUI(new MyErrorPanel()); |
| } |
| else { |
| myEditor.getVerticalScrollBar().setPersistentUI(ButtonlessScrollBarUI.createNormal()); |
| } |
| } |
| |
| @Nullable |
| private MyErrorPanel getErrorPanel() { |
| ScrollBarUI ui = myEditor.getVerticalScrollBar().getUI(); |
| return ui instanceof MyErrorPanel ? (MyErrorPanel)ui : null; |
| } |
| |
| @Override |
| public void setErrorPanelPopupHandler(@NotNull PopupHandler handler) { |
| ApplicationManager.getApplication().assertIsDispatchThread(); |
| MyErrorPanel errorPanel = getErrorPanel(); |
| if (errorPanel != null) { |
| errorPanel.setPopupHandler(handler); |
| } |
| } |
| |
| @Override |
| public void setErrorStripTooltipRendererProvider(@NotNull final ErrorStripTooltipRendererProvider provider) { |
| myTooltipRendererProvider = provider; |
| } |
| |
| @Override |
| @NotNull |
| public ErrorStripTooltipRendererProvider getErrorStripTooltipRendererProvider() { |
| return myTooltipRendererProvider; |
| } |
| |
| @Override |
| @NotNull |
| public Editor getEditor() { |
| return myEditor; |
| } |
| |
| @Override |
| public void setErrorStripeRenderer(ErrorStripeRenderer renderer) { |
| assertIsDispatchThread(); |
| if (myErrorStripeRenderer instanceof Disposable) { |
| Disposer.dispose((Disposable)myErrorStripeRenderer); |
| } |
| myErrorStripeRenderer = renderer; |
| //try to not cancel tooltips here, since it is being called after every writeAction, even to the console |
| //HintManager.getInstance().getTooltipController().cancelTooltips(); |
| |
| myEditor.getVerticalScrollBar() |
| .updateUI(); // re-create increase/decrease buttons, in case of not-null renderer it will show traffic light icon |
| repaintVerticalScrollBar(); |
| } |
| |
| private void assertIsDispatchThread() { |
| ApplicationManagerEx.getApplicationEx().assertIsDispatchThread(myEditor.getComponent()); |
| } |
| |
| @Override |
| public ErrorStripeRenderer getErrorStripeRenderer() { |
| return myErrorStripeRenderer; |
| } |
| |
| @Override |
| public void dispose() { |
| |
| final MyErrorPanel panel = getErrorPanel(); |
| if (panel != null) { |
| panel.uninstallListeners(); |
| } |
| |
| if (myErrorStripeRenderer instanceof Disposable) { |
| Disposer.dispose((Disposable)myErrorStripeRenderer); |
| } |
| myErrorStripeRenderer = null; |
| super.dispose(); |
| } |
| |
| // startOffset == -1 || endOffset == -1 means whole document |
| void repaint(int startOffset, int endOffset) { |
| ProperTextRange range = offsetsToYPositions(startOffset, endOffset); |
| markDirtied(range); |
| if (startOffset == -1 || endOffset == -1) { |
| myDirtyYPositions = WHOLE_DOCUMENT; |
| } |
| |
| myEditor.getVerticalScrollBar().repaint(0, range.getStartOffset(), PREFERRED_WIDTH, range.getLength() + myMinMarkHeight); |
| } |
| |
| private boolean isMirrored() { |
| return myEditor.getVerticalScrollbarOrientation() == EditorEx.VERTICAL_SCROLLBAR_LEFT; |
| } |
| |
| private static final Dimension STRIPE_BUTTON_PREFERRED_SIZE = new Dimension(PREFERRED_WIDTH, ERROR_ICON_HEIGHT + 4); |
| |
| private class ErrorStripeButton extends JButton { |
| private ErrorStripeButton() { |
| setFocusable(false); |
| } |
| |
| @Override |
| public void paint(Graphics g) { |
| ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart(); |
| |
| final Rectangle bounds = getBounds(); |
| try { |
| if (UISettings.getInstance().PRESENTATION_MODE) { |
| g.setColor(getEditor().getColorsScheme().getDefaultBackground()); |
| g.fillRect(0, 0, bounds.width, bounds.height); |
| |
| if (myErrorStripeRenderer != null) { |
| myErrorStripeRenderer.paint(this, g, new Rectangle(2, 0, 10, 7)); |
| } |
| } else { |
| |
| g.setColor(ButtonlessScrollBarUI.getTrackBackground()); |
| g.fillRect(0, 0, bounds.width, bounds.height); |
| |
| g.setColor(ButtonlessScrollBarUI.getTrackBorderColor()); |
| g.drawLine(0, 0, 0, bounds.height); |
| |
| if (myErrorStripeRenderer != null) { |
| myErrorStripeRenderer.paint(this, g, new Rectangle(5, 2, ERROR_ICON_WIDTH, ERROR_ICON_HEIGHT)); |
| } |
| } |
| } |
| finally { |
| ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintFinish(); |
| } |
| } |
| |
| @Override |
| public Dimension getPreferredSize() { |
| return UISettings.getInstance().PRESENTATION_MODE ? new Dimension(10,7) : STRIPE_BUTTON_PREFERRED_SIZE; |
| } |
| } |
| |
| private class MyErrorPanel extends ButtonlessScrollBarUI implements MouseMotionListener, MouseListener { |
| private PopupHandler myHandler; |
| private JButton myErrorStripeButton; |
| private BufferedImage myCachedTrack; |
| |
| @Override |
| protected JButton createDecreaseButton(int orientation) { |
| myErrorStripeButton = myErrorStripeRenderer == null ? super.createDecreaseButton(orientation) : new ErrorStripeButton(); |
| return myErrorStripeButton; |
| } |
| |
| @Override |
| protected void installListeners() { |
| super.installListeners(); |
| scrollbar.addMouseMotionListener(this); |
| scrollbar.addMouseListener(this); |
| myErrorStripeButton.addMouseMotionListener(this); |
| myErrorStripeButton.addMouseListener(this); |
| } |
| |
| @Override |
| protected void uninstallListeners() { |
| scrollbar.removeMouseMotionListener(this); |
| scrollbar.removeMouseListener(this); |
| myErrorStripeButton.removeMouseMotionListener(this); |
| myErrorStripeButton.removeMouseListener(this); |
| super.uninstallListeners(); |
| } |
| |
| @Override |
| protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { |
| if (UISettings.getInstance().PRESENTATION_MODE) { |
| super.paintThumb(g, c, thumbBounds); |
| return; |
| } |
| int shift = isMirrored() ? -9 : 9; |
| g.translate(shift, 0); |
| super.paintThumb(g, c, thumbBounds); |
| g.translate(-shift, 0); |
| } |
| |
| @Override |
| protected int adjustThumbWidth(int width) { |
| if (UISettings.getInstance().PRESENTATION_MODE) return super.adjustThumbWidth(width); |
| return width - 2; |
| } |
| |
| @Override |
| protected int getThickness() { |
| if (UISettings.getInstance().PRESENTATION_MODE) return super.getThickness(); |
| return super.getThickness() + 7; |
| } |
| |
| @Override |
| protected void paintTrack(Graphics g, JComponent c, Rectangle bounds) { |
| if (UISettings.getInstance().PRESENTATION_MODE) { |
| g.setColor(getEditor().getColorsScheme().getDefaultBackground()); |
| g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); |
| return; |
| } |
| Rectangle clip = g.getClipBounds().intersection(bounds); |
| if (clip.height == 0) return; |
| |
| Rectangle componentBounds = c.getBounds(); |
| ProperTextRange docRange = ProperTextRange.create(0, (int)componentBounds.getHeight()); |
| if (myCachedTrack == null || myCachedTrack.getHeight() != componentBounds.getHeight()) { |
| myCachedTrack = UIUtil.createImage(componentBounds.width, componentBounds.height, BufferedImage.TYPE_INT_ARGB); |
| myDirtyYPositions = docRange; |
| paintTrackBasement(myCachedTrack.getGraphics(), new Rectangle(0, 0, componentBounds.width, componentBounds.height)); |
| } |
| if (myDirtyYPositions == WHOLE_DOCUMENT) { |
| myDirtyYPositions = docRange; |
| } |
| if (myDirtyYPositions != null) { |
| final Graphics2D imageGraphics = myCachedTrack.createGraphics(); |
| |
| ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart(); |
| |
| try { |
| myDirtyYPositions = myDirtyYPositions.intersection(docRange); |
| if (myDirtyYPositions == null) myDirtyYPositions = docRange; |
| repaint(imageGraphics, componentBounds.width, ERROR_ICON_WIDTH - 1, myDirtyYPositions); |
| myDirtyYPositions = null; |
| } |
| finally { |
| ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintFinish(); |
| } |
| } |
| |
| UIUtil.drawImage(g, myCachedTrack, null, 0, 0); |
| } |
| |
| private void paintTrackBasement(Graphics g, Rectangle bounds) { |
| if (UISettings.getInstance().PRESENTATION_MODE) { |
| return; |
| } |
| |
| g.setColor(ButtonlessScrollBarUI.getTrackBackground()); |
| g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height + 1); |
| |
| g.setColor(ButtonlessScrollBarUI.getTrackBorderColor()); |
| int border = isMirrored() ? bounds.x + bounds.width - 1 : bounds.x; |
| g.drawLine(border, bounds.y, border, bounds.y + bounds.height + 1); |
| } |
| |
| @Override |
| protected Color adjustColor(Color c) { |
| if (UIUtil.isUnderDarcula()) { |
| return c; |
| } |
| return ColorUtil.withAlpha(ColorUtil.shift(super.adjustColor(c), 0.9), 0.85); |
| } |
| |
| private void repaint(final Graphics g, int gutterWidth, final int stripeWidth, ProperTextRange yrange) { |
| final Rectangle clip = new Rectangle(0, yrange.getStartOffset(), gutterWidth, yrange.getLength() + myMinMarkHeight); |
| paintTrackBasement(g, clip); |
| |
| Document document = myEditor.getDocument(); |
| int startOffset = yPositionToOffset(clip.y - myMinMarkHeight, true); |
| int endOffset = yPositionToOffset(clip.y + clip.height, false); |
| |
| drawMarkup(g, stripeWidth, startOffset, endOffset, EditorMarkupModelImpl.this); |
| drawMarkup(g, stripeWidth, startOffset, endOffset, |
| (MarkupModelEx)DocumentMarkupModel.forDocument(document, myEditor.getProject(), true)); |
| } |
| |
| private void drawMarkup(final Graphics g, final int width, int startOffset, int endOffset, MarkupModelEx markup) { |
| final Queue<PositionedStripe> thinEnds = new PriorityQueue<PositionedStripe>(5, new Comparator<PositionedStripe>() { |
| @Override |
| public int compare(PositionedStripe o1, PositionedStripe o2) { |
| return o1.yEnd - o2.yEnd; |
| } |
| }); |
| final Queue<PositionedStripe> wideEnds = new PriorityQueue<PositionedStripe>(5, new Comparator<PositionedStripe>() { |
| @Override |
| public int compare(PositionedStripe o1, PositionedStripe o2) { |
| return o1.yEnd - o2.yEnd; |
| } |
| }); |
| // sorted by layer |
| final List<PositionedStripe> thinStripes = new ArrayList<PositionedStripe>(); |
| final List<PositionedStripe> wideStripes = new ArrayList<PositionedStripe>(); |
| final int[] thinYStart = new int[1]; // in range 0..yStart all spots are drawn |
| final int[] wideYStart = new int[1]; // in range 0..yStart all spots are drawn |
| |
| markup.processRangeHighlightersOverlappingWith(startOffset, endOffset, new Processor<RangeHighlighterEx>() { |
| @Override |
| public boolean process(RangeHighlighterEx highlighter) { |
| Color color = highlighter.getErrorStripeMarkColor(); |
| if (color == null) return true; |
| boolean isThin = highlighter.isThinErrorStripeMark(); |
| int[] yStart = isThin ? thinYStart : wideYStart; |
| List<PositionedStripe> stripes = isThin ? thinStripes : wideStripes; |
| Queue<PositionedStripe> ends = isThin ? thinEnds : wideEnds; |
| |
| ProperTextRange range = offsetsToYPositions(highlighter.getStartOffset(), highlighter.getEndOffset()); |
| final int ys = range.getStartOffset(); |
| int ye = range.getEndOffset(); |
| if (ye - ys < myMinMarkHeight) ye = ys + myMinMarkHeight; |
| |
| yStart[0] = drawStripesEndingBefore(ys, ends, stripes, g, width, yStart[0]); |
| |
| final int layer = highlighter.getLayer(); |
| |
| PositionedStripe stripe = null; |
| int i; |
| for (i = 0; i < stripes.size(); i++) { |
| PositionedStripe s = stripes.get(i); |
| if (s.layer == layer) { |
| stripe = s; |
| break; |
| } |
| if (s.layer < layer) { |
| break; |
| } |
| } |
| if (stripe == null) { |
| // started new stripe, draw previous above |
| if (yStart[0] != ys) { |
| if (!stripes.isEmpty()) { |
| PositionedStripe top = stripes.get(0); |
| drawSpot(g, width, top.thin, yStart[0], ys, top.color, true, true); |
| } |
| yStart[0] = ys; |
| } |
| stripe = new PositionedStripe(color, ye, isThin, layer); |
| stripes.add(i, stripe); |
| ends.offer(stripe); |
| } |
| else { |
| // key changed, reinsert into queue |
| if (stripe.yEnd != ye) { |
| ends.remove(stripe); |
| stripe.yEnd = ye; |
| ends.offer(stripe); |
| } |
| } |
| |
| return true; |
| } |
| }); |
| |
| drawStripesEndingBefore(Integer.MAX_VALUE, thinEnds, thinStripes, g, width, thinYStart[0]); |
| drawStripesEndingBefore(Integer.MAX_VALUE, wideEnds, wideStripes, g, width, wideYStart[0]); |
| } |
| |
| private int drawStripesEndingBefore(int ys, |
| Queue<PositionedStripe> ends, |
| List<PositionedStripe> stripes, |
| Graphics g, int width, int yStart) { |
| while (!ends.isEmpty()) { |
| PositionedStripe endingStripe = ends.peek(); |
| if (endingStripe.yEnd > ys) break; |
| ends.remove(); |
| |
| // check whether endingStripe got obscured in the range yStart..endingStripe.yEnd |
| int i = stripes.indexOf(endingStripe); |
| stripes.remove(i); |
| if (i == 0) { |
| // visible |
| drawSpot(g, width, endingStripe.thin, yStart, endingStripe.yEnd, endingStripe.color, true, true); |
| yStart = endingStripe.yEnd; |
| } |
| } |
| return yStart; |
| } |
| |
| private void drawSpot(Graphics g, |
| int width, |
| boolean thinErrorStripeMark, |
| int yStart, |
| int yEnd, |
| Color color, |
| boolean drawTopDecoration, |
| boolean drawBottomDecoration) { |
| int x = isMirrored() ? 3 : 5; |
| int paintWidth = width; |
| if (thinErrorStripeMark) { |
| paintWidth /= 2; |
| paintWidth += 1; |
| x = isMirrored() ? width + 2 : 0; |
| } |
| if (color == null) return; |
| g.setColor(color); |
| g.fillRect(x + 1, yStart, paintWidth - 2, yEnd - yStart + 1); |
| |
| Color brighter = color.brighter(); |
| g.setColor(brighter); |
| //left decoration |
| UIUtil.drawLine(g, x, yStart, x, yEnd/* - 1*/); |
| if (drawTopDecoration) { |
| //top decoration |
| UIUtil.drawLine(g, x + 1, yStart, x + paintWidth - 2, yStart); |
| } |
| Color darker = ColorUtil.shift(color, 0.75); |
| |
| g.setColor(darker); |
| if (drawBottomDecoration) { |
| // bottom decoration |
| UIUtil.drawLine(g, x + 1, yEnd/* - 1*/, x + paintWidth - 2, yEnd/* - 1*/); // large bottom to let overwrite by hl below |
| } |
| //right decoration |
| UIUtil.drawLine(g, x + paintWidth - 2, yStart, x + paintWidth - 2, yEnd/* - 1*/); |
| } |
| |
| // mouse events |
| @Override |
| public void mouseClicked(final MouseEvent e) { |
| CommandProcessor.getInstance().executeCommand(myEditor.getProject(), new Runnable() { |
| @Override |
| public void run() { |
| doMouseClicked(e); |
| } |
| }, |
| EditorBundle.message("move.caret.command.name"), |
| DocCommandGroupId.noneGroupId(getDocument()), UndoConfirmationPolicy.DEFAULT, |
| getDocument() |
| ); |
| } |
| |
| @Override |
| public void mousePressed(MouseEvent e) { |
| } |
| |
| @Override |
| public void mouseReleased(MouseEvent e) { |
| } |
| |
| private int getWidth() { |
| return scrollbar.getWidth(); |
| } |
| |
| private void doMouseClicked(MouseEvent e) { |
| myEditor.getContentComponent().requestFocus(); |
| int lineCount = getDocument().getLineCount() + myEditor.getSettings().getAdditionalLinesCount(); |
| if (lineCount == 0) { |
| return; |
| } |
| if (e.getX() > 0 && e.getX() <= getWidth()) { |
| doClick(e); |
| } |
| } |
| |
| @Override |
| public void mouseMoved(MouseEvent e) { |
| EditorImpl.MyScrollBar scrollBar = myEditor.getVerticalScrollBar(); |
| int buttonHeight = scrollBar.getDecScrollButtonHeight(); |
| int lineCount = getDocument().getLineCount() + myEditor.getSettings().getAdditionalLinesCount(); |
| if (lineCount == 0) { |
| return; |
| } |
| |
| if (e.getY() < buttonHeight && myErrorStripeRenderer != null) { |
| showTrafficLightTooltip(e); |
| return; |
| } |
| |
| if (e.getX() > 0 && e.getX() <= getWidth() && showToolTipByMouseMove(e)) { |
| scrollbar.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); |
| return; |
| } |
| |
| cancelMyToolTips(e, false); |
| |
| if (scrollbar.getCursor().equals(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR))) { |
| scrollbar.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); |
| } |
| } |
| |
| private TrafficTooltipRenderer myTrafficTooltipRenderer; |
| |
| private void showTrafficLightTooltip(MouseEvent e) { |
| if (myTrafficTooltipRenderer == null) { |
| myTrafficTooltipRenderer = myTooltipRendererProvider.createTrafficTooltipRenderer(new Runnable() { |
| @Override |
| public void run() { |
| myTrafficTooltipRenderer = null; |
| } |
| }, myEditor); |
| } |
| showTooltip(e, myTrafficTooltipRenderer, new HintHint(e).setAwtTooltip(true).setMayCenterPosition(true).setContentActive(false) |
| .setPreferredPosition(Balloon.Position.atLeft)); |
| } |
| |
| private void repaintTrafficTooltip() { |
| if (myTrafficTooltipRenderer != null) { |
| myTrafficTooltipRenderer.repaintTooltipWindow(); |
| } |
| } |
| |
| private void cancelMyToolTips(final MouseEvent e, boolean checkIfShouldSurvive) { |
| if (myEditorPreviewHint != null) { |
| myEditorPreviewHint.hide(); |
| myEditorPreviewHint = null; |
| } |
| final TooltipController tooltipController = TooltipController.getInstance(); |
| if (!checkIfShouldSurvive || !tooltipController.shouldSurvive(e)) { |
| tooltipController.cancelTooltip(ERROR_STRIPE_TOOLTIP_GROUP, e, true); |
| } |
| } |
| |
| @Override |
| public void mouseEntered(MouseEvent e) { |
| } |
| |
| @Override |
| public void mouseExited(MouseEvent e) { |
| cancelMyToolTips(e, true); |
| } |
| |
| @Override |
| public void mouseDragged(MouseEvent e) { |
| cancelMyToolTips(e, true); |
| } |
| |
| private void setPopupHandler(@NotNull PopupHandler handler) { |
| if (myHandler != null) { |
| scrollbar.removeMouseListener(myHandler); |
| myErrorStripeButton.removeMouseListener(myHandler); |
| } |
| |
| myHandler = handler; |
| scrollbar.addMouseListener(handler); |
| myErrorStripeButton.addMouseListener(myHandler); |
| } |
| } |
| |
| private void showTooltip(MouseEvent e, final TooltipRenderer tooltipObject, @NotNull HintHint hintHint) { |
| TooltipController tooltipController = TooltipController.getInstance(); |
| tooltipController.showTooltipByMouseMove(myEditor, new RelativePoint(e), tooltipObject, |
| myEditor.getVerticalScrollbarOrientation() == EditorEx.VERTICAL_SCROLLBAR_RIGHT, |
| ERROR_STRIPE_TOOLTIP_GROUP, hintHint); |
| } |
| |
| private void fireErrorMarkerClicked(RangeHighlighter marker, MouseEvent e) { |
| ApplicationManager.getApplication().assertIsDispatchThread(); |
| ErrorStripeEvent event = new ErrorStripeEvent(getEditor(), e, marker); |
| for (ErrorStripeListener listener : myErrorMarkerListeners) { |
| listener.errorMarkerClicked(event); |
| } |
| } |
| |
| @Override |
| public void addErrorMarkerListener(@NotNull final ErrorStripeListener listener, @NotNull Disposable parent) { |
| ContainerUtil.add(listener, myErrorMarkerListeners, parent); |
| } |
| |
| public void markDirtied(@NotNull ProperTextRange yPositions) { |
| int start = Math.max(0, yPositions.getStartOffset() - myEditor.getLineHeight()); |
| int end = myEditorScrollbarTop + myEditorTargetHeight == 0 ? yPositions.getEndOffset() + myEditor.getLineHeight() |
| : Math |
| .min(myEditorScrollbarTop + myEditorTargetHeight, yPositions.getEndOffset() + myEditor.getLineHeight()); |
| ProperTextRange adj = new ProperTextRange(start, Math.max(end, start)); |
| |
| myDirtyYPositions = myDirtyYPositions == null ? adj : myDirtyYPositions.union(adj); |
| |
| myEditorScrollbarTop = 0; |
| myEditorSourceHeight = 0; |
| myEditorTargetHeight = 0; |
| dimensionsAreValid = false; |
| } |
| |
| @Override |
| public void setMinMarkHeight(final int minMarkHeight) { |
| myMinMarkHeight = minMarkHeight; |
| } |
| |
| @Override |
| public boolean isErrorStripeVisible() { |
| return getErrorPanel() != null; |
| } |
| |
| private static class BasicTooltipRendererProvider implements ErrorStripTooltipRendererProvider { |
| @Override |
| public TooltipRenderer calcTooltipRenderer(@NotNull final Collection<RangeHighlighter> highlighters) { |
| LineTooltipRenderer bigRenderer = null; |
| //do not show same tooltip twice |
| Set<String> tooltips = null; |
| |
| for (RangeHighlighter highlighter : highlighters) { |
| final Object tooltipObject = highlighter.getErrorStripeTooltip(); |
| if (tooltipObject == null) continue; |
| |
| final String text = tooltipObject.toString(); |
| if (tooltips == null) { |
| tooltips = new THashSet<String>(); |
| } |
| if (tooltips.add(text)) { |
| if (bigRenderer == null) { |
| bigRenderer = new LineTooltipRenderer(text, new Object[]{highlighters}); |
| } |
| else { |
| bigRenderer.addBelow(text); |
| } |
| } |
| } |
| |
| return bigRenderer; |
| } |
| |
| @NotNull |
| @Override |
| public TooltipRenderer calcTooltipRenderer(@NotNull final String text) { |
| return new LineTooltipRenderer(text, new Object[]{text}); |
| } |
| |
| @NotNull |
| @Override |
| public TooltipRenderer calcTooltipRenderer(@NotNull final String text, final int width) { |
| return new LineTooltipRenderer(text, width, new Object[]{text}); |
| } |
| |
| @NotNull |
| @Override |
| public TrafficTooltipRenderer createTrafficTooltipRenderer(@NotNull final Runnable onHide, @NotNull Editor editor) { |
| return new TrafficTooltipRenderer() { |
| @Override |
| public void repaintTooltipWindow() { |
| } |
| |
| @Override |
| public LightweightHint show(@NotNull Editor editor, |
| @NotNull Point p, |
| boolean alignToRight, |
| @NotNull TooltipGroup group, |
| @NotNull HintHint hintHint) { |
| JLabel label = new JLabel("WTF"); |
| return new LightweightHint(label) { |
| @Override |
| public void hide() { |
| super.hide(); |
| onHide.run(); |
| } |
| }; |
| } |
| }; |
| } |
| } |
| |
| @NotNull |
| private ProperTextRange offsetsToYPositions(int start, int end) { |
| if (!dimensionsAreValid) { |
| recalcEditorDimensions(); |
| } |
| Document document = myEditor.getDocument(); |
| int startLineNumber = end == -1 ? 0 : offsetToLine(start, document); |
| int startY; |
| int lineCount; |
| if (myEditorSourceHeight < myEditorTargetHeight) { |
| lineCount = 0; |
| startY = myEditorScrollbarTop + startLineNumber * myEditor.getLineHeight(); |
| } |
| else { |
| lineCount = myEditorSourceHeight / myEditor.getLineHeight(); |
| startY = myEditorScrollbarTop + (int)((float)startLineNumber / lineCount * myEditorTargetHeight); |
| } |
| |
| int endY; |
| int endLineNumber = offsetToLine(end, document); |
| if (end == -1 || start == -1) { |
| endY = Math.min(myEditorSourceHeight, myEditorTargetHeight); |
| } |
| else if (start == end || offsetToLine(start, document) == endLineNumber) { |
| endY = startY; // both offsets are on the same line, no need to recalc Y position |
| } |
| else { |
| if (myEditorSourceHeight < myEditorTargetHeight) { |
| endY = myEditorScrollbarTop + endLineNumber * myEditor.getLineHeight(); |
| } |
| else { |
| endY = myEditorScrollbarTop + (int)((float)endLineNumber / lineCount * myEditorTargetHeight); |
| } |
| } |
| if (endY < startY) endY = startY; |
| return new ProperTextRange(startY, endY); |
| } |
| |
| private int yPositionToOffset(int y, boolean beginLine) { |
| if (!dimensionsAreValid) { |
| recalcEditorDimensions(); |
| } |
| final int safeY = Math.max(0, y - myEditorScrollbarTop); |
| VisualPosition visual; |
| if (myEditorSourceHeight < myEditorTargetHeight) { |
| visual = myEditor.xyToVisualPosition(new Point(0, safeY)); |
| } |
| else { |
| float fraction = Math.max(0, Math.min(1, safeY / (float)myEditorTargetHeight)); |
| final int lineCount = myEditorSourceHeight / myEditor.getLineHeight(); |
| visual = new VisualPosition((int)(fraction * lineCount), 0); |
| } |
| int line = myEditor.visualToLogicalPosition(visual).line; |
| Document document = myEditor.getDocument(); |
| if (line < 0) return 0; |
| if (line >= document.getLineCount()) return document.getTextLength(); |
| |
| final FoldingModelEx foldingModel = myEditor.getFoldingModel(); |
| if (beginLine) { |
| final int offset = document.getLineStartOffset(line); |
| final FoldRegion startCollapsed = foldingModel.getCollapsedRegionAtOffset(offset); |
| return startCollapsed != null ? Math.min(offset, startCollapsed.getStartOffset()) : offset; |
| } |
| else { |
| final int offset = document.getLineEndOffset(line); |
| final FoldRegion startCollapsed = foldingModel.getCollapsedRegionAtOffset(offset); |
| return startCollapsed != null ? Math.max(offset, startCollapsed.getEndOffset()) : offset; |
| } |
| } |
| private class EditorFragmentRenderer implements TooltipRenderer { |
| private int myLine; |
| private Collection<RangeHighlighterEx> myHighlighters; |
| private BufferedImage myImage; |
| private int myStartLine; |
| private int myEndLine; |
| private int myRelativeY; |
| |
| private EditorFragmentRenderer() { |
| update(0, Collections.<RangeHighlighterEx>emptyList()); |
| } |
| |
| public void update(int currentLine, Collection<RangeHighlighterEx> rangeHighlighters) { |
| myLine = currentLine; |
| myHighlighters = rangeHighlighters; |
| myImage = null; |
| myStartLine = Math.max(0, myLine - myPreviewLines); |
| myEndLine = Math.min(myEditor.getDocument().getLineCount() - 1, myLine + myPreviewLines + 1); |
| } |
| |
| @Override |
| public LightweightHint show(@NotNull final Editor editor, |
| @NotNull Point p, |
| boolean alignToRight, |
| @NotNull TooltipGroup group, |
| @NotNull HintHint hintInfo) { |
| final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl(); |
| if (myEditorPreviewHint == null) { |
| final JPanel editorFragmentPreviewPanel = new JPanel() { |
| private static final int R = 6; |
| private static final int LEFT_INDENT = BalloonImpl.ARC + 5; |
| |
| @Override |
| public Dimension getPreferredSize() { |
| int width = myEditor.getGutterComponentEx().getWidth(); |
| width += Math.min(myEditor.getScrollingModel().getVisibleArea().width, myEditor.getContentComponent().getWidth()); |
| return new Dimension(width - BalloonImpl.POINTER_WIDTH - LEFT_INDENT, myEditor.getLineHeight() * (myEndLine - myStartLine)); |
| } |
| |
| @Override |
| protected void paintComponent(Graphics g) { |
| if (myImage == null) { |
| myRelativeY = SwingUtilities.convertPoint(this, 0, 0, myEditor.getScrollPane()).y; |
| Dimension size = getPreferredSize(); |
| myImage = UIUtil.createImage(size.width, size.height, BufferedImage.TYPE_INT_RGB); |
| Graphics2D g2d = myImage.createGraphics(); |
| AffineTransform transform; |
| if (UIUtil.isRetina()) { |
| transform = AffineTransform.getScaleInstance(2, 2); |
| } else { |
| transform = AffineTransform.getScaleInstance(1, 1); |
| } |
| UISettings.setupAntialiasing(g2d); |
| g2d.setColor(myEditor.getBackgroundColor()); |
| g2d.fillRect(0, 0, getWidth(), getHeight()); |
| int lineShift = -myEditor.getLineHeight() * myStartLine;//first fragment line offset |
| int popupStartOffset = myEditor.getDocument().getLineStartOffset(myStartLine); |
| int popupEndOffset = myEditor.getDocument().getLineEndOffset(myEndLine); |
| List<RangeHighlighterEx> exs = new ArrayList<RangeHighlighterEx>(); |
| for (RangeHighlighterEx rangeHighlighter : myHighlighters) { |
| if (rangeHighlighter.getEndOffset()> popupStartOffset && rangeHighlighter.getStartOffset() < popupEndOffset) { |
| exs.add(rangeHighlighter); |
| } |
| } |
| AffineTransform translateInstance = AffineTransform.getTranslateInstance(-LEFT_INDENT, lineShift); |
| translateInstance.preConcatenate(transform); |
| g2d.setTransform(translateInstance); |
| EditorGutterComponentEx gutterComponentEx = myEditor.getGutterComponentEx(); |
| int width = gutterComponentEx.getWidth(); |
| g2d.setClip(0, 0, width, gutterComponentEx.getHeight()); |
| gutterComponentEx.paint(g2d); |
| JComponent contentComponent = myEditor.getContentComponent(); |
| g2d.setClip(width, 0, contentComponent.getWidth(), contentComponent.getHeight()); |
| translateInstance = AffineTransform.getTranslateInstance(width - LEFT_INDENT, lineShift); |
| translateInstance.preConcatenate(transform); |
| g2d.setTransform(translateInstance); |
| contentComponent.paint(g2d); |
| Collections.sort(exs, new Comparator<RangeHighlighterEx>() { |
| public int compare(RangeHighlighterEx ex1, RangeHighlighterEx ex2) { |
| LogicalPosition startPos1 = myEditor.offsetToLogicalPosition(ex1.getAffectedAreaStartOffset()); |
| LogicalPosition startPos2 = myEditor.offsetToLogicalPosition(ex2.getAffectedAreaStartOffset()); |
| if (startPos1.line != startPos2.line) return 0; |
| return startPos1.column - startPos2.column; |
| } |
| }); |
| TIntIntHashMap rightEdges = new TIntIntHashMap(); |
| for (RangeHighlighterEx ex : exs) { |
| //int hStartOffset = ex.getAffectedAreaStartOffset(); |
| int hEndOffset = ex.getAffectedAreaEndOffset(); |
| Object tooltip = ex.getErrorStripeTooltip(); |
| if (tooltip == null) continue; |
| String s = String.valueOf(tooltip); |
| if (s.isEmpty()) continue; |
| |
| LogicalPosition logicalPosition = myEditor.offsetToLogicalPosition(hEndOffset); |
| int endOfLineOffset = myEditor.getDocument().getLineEndOffset(logicalPosition.line); |
| logicalPosition = myEditor.offsetToLogicalPosition(endOfLineOffset); |
| Point placeToShow = myEditor.logicalPositionToXY(logicalPosition); |
| logicalPosition = myEditor.xyToLogicalPosition(placeToShow);//wraps&foldings workaround |
| placeToShow.x += R * 3 / 2; |
| placeToShow.y += myEditor.getLineHeight() - R/2; |
| |
| int w = g2d.getFontMetrics().stringWidth(s); |
| int a = g2d.getFontMetrics().getAscent(); |
| int h = myEditor.getLineHeight(); |
| |
| int rightEdge = rightEdges.get(logicalPosition.line); |
| placeToShow.x = Math.max(placeToShow.x, rightEdge); |
| rightEdge = Math.max(rightEdge, placeToShow.x + w + 3 * R); |
| rightEdges.put(logicalPosition.line, rightEdge); |
| |
| GraphicsUtil.setupAAPainting(g2d); |
| g2d.setColor(MessageType.WARNING.getPopupBackground()); |
| g2d.fillRoundRect(placeToShow.x - R, placeToShow.y - a, w + 2 * R, h, R, R); |
| g2d.setColor(new JBColor(JBColor.GRAY, Gray._200)); |
| g2d.drawRoundRect(placeToShow.x - R, placeToShow.y - a, w + 2 * R, h, R, R); |
| g2d.setColor(JBColor.foreground()); |
| g2d.drawString(s, placeToShow.x, placeToShow.y); |
| } |
| |
| } |
| UIUtil.drawImage(g, myImage, 0, 0, this); |
| } |
| }; |
| myEditorPreviewHint = new LightweightHint(editorFragmentPreviewPanel); |
| } |
| Point point = hintInfo.getOriginalPoint(); |
| hintInfo.setTextBg(myEditor.getColorsScheme().getDefaultBackground()); |
| hintInfo.setBorderColor(new JBColor(Gray._0, Gray._111)); |
| point = SwingUtilities.convertPoint(((EditorImpl)editor).getVerticalScrollBar(), point, myEditor.getComponent().getRootPane()); |
| hintManager.showEditorHint(myEditorPreviewHint, myEditor, point, HintManager.HIDE_BY_ANY_KEY | |
| HintManager.HIDE_BY_TEXT_CHANGE | |
| HintManager.HIDE_BY_MOUSEOVER | |
| HintManager.HIDE_BY_ESCAPE | |
| HintManager.HIDE_BY_SCROLLING, 0, false, hintInfo); |
| return myEditorPreviewHint; |
| } |
| } |
| } |