2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Created by IntelliJ IDEA.
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
25 package com
.intellij
.openapi
.editor
.impl
;
27 import com
.intellij
.codeInsight
.hint
.LineTooltipRenderer
;
28 import com
.intellij
.codeInsight
.hint
.TooltipController
;
29 import com
.intellij
.codeInsight
.hint
.TooltipGroup
;
30 import com
.intellij
.codeInsight
.hint
.TooltipRenderer
;
31 import com
.intellij
.ide
.ui
.LafManager
;
32 import com
.intellij
.openapi
.application
.ApplicationManager
;
33 import com
.intellij
.openapi
.application
.ex
.ApplicationManagerEx
;
34 import com
.intellij
.openapi
.application
.impl
.ApplicationImpl
;
35 import com
.intellij
.openapi
.command
.CommandProcessor
;
36 import com
.intellij
.openapi
.command
.UndoConfirmationPolicy
;
37 import com
.intellij
.openapi
.diagnostic
.Logger
;
38 import com
.intellij
.openapi
.editor
.*;
39 import com
.intellij
.openapi
.editor
.actionSystem
.DocCommandGroupId
;
40 import com
.intellij
.openapi
.editor
.ex
.*;
41 import com
.intellij
.openapi
.editor
.markup
.ErrorStripeRenderer
;
42 import com
.intellij
.openapi
.editor
.markup
.MarkupModel
;
43 import com
.intellij
.openapi
.editor
.markup
.RangeHighlighter
;
44 import com
.intellij
.openapi
.util
.IconLoader
;
45 import com
.intellij
.ui
.PopupHandler
;
46 import com
.intellij
.util
.SmartList
;
47 import com
.intellij
.util
.containers
.ContainerUtil
;
48 import com
.intellij
.util
.ui
.UIUtil
;
49 import gnu
.trove
.THashSet
;
50 import org
.jetbrains
.annotations
.NotNull
;
54 import java
.awt
.event
.MouseEvent
;
55 import java
.awt
.event
.MouseListener
;
56 import java
.awt
.event
.MouseMotionListener
;
58 import java
.util
.List
;
59 import java
.util
.Queue
;
61 public class EditorMarkupModelImpl
extends MarkupModelImpl
implements EditorMarkupModel
{
62 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.editor.impl.EditorMarkupModelImpl");
64 private static final TooltipGroup ERROR_STRIPE_TOOLTIP_GROUP
= new TooltipGroup("ERROR_STRIPE_TOOLTIP_GROUP", 0);
65 private static final Icon ERRORS_FOUND_ICON
= IconLoader
.getIcon("/general/errorsFound.png");
67 private final EditorImpl myEditor
;
68 private MyErrorPanel myErrorPanel
;
69 private ErrorStripeRenderer myErrorStripeRenderer
= null;
70 private final List
<ErrorStripeListener
> myErrorMarkerListeners
= new ArrayList
<ErrorStripeListener
>();
71 private ErrorStripeListener
[] myCachedErrorMarkerListeners
= null;
72 private List
<RangeHighlighter
> myCachedSortedHighlighters
= null;
73 private final MarkSpots myMarkSpots
= new MarkSpots();
74 private int myScrollBarHeight
;
75 private static final Comparator
<RangeHighlighter
> LAYER_COMPARATOR
= new Comparator
<RangeHighlighter
>() {
76 public int compare(final RangeHighlighter o1
, final RangeHighlighter o2
) {
77 return o1
.getLayer() - o2
.getLayer();
81 @NotNull private ErrorStripTooltipRendererProvider myTooltipRendererProvider
= new BasicTooltipRendererProvider();
83 private static int myMinMarkHeight
= 3;
85 EditorMarkupModelImpl(@NotNull EditorImpl editor
) {
86 super((DocumentImpl
)editor
.getDocument());
91 protected void assertDispatchThread() {
92 ApplicationManagerEx
.getApplicationEx().assertIsDispatchThread(myEditor
.getComponent());
95 private int offsetToLine(int offset
) {
96 final Document document
= myEditor
.getDocument();
97 if (offset
> document
.getTextLength()) {
98 offset
= document
.getTextLength();
100 final int lineNumber
= document
.getLineNumber(offset
);
101 return myEditor
.logicalToVisualPosition(new LogicalPosition(lineNumber
, 0)).line
;
104 private static class MarkSpot
{
106 private final int yStart
;
110 // sorted by layers from bottom to top
111 private RangeHighlighter
[] highlighters
= RangeHighlighter
.EMPTY_ARRAY
;
113 private MarkSpot(final int yStart
, final int yEnd
) {
114 this.yStart
= yStart
;
118 private boolean near(MouseEvent e
, double width
) {
119 final int x
= e
.getX();
120 final int y
= e
.getY();
121 return 0 <= x
&& x
< width
&& yStart
- getMinHeight() <= y
&& y
< yEnd
+ getMinHeight();
126 public static int getMinHeight() {
127 return myMinMarkHeight
;
130 private class MarkSpots
{
132 private List
<MarkSpot
> mySpots
;
134 private int myEditorScrollbarTop
= -1;
136 private int myEditorTargetHeight
= -1;
138 private int myEditorSourceHeight
= -1;
140 private void recalcEditorDimensions() {
141 EditorImpl
.MyScrollBar scrollBar
= myEditor
.getVerticalScrollBar();
142 myEditorScrollbarTop
= scrollBar
.getDecScrollButtonHeight() + 1;
143 int bottom
= scrollBar
.getIncScrollButtonHeight();
144 myEditorTargetHeight
= myScrollBarHeight
- myEditorScrollbarTop
- bottom
;
145 myEditorSourceHeight
= myEditor
.getPreferredSize().height
;
148 private void clear() {
150 myEditorScrollbarTop
= -1;
151 myEditorSourceHeight
= -1;
152 myEditorTargetHeight
= -1;
155 public boolean showToolTipByMouseMove(final MouseEvent e
, final double width
) {
157 final List
<MarkSpot
> nearestMarkSpots
= getNearestMarkSpots(e
, width
);
158 if (nearestMarkSpots
.isEmpty()) return false;
159 Set
<RangeHighlighter
> highlighters
= new THashSet
<RangeHighlighter
>(nearestMarkSpots
.size() + 4);
160 for (MarkSpot markSpot
: nearestMarkSpots
) {
161 highlighters
.addAll(Arrays
.asList(markSpot
.highlighters
));
163 TooltipRenderer bigRenderer
= myTooltipRendererProvider
.calcTooltipRenderer(highlighters
);
164 if (bigRenderer
!= null) {
165 showTooltip(e
, bigRenderer
);
171 private class PositionedRangeHighlighter
{
173 private final RangeHighlighter highlighter
;
175 private final int yStart
;
177 private final int yEnd
;
179 private PositionedRangeHighlighter(final RangeHighlighter highlighter
, final int yStart
, final int yEnd
) {
180 this.highlighter
= highlighter
;
181 this.yStart
= yStart
;
185 @SuppressWarnings({"HardCodedStringLiteral"})
186 public String
toString() {
187 return "PR[" + yStart
+ "-" + yEnd
+ ")";
192 private PositionedRangeHighlighter
getPositionedRangeHighlighter(RangeHighlighter mark
) {
193 int visStartLine
= offsetToLine(mark
.getStartOffset());
194 int visEndLine
= offsetToLine(mark
.getEndOffset());
195 int yStartPosition
= visibleLineToYPosition(visStartLine
);
196 int yEndPosition
= visibleLineToYPosition(visEndLine
);
197 if (yEndPosition
- yStartPosition
< getMinMarkHeight()) {
198 yEndPosition
= yStartPosition
+ getMinMarkHeight();
200 return new PositionedRangeHighlighter(mark
, yStartPosition
, yEndPosition
);
203 private int visibleLineToYPosition(int lineNumber
) {
204 if (myEditorScrollbarTop
== -1) {
205 recalcEditorDimensions();
207 if (myEditorSourceHeight
< myEditorTargetHeight
) {
208 return myEditorScrollbarTop
+ lineNumber
* myEditor
.getLineHeight();
211 final int lineCount
= myEditorSourceHeight
/ myEditor
.getLineHeight();
212 return myEditorScrollbarTop
+ (int)((float)lineNumber
/ lineCount
* myEditorTargetHeight
);
216 private void recalcMarkSpots() {
217 if (mySpots
!= null) return;
218 final List
<RangeHighlighter
> sortedHighlighters
= getSortedHighlighters();
219 mySpots
= new ArrayList
<MarkSpot
>();
220 if (sortedHighlighters
.isEmpty()) return;
221 Queue
<PositionedRangeHighlighter
> startQueue
=
222 new PriorityQueue
<PositionedRangeHighlighter
>(5, new Comparator
<PositionedRangeHighlighter
>() {
223 public int compare(final PositionedRangeHighlighter o1
, final PositionedRangeHighlighter o2
) {
224 return o1
.yStart
- o2
.yStart
;
227 Queue
<PositionedRangeHighlighter
> endQueue
=
228 new PriorityQueue
<PositionedRangeHighlighter
>(5, new Comparator
<PositionedRangeHighlighter
>() {
229 public int compare(final PositionedRangeHighlighter o1
, final PositionedRangeHighlighter o2
) {
230 return o1
.yEnd
- o2
.yEnd
;
234 MarkSpot currentSpot
= null;
235 while (!startQueue
.isEmpty() || !endQueue
.isEmpty() || index
!= sortedHighlighters
.size()) {
236 LOG
.assertTrue(startQueue
.size() == endQueue
.size());
238 final PositionedRangeHighlighter positionedMark
;
240 if (index
!= sortedHighlighters
.size()) {
241 RangeHighlighter mark
= sortedHighlighters
.get(index
);
242 if (!mark
.isValid() || mark
.getErrorStripeMarkColor() == null) {
243 sortedHighlighters
.remove(index
);
246 PositionedRangeHighlighter positioned
= getPositionedRangeHighlighter(mark
);
247 if (!endQueue
.isEmpty() && endQueue
.peek().yEnd
<= positioned
.yStart
) {
248 positionedMark
= endQueue
.peek();
252 positionedMark
= positioned
;
256 else if (!endQueue
.isEmpty()) {
257 positionedMark
= endQueue
.peek();
261 LOG
.error("cant be");
266 if (currentSpot
== null) {
267 currentSpot
= new MarkSpot(positionedMark
.yStart
, -1);
270 currentSpot
.yEnd
= positionedMark
.yStart
;
271 if (currentSpot
.yEnd
!= currentSpot
.yStart
) {
272 spitOutMarkSpot(currentSpot
, startQueue
);
274 currentSpot
= new MarkSpot(positionedMark
.yStart
, -1);
276 while (index
!= sortedHighlighters
.size()) {
277 PositionedRangeHighlighter positioned
= getPositionedRangeHighlighter(sortedHighlighters
.get(index
));
278 if (positioned
.yStart
!= positionedMark
.yStart
) break;
279 startQueue
.add(positioned
);
280 endQueue
.add(positioned
);
285 currentSpot
.yEnd
= positionedMark
.yEnd
;
286 spitOutMarkSpot(currentSpot
, startQueue
);
287 currentSpot
= new MarkSpot(positionedMark
.yEnd
, -1);
288 while (!endQueue
.isEmpty() && endQueue
.peek().yEnd
== positionedMark
.yEnd
) {
289 final PositionedRangeHighlighter highlighter
= endQueue
.remove();
290 for (Iterator
<PositionedRangeHighlighter
> iterator
= startQueue
.iterator(); iterator
.hasNext();) {
291 PositionedRangeHighlighter positioned
= iterator
.next();
292 if (positioned
== highlighter
) {
298 if (startQueue
.isEmpty()) {
305 private void spitOutMarkSpot(final MarkSpot currentSpot
, final Queue
<PositionedRangeHighlighter
> startQueue
) {
306 mySpots
.add(currentSpot
);
307 currentSpot
.highlighters
= new RangeHighlighter
[startQueue
.size()];
309 for (PositionedRangeHighlighter positioned
: startQueue
) {
310 currentSpot
.highlighters
[i
++] = positioned
.highlighter
;
312 Arrays
.sort(currentSpot
.highlighters
, LAYER_COMPARATOR
);
315 private void repaint(Graphics g
, final int width
) {
317 for (int i
= 0; i
< mySpots
.size(); i
++) {
318 MarkSpot markSpot
= mySpots
.get(i
);
320 int yStart
= markSpot
.yStart
;
321 RangeHighlighter mark
= markSpot
.highlighters
[markSpot
.highlighters
.length
- 1];
323 int yEnd
= markSpot
.yEnd
;
325 final Color color
= mark
.getErrorStripeMarkColor();
328 int paintWidth
= width
;
329 if (mark
.isThinErrorStripeMark()) {
335 g
.fillRect(x
+ 1, yStart
, paintWidth
- 2, yEnd
- yStart
);
337 Color brighter
= color
.brighter();
338 Color darker
= color
.darker();
340 g
.setColor(brighter
);
342 UIUtil
.drawLine(g
, x
, yStart
, x
, yEnd
- 1);
343 if (i
== 0 || !isAdjacent(mySpots
.get(i
- 1), markSpot
) || wider(markSpot
, mySpots
.get(i
- 1))) {
345 UIUtil
.drawLine(g
, x
+ 1, yStart
, x
+ paintWidth
- 2, yStart
);
348 if (i
== mySpots
.size() - 1 || !isAdjacent(markSpot
, mySpots
.get(i
+ 1)) || wider(markSpot
, mySpots
.get(i
+ 1))) {
350 UIUtil
.drawLine(g
, x
+ 1, yEnd
- 1, x
+ paintWidth
- 2, yEnd
- 1);
353 UIUtil
.drawLine(g
, x
+ paintWidth
- 2, yStart
, x
+ paintWidth
- 2, yEnd
- 1);
358 private boolean isAdjacent(MarkSpot markTop
, MarkSpot markBottom
) {
359 return markTop
.yEnd
>= markBottom
.yStart
;
362 private boolean wider(MarkSpot markTop
, MarkSpot markBottom
) {
363 final RangeHighlighter highlighterTop
= markTop
.highlighters
[markTop
.highlighters
.length
- 1];
364 final RangeHighlighter highlighterBottom
= markBottom
.highlighters
[markBottom
.highlighters
.length
- 1];
365 return !highlighterTop
.isThinErrorStripeMark() && highlighterBottom
.isThinErrorStripeMark();
368 public void doClick(final MouseEvent e
, final int width
) {
370 RangeHighlighter marker
= getNearestRangeHighlighter(e
, width
);
371 if (marker
== null) return;
372 int offset
= marker
.getStartOffset();
374 final Document doc
= myEditor
.getDocument();
375 if (doc
.getLineCount() > 0) {
376 // Necessary to expand folded block even if navigating just before one
377 // Very useful when navigating to first unused import statement.
378 int lineEnd
= doc
.getLineEndOffset(doc
.getLineNumber(offset
));
379 myEditor
.getCaretModel().moveToOffset(lineEnd
);
382 myEditor
.getCaretModel().moveToOffset(offset
);
383 myEditor
.getSelectionModel().removeSelection();
384 ScrollingModel scrollingModel
= myEditor
.getScrollingModel();
385 scrollingModel
.disableAnimation();
386 scrollingModel
.scrollToCaret(ScrollType
.CENTER
);
387 scrollingModel
.enableAnimation();
388 fireErrorMarkerClicked(marker
, e
);
391 private RangeHighlighter
getNearestRangeHighlighter(final MouseEvent e
, final int width
) {
392 List
<MarkSpot
> nearestSpots
= getNearestMarkSpots(e
, width
);
393 RangeHighlighter nearestMarker
= null;
395 for (MarkSpot markSpot
: nearestSpots
) {
396 for (RangeHighlighter highlighter
: markSpot
.highlighters
) {
397 final int newYPos
= visibleLineToYPosition(offsetToLine(highlighter
.getStartOffset()));
399 if (nearestMarker
== null || Math
.abs(yPos
- e
.getY()) > Math
.abs(newYPos
- e
.getY())) {
400 nearestMarker
= highlighter
;
405 return nearestMarker
;
408 private List
<MarkSpot
> getNearestMarkSpots(final MouseEvent e
, final double width
) {
409 List
<MarkSpot
> nearestSpot
= null;
410 for (MarkSpot markSpot
: mySpots
) {
411 if (markSpot
.near(e
, width
)) {
412 if (nearestSpot
== null) {
413 nearestSpot
= new SmartList
<MarkSpot
>();
415 nearestSpot
.add(markSpot
);
418 return nearestSpot
== null ? Collections
.<MarkSpot
>emptyList() : nearestSpot
;
423 public void setErrorStripeVisible(boolean val
) {
425 myErrorPanel
= new MyErrorPanel();
426 myEditor
.getPanel().add(myErrorPanel
,
427 myEditor
.getVerticalScrollbarOrientation() == EditorEx
.VERTICAL_SCROLLBAR_LEFT
429 : BorderLayout
.EAST
);
431 else if (myErrorPanel
!= null) {
432 myEditor
.getPanel().remove(myErrorPanel
);
437 public void setErrorPanelPopupHandler(@NotNull PopupHandler handler
) {
438 if (myErrorPanel
!= null) {
439 myErrorPanel
.setPopupHandler(handler
);
443 public void setErrorStripTooltipRendererProvider(@NotNull final ErrorStripTooltipRendererProvider provider
) {
444 myTooltipRendererProvider
= provider
;
448 public ErrorStripTooltipRendererProvider
getErrorStripTooltipRendererProvider() {
449 return myTooltipRendererProvider
;
453 public Editor
getEditor() {
457 public void setErrorStripeRenderer(ErrorStripeRenderer renderer
) {
458 assertIsDispatchThread();
459 myErrorStripeRenderer
= renderer
;
460 //try to not cancel tooltips here, since it is being called after every writeAction, even to the console
461 //HintManager.getInstance().getTooltipController().cancelTooltips();
462 if (myErrorPanel
!= null) {
463 myErrorPanel
.repaint();
467 private void assertIsDispatchThread() {
468 ApplicationManagerEx
.getApplicationEx().assertIsDispatchThread(myEditor
.getComponent());
471 public ErrorStripeRenderer
getErrorStripeRenderer() {
472 return myErrorStripeRenderer
;
475 public void dispose() {
476 myErrorStripeRenderer
= null;
480 public void repaint() {
482 EditorImpl
.MyScrollBar scrollBar
= myEditor
.getVerticalScrollBar();
483 myScrollBarHeight
= scrollBar
.getSize().height
;
485 if (myErrorPanel
!= null) {
486 myErrorPanel
.repaint();
490 private List
<RangeHighlighter
> getSortedHighlighters() {
491 if (myCachedSortedHighlighters
== null) {
492 myCachedSortedHighlighters
= new ArrayList
<RangeHighlighter
>();
494 for (RangeHighlighter highlighter
: getAllHighlighters()) {
495 if (highlighter
.getErrorStripeMarkColor() != null && highlighter
.isValid()) {
496 myCachedSortedHighlighters
.add(highlighter
);
500 final MarkupModel docMarkup
= getDocument().getMarkupModel(myEditor
.getProject());
501 for (RangeHighlighter highlighter
: docMarkup
.getAllHighlighters()) {
502 if (highlighter
.getErrorStripeMarkColor() != null && highlighter
.isValid()) {
503 myCachedSortedHighlighters
.add(highlighter
);
507 if (!myCachedSortedHighlighters
.isEmpty()) {
508 ContainerUtil
.quickSort(myCachedSortedHighlighters
, new Comparator
<RangeHighlighter
>() {
509 public int compare(final RangeHighlighter h1
, final RangeHighlighter h2
) {
510 return h1
.getStartOffset() - h2
.getStartOffset();
515 return myCachedSortedHighlighters
;
518 private class MyErrorPanel
extends JPanel
implements MouseMotionListener
, MouseListener
{
519 private PopupHandler myHandler
;
521 private MyErrorPanel() {
523 addMouseListener(this);
524 addMouseMotionListener(this);
527 public Dimension
getPreferredSize() {
528 return new Dimension(ERRORS_FOUND_ICON
.getIconWidth() + 2, 0);
531 protected void paintComponent(Graphics g
) {
532 if (getEditor().isDisposed()) return;
533 ((ApplicationImpl
)ApplicationManager
.getApplication()).editorPaintStart();
536 LafManager lafManager
= LafManager
.getInstance();
537 if (lafManager
== null || lafManager
.isUnderAquaLookAndFeel()) {
538 g
.setColor(new Color(0xF0F0F0));
539 Rectangle clipBounds
= g
.getClipBounds();
540 g
.fillRect(clipBounds
.x
, clipBounds
.y
, clipBounds
.width
, clipBounds
.height
);
542 super.paintComponent(g
);
545 if (myErrorStripeRenderer
!= null) {
546 EditorImpl
.MyScrollBar scrollBar
= myEditor
.getVerticalScrollBar();
547 int top
= scrollBar
.getDecScrollButtonHeight();
548 myErrorStripeRenderer
.paint(this, g
, new Rectangle(0, 0, getWidth(), top
));
551 myMarkSpots
.repaint(g
, ERRORS_FOUND_ICON
.getIconWidth());
554 ((ApplicationImpl
)ApplicationManager
.getApplication()).editorPaintFinish();
559 public void mouseClicked(final MouseEvent e
) {
560 CommandProcessor
.getInstance().executeCommand(myEditor
.getProject(), new Runnable() {
565 EditorBundle
.message("move.caret.command.name"), DocCommandGroupId
.noneGroupId(getDocument()), UndoConfirmationPolicy
.DEFAULT
, getDocument()
569 public void mousePressed(MouseEvent e
) {
572 public void mouseReleased(MouseEvent e
) {
575 private void doMouseClicked(MouseEvent e
) {
576 myEditor
.getContentComponent().requestFocus();
577 int lineCount
= getDocument().getLineCount() + myEditor
.getSettings().getAdditionalLinesCount();
578 if (lineCount
== 0) {
581 myMarkSpots
.doClick(e
, getWidth());
584 public void mouseMoved(MouseEvent e
) {
585 EditorImpl
.MyScrollBar scrollBar
= myEditor
.getVerticalScrollBar();
586 int buttonHeight
= scrollBar
.getDecScrollButtonHeight();
587 int lineCount
= getDocument().getLineCount() + myEditor
.getSettings().getAdditionalLinesCount();
588 if (lineCount
== 0) {
592 if (e
.getY() < buttonHeight
&& myErrorStripeRenderer
!= null) {
593 String tooltipMessage
= myErrorStripeRenderer
.getTooltipMessage();
594 if (tooltipMessage
!= null) {
595 showTooltip(e
, myTooltipRendererProvider
.calcTooltipRenderer(tooltipMessage
));
600 if (myMarkSpots
.showToolTipByMouseMove(e
,getWidth())) {
601 setCursor(Cursor
.getPredefinedCursor(Cursor
.HAND_CURSOR
));
607 if (getCursor().equals(Cursor
.getPredefinedCursor(Cursor
.HAND_CURSOR
))) {
608 setCursor(Cursor
.getPredefinedCursor(Cursor
.DEFAULT_CURSOR
));
612 private void cancelMyToolTips(final MouseEvent e
) {
613 final TooltipController tooltipController
= TooltipController
.getInstance();
614 if (!tooltipController
.shouldSurvive(e
)) {
615 tooltipController
.cancelTooltip(ERROR_STRIPE_TOOLTIP_GROUP
);
619 public void mouseEntered(MouseEvent e
) {
622 public void mouseExited(MouseEvent e
) {
626 public void mouseDragged(MouseEvent e
) {
630 public void setPopupHandler(final PopupHandler handler
) {
631 if (myHandler
!= null) {
632 removeMouseListener(myHandler
);
636 addMouseListener(handler
);
641 private void showTooltip(MouseEvent e
, final TooltipRenderer tooltipObject
) {
642 TooltipController tooltipController
= TooltipController
.getInstance();
643 tooltipController
.showTooltipByMouseMove(myEditor
, e
, tooltipObject
,
644 myEditor
.getVerticalScrollbarOrientation() == EditorEx
.VERTICAL_SCROLLBAR_RIGHT
,
645 ERROR_STRIPE_TOOLTIP_GROUP
);
648 private ErrorStripeListener
[] getCachedErrorMarkerListeners() {
649 if (myCachedErrorMarkerListeners
== null) {
650 myCachedErrorMarkerListeners
= myErrorMarkerListeners
.toArray(new ErrorStripeListener
[myErrorMarkerListeners
.size()]);
653 return myCachedErrorMarkerListeners
;
656 private void fireErrorMarkerClicked(RangeHighlighter marker
, MouseEvent e
) {
657 ErrorStripeEvent event
= new ErrorStripeEvent(getEditor(), e
, marker
);
658 ErrorStripeListener
[] listeners
= getCachedErrorMarkerListeners();
659 for (ErrorStripeListener listener
: listeners
) {
660 listener
.errorMarkerClicked(event
);
664 public void addErrorMarkerListener(@NotNull ErrorStripeListener listener
) {
665 myCachedErrorMarkerListeners
= null;
666 myErrorMarkerListeners
.add(listener
);
669 public void removeErrorMarkerListener(@NotNull ErrorStripeListener listener
) {
670 myCachedErrorMarkerListeners
= null;
671 boolean success
= myErrorMarkerListeners
.remove(listener
);
672 LOG
.assertTrue(success
);
675 public void markDirtied() {
676 myCachedSortedHighlighters
= null;
680 public int getMinMarkHeight() {
681 return myMinMarkHeight
;
684 public void setMinMarkHeight(final int minMarkHeight
) {
685 myMinMarkHeight
= minMarkHeight
;
688 private static class BasicTooltipRendererProvider
implements ErrorStripTooltipRendererProvider
{
689 public TooltipRenderer
calcTooltipRenderer(@NotNull final Collection
<RangeHighlighter
> highlighters
) {
690 LineTooltipRenderer bigRenderer
= null;
691 //do not show same tooltip twice
692 Set
<String
> tooltips
= null;
694 for (RangeHighlighter highlighter
: highlighters
) {
695 final Object tooltipObject
= highlighter
.getErrorStripeTooltip();
696 if (tooltipObject
== null) continue;
698 final String text
= tooltipObject
.toString();
699 if (tooltips
== null) {
700 tooltips
= new THashSet
<String
>();
702 if (tooltips
.add(text
)) {
703 if (bigRenderer
== null) {
704 bigRenderer
= new LineTooltipRenderer(text
);
707 bigRenderer
.addBelow(text
);
715 public TooltipRenderer
calcTooltipRenderer(@NotNull final String text
) {
716 return new LineTooltipRenderer(text
);
719 public TooltipRenderer
calcTooltipRenderer(@NotNull final String text
, final int width
) {
720 return new LineTooltipRenderer(text
, width
);