IDEA-158861 changes from review
[idea/community.git] / platform / lang-impl / src / com / intellij / application / options / colors / CustomizedSwitcherPanel.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.application.options.colors;
17
18 import com.intellij.application.options.colors.highlighting.HighlightData;
19 import com.intellij.codeHighlighting.RainbowHighlighter;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.editor.colors.EditorColorsScheme;
22 import com.intellij.openapi.editor.colors.EditorSchemeAttributeDescriptor;
23 import com.intellij.openapi.editor.colors.TextAttributesKey;
24 import com.intellij.openapi.editor.ex.DocumentEx;
25 import com.intellij.openapi.editor.ex.EditorEx;
26 import com.intellij.openapi.options.colors.ColorSettingsPage;
27 import com.intellij.openapi.options.colors.RainbowColorSettingsPage;
28 import com.intellij.openapi.util.Pair;
29 import com.intellij.openapi.util.TextRange;
30 import com.intellij.util.ui.UIUtil;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33
34 import javax.swing.*;
35 import java.awt.*;
36 import java.util.*;
37 import java.util.List;
38
39 class CustomizedSwitcherPanel extends JPanel implements OptionsPanelImpl.ColorDescriptionPanel {
40   private ColorSettingsPage myPage;
41
42   private PreviewPanel myPreviewPanel;
43   private ColorAndFontDescriptionPanel myColorAndFontPanel;
44   private RainbowDescriptionPanel myRainbowPanel;
45
46   private OptionsPanelImpl.ColorDescriptionPanel myActive;
47
48   public CustomizedSwitcherPanel(ColorAndFontGlobalState options, @Nullable PreviewPanel previewPanel, ColorSettingsPage page) {
49     super();
50     myPage = page;
51     myPreviewPanel = previewPanel;
52
53     myRainbowPanel = new RainbowDescriptionPanel(options);
54     myColorAndFontPanel = new ColorAndFontDescriptionPanel();
55
56     Dimension sizeR = myRainbowPanel.getPreferredSize();
57     Dimension sizeC = myColorAndFontPanel.getPreferredSize();
58     Dimension preferredSize = new Dimension();
59     preferredSize.setSize(Math.max(sizeR.getWidth(), sizeC.getWidth()),
60                           Math.max(sizeR.getHeight(), sizeC.getHeight()));
61     setPreferredSize(preferredSize);
62   }
63
64   @NotNull
65   @Override
66   public JComponent getPanel() {
67     return this;
68   }
69
70   @Override
71   public void resetDefault() {
72     myActive = null;
73     if (getComponentCount() != 0) {
74       final PaintLocker locker = new PaintLocker(this);
75       try {
76         setPreferredSize(getSize());
77         remove(0);
78       }
79       finally {
80         locker.release();
81       }
82     }
83   }
84
85   @Override
86   public void reset(@NotNull EditorSchemeAttributeDescriptor descriptor) {
87     myActive = null;
88     if (descriptor instanceof RainbowAttributeDescriptor) {
89       myActive = myRainbowPanel;
90     }
91     else if (descriptor instanceof ColorAndFontDescription) {
92       myActive = myColorAndFontPanel;
93     }
94
95     if (getComponentCount() == 0 || myActive != getComponent(0)) {
96       final PaintLocker locker = new PaintLocker(this);
97       try {
98         if (getComponentCount() != 0) {
99           remove(0);
100         }
101         setPreferredSize(null);
102         add((JPanel)myActive);
103       }
104       finally {
105         locker.release();
106       }
107     }
108     myActive.reset(descriptor);
109     updatePreviewPanel(descriptor);
110   }
111
112   private void addRainbowHighlighting(@NotNull DocumentEx document,
113                                       @Nullable List<HighlightData> showLineData,
114                                       @NotNull List<HighlightData> data,
115                                       @NotNull RainbowHighlighter rainbowHighlighter,
116                                       @NotNull List<TextAttributesKey> rainbowTempKeys) {
117     if (!rainbowTempKeys.isEmpty()) {
118       List<HighlightData> newData = new ArrayList<HighlightData>();
119       if (showLineData != null) newData.addAll(showLineData);
120
121       HashMap<String, Integer> id2index = new HashMap<String, Integer>();
122
123       for (HighlightData d : data) {
124         if (((RainbowColorSettingsPage)myPage).isRainbowType(d.getHighlightKey())) {
125           String id = document.getText(TextRange.create(d.getStartOffset(), d.getEndOffset()));
126
127           int index = rainbowHighlighter.getColorIndex(id2index, id, RainbowHighlighter.getRainbowHash(id));
128           HighlightData rainbow = new HighlightData(d.getStartOffset(), d.getEndOffset(), rainbowTempKeys.get(index));
129
130           //fixme: twisted coloring in editor. We need add rainbow-tag twice.
131           newData.add(rainbow);
132           newData.add(d);
133           newData.add(rainbow);
134         }
135         else if (!RainbowHighlighter.isRainbowTempKey(d.getHighlightKey())) {
136           newData.add(d);
137         }
138       }
139       data.clear();
140       data.addAll(newData);
141     }
142   }
143
144   private static void removeRainbowHighlighting(@NotNull List<HighlightData> data) {
145     List<TextAttributesKey> keys = RainbowHighlighter.getRainbowKeys();
146     if (!keys.isEmpty()) {
147       List<HighlightData> newData = new ArrayList<HighlightData>();
148       for (HighlightData d : data) {
149         if (!keys.contains(d.getHighlightKey())) {
150           newData.add(d);
151         }
152       }
153       data.clear();
154       data.addAll(newData);
155     }
156   }
157
158   @Override
159   public void apply(@NotNull EditorSchemeAttributeDescriptor descriptor, EditorColorsScheme scheme) {
160     if (myActive != null) {
161       myActive.apply(descriptor, scheme);
162       updatePreviewPanel(descriptor);
163     }
164   }
165
166   protected void updatePreviewPanel(@NotNull EditorSchemeAttributeDescriptor descriptor) {
167     if (!(myPreviewPanel instanceof SimpleEditorPreview)) return;
168     UIUtil.invokeAndWaitIfNeeded((Runnable)() -> ApplicationManager.getApplication().runWriteAction(() -> {
169       SimpleEditorPreview simpleEditorPreview = (SimpleEditorPreview)myPreviewPanel;
170       String demoText = (myPage instanceof RainbowColorSettingsPage
171                          && descriptor instanceof RainbowAttributeDescriptor)
172                         ? ((RainbowColorSettingsPage)myPage).getRainbowDemoText()
173                         : myPage.getDemoText();
174       List<HighlightData> showLineData = null;
175
176       if (myPage instanceof RainbowColorSettingsPage && myRainbowPanel.myGlobalState.isRainbowOn) {
177         RainbowHighlighter highlighter = new RainbowHighlighter(descriptor.getScheme());
178         List<TextAttributesKey> tempKeys = highlighter.getRainbowTempKeys();
179         EditorEx editor = simpleEditorPreview.getEditor();
180         if (myActive == myRainbowPanel) {
181           Pair<String, List<HighlightData>> demo = getColorDemoLine(highlighter, tempKeys);
182           simpleEditorPreview.setDemoText(demo.first + "\n" + demoText);
183           showLineData = demo.second;
184         }
185         else {
186           simpleEditorPreview.setDemoText(demoText);
187         }
188         addRainbowHighlighting(editor.getDocument(),
189                                showLineData,
190                                simpleEditorPreview.getHighlightDataForExtension(),
191                                highlighter,
192                                tempKeys);
193       }
194       else {
195         simpleEditorPreview.setDemoText(demoText);
196         removeRainbowHighlighting(simpleEditorPreview.getHighlightDataForExtension());
197       }
198
199       simpleEditorPreview.updateView();
200       if (descriptor instanceof RainbowAttributeDescriptor) {
201         simpleEditorPreview.scrollHighlightInView(showLineData);
202       }
203     }));
204   }
205
206   @NotNull
207   private static Pair<String, List<HighlightData>> getColorDemoLine(RainbowHighlighter highlighter, List<TextAttributesKey> tempKeys) {
208     int colorsCount = highlighter.getColorsCount();
209     int stopCount = RainbowHighlighter.getRainbowKeys().size();
210     List<HighlightData> markup = new ArrayList<HighlightData>(colorsCount);
211     StringBuilder sb = new StringBuilder();
212     int pos = 0;
213     int i = 0;
214     for (TextAttributesKey key : tempKeys) {
215       String toAdd = (i % stopCount == 0) ? "Stop#" + String.valueOf(i / stopCount + 1) : "T";
216       int end = pos + toAdd.length();
217       markup.add(new HighlightData(pos, end, key));
218       if (sb.length() != 0) {
219         sb.append(" ");
220       }
221       sb.append(toAdd);
222       pos = end + 1;
223       ++i;
224     }
225     return Pair.create(sb.toString(), markup);
226   }
227
228   @Override
229   public void addListener(@NotNull Listener listener) {
230     myRainbowPanel.addListener(listener);
231     myColorAndFontPanel.addListener(listener);
232   }
233
234   private static class PaintLocker {
235     private Container myPaintHolder;
236     private boolean myPaintState;
237
238     public PaintLocker(@NotNull JComponent component) {
239       myPaintHolder = component.getParent();
240       myPaintState = myPaintHolder.getIgnoreRepaint();
241       myPaintHolder.setIgnoreRepaint(true);
242     }
243
244     public void release() {
245       myPaintHolder.validate();
246       myPaintHolder.setIgnoreRepaint(myPaintState);
247       myPaintHolder.repaint();
248     }
249   }
250 }