2 * Copyright 2000-2014 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.
17 package com.intellij.application.options.colors;
19 import com.intellij.application.options.colors.highlighting.HighlightData;
20 import com.intellij.application.options.colors.highlighting.HighlightsExtractor;
21 import com.intellij.ide.highlighter.HighlighterFactory;
22 import com.intellij.openapi.editor.Editor;
23 import com.intellij.openapi.editor.EditorFactory;
24 import com.intellij.openapi.editor.LogicalPosition;
25 import com.intellij.openapi.editor.ScrollType;
26 import com.intellij.openapi.editor.colors.CodeInsightColors;
27 import com.intellij.openapi.editor.colors.EditorColorsScheme;
28 import com.intellij.openapi.editor.colors.TextAttributesKey;
29 import com.intellij.openapi.editor.event.CaretAdapter;
30 import com.intellij.openapi.editor.event.CaretEvent;
31 import com.intellij.openapi.editor.event.CaretListener;
32 import com.intellij.openapi.editor.ex.EditorEx;
33 import com.intellij.openapi.editor.highlighter.EditorHighlighter;
34 import com.intellij.openapi.editor.highlighter.HighlighterIterator;
35 import com.intellij.openapi.fileTypes.SyntaxHighlighter;
36 import com.intellij.openapi.options.colors.ColorSettingsPage;
37 import com.intellij.openapi.options.colors.EditorHighlightingProvidingColorSettingsPage;
38 import com.intellij.psi.tree.IElementType;
39 import com.intellij.util.Alarm;
40 import com.intellij.util.EventDispatcher;
41 import com.intellij.util.ui.UIUtil;
42 import org.jetbrains.annotations.NotNull;
46 import java.awt.event.MouseEvent;
47 import java.awt.event.MouseMotionAdapter;
49 import java.util.List;
51 public class SimpleEditorPreview implements PreviewPanel{
52 private final ColorSettingsPage myPage;
54 private final EditorEx myEditor;
55 private final Alarm myBlinkingAlarm;
56 private final HighlightData[] myHighlightData;
58 private final ColorAndFontOptions myOptions;
60 private final EventDispatcher<ColorAndFontSettingsListener> myDispatcher = EventDispatcher.create(ColorAndFontSettingsListener.class);
62 public SimpleEditorPreview(final ColorAndFontOptions options, final ColorSettingsPage page) {
63 this(options, page, true);
66 public SimpleEditorPreview(final ColorAndFontOptions options, final ColorSettingsPage page, final boolean navigatable) {
70 String text = page.getDemoText();
72 HighlightsExtractor extractant2 = new HighlightsExtractor(page.getAdditionalHighlightingTagToDescriptorMap());
73 List<HighlightData> highlights = new ArrayList<>();
74 String stripped = extractant2.extractHighlights(text, highlights);
75 myHighlightData = highlights.toArray(new HighlightData[highlights.size()]);
76 int selectedLine = -1;
77 myEditor = (EditorEx)FontEditorPreview.createPreviewEditor(stripped, 10, 3, selectedLine, myOptions, false);
79 FontEditorPreview.installTrafficLights(myEditor);
80 myBlinkingAlarm = new Alarm().setActivationComponent(myEditor.getComponent());
82 addMouseMotionListener(myEditor, page.getHighlighter(), myHighlightData, false);
84 CaretListener listener = new CaretAdapter() {
86 public void caretPositionChanged(CaretEvent e) {
87 navigate(myEditor, true, e.getNewPosition(), page.getHighlighter(), myHighlightData, false);
90 myEditor.getCaretModel().addCaretListener(listener);
94 public EditorEx getEditor() {
98 private void addMouseMotionListener(final Editor view,
99 final SyntaxHighlighter highlighter,
100 final HighlightData[] data, final boolean isBackgroundImportant) {
101 view.getContentComponent().addMouseMotionListener(new MouseMotionAdapter() {
103 public void mouseMoved(MouseEvent e) {
104 LogicalPosition pos = view.xyToLogicalPosition(new Point(e.getX(), e.getY()));
105 navigate(view, false, pos, highlighter, data, isBackgroundImportant);
110 private void navigate(final Editor editor, boolean select,
112 final SyntaxHighlighter highlighter,
113 final HighlightData[] data, final boolean isBackgroundImportant) {
114 int offset = editor.logicalPositionToOffset(pos);
116 if (!isBackgroundImportant && editor.offsetToLogicalPosition(offset).column != pos.column) {
118 ClickNavigator.setCursor(editor, Cursor.TEXT_CURSOR);
124 for (HighlightData highlightData : data) {
125 if (ClickNavigator.highlightDataContainsOffset(highlightData, editor.logicalPositionToOffset(pos))) {
127 ClickNavigator.setCursor(editor, Cursor.HAND_CURSOR);
130 myDispatcher.getMulticaster().selectionInPreviewChanged(highlightData.getHighlightType());
137 if (highlighter != null) {
138 HighlighterIterator itr = ((EditorEx)editor).getHighlighter().createIterator(offset);
139 selectItem(itr, highlighter, select);
140 ClickNavigator.setCursor(editor, select ? Cursor.TEXT_CURSOR : Cursor.HAND_CURSOR);
144 private void selectItem(HighlighterIterator itr, SyntaxHighlighter highlighter, final boolean select) {
145 IElementType tokenType = itr.getTokenType();
146 if (tokenType == null) return;
147 String type = ClickNavigator.highlightingTypeFromTokenType(tokenType, highlighter);
149 myDispatcher.getMulticaster().selectionInPreviewChanged(type);
154 public JComponent getPanel() {
155 return myEditor.getComponent();
159 public void updateView() {
160 EditorColorsScheme scheme = myOptions.getSelectedScheme();
162 myEditor.setColorsScheme(scheme);
164 EditorHighlighter highlighter = null;
165 if (myPage instanceof EditorHighlightingProvidingColorSettingsPage) {
167 highlighter = ((EditorHighlightingProvidingColorSettingsPage)myPage).createEditorHighlighter(scheme);
169 if (highlighter == null) {
170 final SyntaxHighlighter pageHighlighter = myPage.getHighlighter();
171 highlighter = HighlighterFactory.createHighlighter(pageHighlighter, scheme);
173 myEditor.setHighlighter(highlighter);
174 updateHighlighters();
176 myEditor.reinitSettings();
180 private void updateHighlighters() {
181 UIUtil.invokeLaterIfNeeded(() -> {
182 if (myEditor.isDisposed()) return;
183 myEditor.getMarkupModel().removeAllHighlighters();
184 HighlightData[] datum = myHighlightData;
185 final Map<TextAttributesKey, String> displayText = ColorSettingsUtil.keyToDisplayTextMap(myPage);
186 for (final HighlightData data : datum) {
187 data.addHighlToView(myEditor, myOptions.getSelectedScheme(), displayText);
192 private static final int BLINK_COUNT = 3 * 2;
195 public void blinkSelectedHighlightType(Object description) {
196 if (description instanceof EditorSchemeAttributeDescriptor){
197 String type = ((EditorSchemeAttributeDescriptor)description).getType();
199 List<HighlightData> highlights = startBlinkingHighlights(myEditor,
200 myHighlightData, type,
201 myPage.getHighlighter(), true,
202 myBlinkingAlarm, BLINK_COUNT, myPage);
204 scrollHighlightInView(highlights, myEditor);
208 private static void scrollHighlightInView(final List<HighlightData> highlightDatas, final Editor editor) {
209 boolean needScroll = true;
210 int minOffset = Integer.MAX_VALUE;
211 for(HighlightData data: highlightDatas) {
212 if (isOffsetVisible(editor, data.getStartOffset())) {
216 minOffset = Math.min(minOffset, data.getStartOffset());
218 if (needScroll && minOffset != Integer.MAX_VALUE) {
219 LogicalPosition pos = editor.offsetToLogicalPosition(minOffset);
220 editor.getScrollingModel().scrollTo(pos, ScrollType.MAKE_VISIBLE);
224 private static boolean isOffsetVisible(final Editor editor, final int startOffset) {
225 Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
226 Point point = editor.logicalPositionToXY(editor.offsetToLogicalPosition(startOffset));
227 return point.y >= visibleArea.y && point.y < (visibleArea.y + visibleArea.height);
230 private void stopBlinking() {
231 myBlinkingAlarm.cancelAllRequests();
234 private List<HighlightData> startBlinkingHighlights(final EditorEx editor,
235 final HighlightData[] highlightDatum,
236 final String attrKey,
237 final SyntaxHighlighter highlighter,
241 final ColorSettingsPage page) {
242 if (show && count <= 0) return Collections.emptyList();
243 editor.getMarkupModel().removeAllHighlighters();
244 boolean found = false;
245 List<HighlightData> highlights = new ArrayList<>();
246 List<HighlightData> matchingHighlights = new ArrayList<>();
247 for (int i = 0; highlightDatum != null && i < highlightDatum.length; i++) {
248 HighlightData highlightData = highlightDatum[i];
249 String type = highlightData.getHighlightType();
250 highlights.add(highlightData);
251 if (show && type.equals(attrKey)) {
253 new HighlightData(highlightData.getStartOffset(), highlightData.getEndOffset(),
254 CodeInsightColors.BLINKING_HIGHLIGHTS_ATTRIBUTES);
255 highlights.add(highlightData);
256 matchingHighlights.add(highlightData);
260 if (!found && highlighter != null) {
261 HighlighterIterator iterator = editor.getHighlighter().createIterator(0);
263 IElementType tokenType = iterator.getTokenType();
264 TextAttributesKey[] tokenHighlights = highlighter.getTokenHighlights(tokenType);
265 for (final TextAttributesKey tokenHighlight : tokenHighlights) {
266 String type = tokenHighlight.getExternalName();
267 if (show && type != null && type.equals(attrKey)) {
268 HighlightData highlightData = new HighlightData(iterator.getStart(), iterator.getEnd(),
269 CodeInsightColors.BLINKING_HIGHLIGHTS_ATTRIBUTES);
270 highlights.add(highlightData);
271 matchingHighlights.add(highlightData);
276 while (!iterator.atEnd());
279 final Map<TextAttributesKey, String> displayText = ColorSettingsUtil.keyToDisplayTextMap(page);
281 // sort highlights to avoid overlappings
282 Collections.sort(highlights, (highlightData1, highlightData2) -> highlightData1.getStartOffset() - highlightData2.getStartOffset());
283 for (int i = highlights.size() - 1; i >= 0; i--) {
284 HighlightData highlightData = highlights.get(i);
285 int startOffset = highlightData.getStartOffset();
286 HighlightData prevHighlightData = i == 0 ? null : highlights.get(i - 1);
287 if (prevHighlightData != null
288 && startOffset <= prevHighlightData.getEndOffset()
289 && highlightData.getHighlightType().equals(prevHighlightData.getHighlightType())) {
290 prevHighlightData.setEndOffset(highlightData.getEndOffset());
293 highlightData.addHighlToView(editor, myOptions.getSelectedScheme(), displayText);
296 alarm.cancelAllRequests();
297 alarm.addComponentRequest(() -> startBlinkingHighlights(editor, highlightDatum, attrKey, highlighter, !show, alarm, count - 1, page), 400);
298 return matchingHighlights;
303 public void addListener(@NotNull final ColorAndFontSettingsListener listener) {
304 myDispatcher.addListener(listener);
308 public void disposeUIResources() {
309 EditorFactory editorFactory = EditorFactory.getInstance();
310 editorFactory.releaseEditor(myEditor);