IDEA-51739
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / editor / impl / EditorMarkupModelImpl.java
blobac754787dc95e6e2426d16863a35bc0241ab998a
1 /*
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.
19 * User: max
20 * Date: Apr 19, 2002
21 * Time: 2:56:43 PM
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;
52 import javax.swing.*;
53 import java.awt.*;
54 import java.awt.event.MouseEvent;
55 import java.awt.event.MouseListener;
56 import java.awt.event.MouseMotionListener;
57 import java.util.*;
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());
87 myEditor = editor;
90 @Override
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;
108 private int yEnd;
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;
115 this.yEnd = yEnd;
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() {
149 mySpots = null;
150 myEditorScrollbarTop = -1;
151 myEditorSourceHeight = -1;
152 myEditorTargetHeight = -1;
155 public boolean showToolTipByMouseMove(final MouseEvent e, final double width) {
156 recalcMarkSpots();
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);
166 return true;
168 return false;
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;
182 this.yEnd = yEnd;
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();
210 else {
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;
233 int index = 0;
234 MarkSpot currentSpot = null;
235 while (!startQueue.isEmpty() || !endQueue.isEmpty() || index != sortedHighlighters.size()) {
236 LOG.assertTrue(startQueue.size() == endQueue.size());
238 final PositionedRangeHighlighter positionedMark;
239 boolean addingNew;
240 if (index != sortedHighlighters.size()) {
241 RangeHighlighter mark = sortedHighlighters.get(index);
242 if (!mark.isValid() || mark.getErrorStripeMarkColor() == null) {
243 sortedHighlighters.remove(index);
244 continue;
246 PositionedRangeHighlighter positioned = getPositionedRangeHighlighter(mark);
247 if (!endQueue.isEmpty() && endQueue.peek().yEnd <= positioned.yStart) {
248 positionedMark = endQueue.peek();
249 addingNew = false;
251 else {
252 positionedMark = positioned;
253 addingNew = true;
256 else if (!endQueue.isEmpty()) {
257 positionedMark = endQueue.peek();
258 addingNew = false;
260 else {
261 LOG.error("cant be");
262 return;
265 if (addingNew) {
266 if (currentSpot == null) {
267 currentSpot = new MarkSpot(positionedMark.yStart, -1);
269 else {
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);
281 index++;
284 else {
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) {
293 iterator.remove();
294 break;
298 if (startQueue.isEmpty()) {
299 currentSpot = null;
305 private void spitOutMarkSpot(final MarkSpot currentSpot, final Queue<PositionedRangeHighlighter> startQueue) {
306 mySpots.add(currentSpot);
307 currentSpot.highlighters = new RangeHighlighter[startQueue.size()];
308 int i =0;
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) {
316 recalcMarkSpots();
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();
327 int x = 1;
328 int paintWidth = width;
329 if (mark.isThinErrorStripeMark()) {
330 paintWidth /= 2;
331 x += paintWidth / 2;
334 g.setColor(color);
335 g.fillRect(x + 1, yStart, paintWidth - 2, yEnd - yStart);
337 Color brighter = color.brighter();
338 Color darker = color.darker();
340 g.setColor(brighter);
341 //left
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))) {
344 //top decoration
345 UIUtil.drawLine(g, x + 1, yStart, x + paintWidth - 2, yStart);
347 g.setColor(darker);
348 if (i == mySpots.size() - 1 || !isAdjacent(markSpot, mySpots.get(i + 1)) || wider(markSpot, mySpots.get(i + 1))) {
349 // bottom decoration
350 UIUtil.drawLine(g, x + 1, yEnd - 1, x + paintWidth - 2, yEnd - 1);
352 //right
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) {
369 recalcMarkSpots();
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;
394 int yPos = 0;
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;
401 yPos = newYPos;
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) {
424 if (val) {
425 myErrorPanel = new MyErrorPanel();
426 myEditor.getPanel().add(myErrorPanel,
427 myEditor.getVerticalScrollbarOrientation() == EditorEx.VERTICAL_SCROLLBAR_LEFT
428 ? BorderLayout.WEST
429 : BorderLayout.EAST);
431 else if (myErrorPanel != null) {
432 myEditor.getPanel().remove(myErrorPanel);
433 myErrorPanel = null;
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;
447 @NotNull
448 public ErrorStripTooltipRendererProvider getErrorStripTooltipRendererProvider() {
449 return myTooltipRendererProvider;
452 @NotNull
453 public Editor getEditor() {
454 return myEditor;
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;
477 super.dispose();
480 public void repaint() {
481 markDirtied();
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() {
522 setOpaque(true);
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();
535 try {
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);
541 } else {
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());
553 finally {
554 ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintFinish();
558 // mouse events
559 public void mouseClicked(final MouseEvent e) {
560 CommandProcessor.getInstance().executeCommand(myEditor.getProject(), new Runnable() {
561 public void run() {
562 doMouseClicked(e);
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) {
579 return;
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) {
589 return;
592 if (e.getY() < buttonHeight && myErrorStripeRenderer != null) {
593 String tooltipMessage = myErrorStripeRenderer.getTooltipMessage();
594 if (tooltipMessage != null) {
595 showTooltip(e, myTooltipRendererProvider.calcTooltipRenderer(tooltipMessage));
597 return;
600 if (myMarkSpots.showToolTipByMouseMove(e,getWidth())) {
601 setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
602 return;
605 cancelMyToolTips(e);
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) {
623 cancelMyToolTips(e);
626 public void mouseDragged(MouseEvent e) {
627 cancelMyToolTips(e);
630 public void setPopupHandler(final PopupHandler handler) {
631 if (myHandler != null) {
632 removeMouseListener(myHandler);
635 myHandler = handler;
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;
677 myMarkSpots.clear();
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);
706 else {
707 bigRenderer.addBelow(text);
712 return bigRenderer;
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);