Merge branch 'master' into uta/rainbow
[idea/community.git] / platform / lang-impl / src / com / intellij / application / options / colors / OptionsPanelImpl.java
1 /*
2  * Copyright 2000-2015 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
17 package com.intellij.application.options.colors;
18
19 import com.intellij.ide.DataManager;
20 import com.intellij.ide.util.PropertiesComponent;
21 import com.intellij.openapi.editor.colors.EditorColorsScheme;
22 import com.intellij.openapi.options.SearchableConfigurable;
23 import com.intellij.openapi.editor.colors.EditorSchemeAttributeDescriptor;
24 import com.intellij.openapi.options.ex.Settings;
25 import com.intellij.openapi.util.ActionCallback;
26 import com.intellij.ui.ScrollPaneFactory;
27 import com.intellij.util.EventDispatcher;
28 import org.jetbrains.annotations.NotNull;
29
30 import javax.swing.*;
31 import javax.swing.event.HyperlinkEvent;
32 import javax.swing.event.TreeSelectionEvent;
33 import javax.swing.event.TreeSelectionListener;
34 import javax.swing.text.BadLocationException;
35 import javax.swing.text.Element;
36 import java.awt.*;
37 import java.awt.event.ActionEvent;
38 import java.util.EventListener;
39 import java.util.HashSet;
40 import java.util.Set;
41
42 public class OptionsPanelImpl extends JPanel implements OptionsPanel {
43   public static final String SELECTED_COLOR_OPTION_PROPERTY = "selected.color.option.type";
44
45   private final ColorOptionsTree myOptionsTree;
46   private final ColorDescriptionPanel myOptionsPanel;
47
48   private final ColorAndFontOptions myOptions;
49   private final SchemesPanel mySchemesProvider;
50   private final String myCategoryName;
51
52   private final PropertiesComponent myProperties;
53
54   private final EventDispatcher<ColorAndFontSettingsListener> myDispatcher = EventDispatcher.create(ColorAndFontSettingsListener.class);
55
56   public OptionsPanelImpl(ColorAndFontOptions options,
57                           SchemesPanel schemesProvider,
58                           String categoryName) {
59     this(options, schemesProvider, categoryName, new ColorAndFontDescriptionPanel());
60   }
61
62   public OptionsPanelImpl(ColorAndFontOptions options,
63                           SchemesPanel schemesProvider,
64                           String categoryName,
65                           ColorDescriptionPanel optionsPanel) {
66     super(new BorderLayout());
67     myOptions = options;
68     mySchemesProvider = schemesProvider;
69     myCategoryName = categoryName;
70     myProperties = PropertiesComponent.getInstance();
71
72     myOptionsPanel = optionsPanel;
73     myOptionsPanel.addListener(new ColorDescriptionPanel.Listener() {
74       @Override
75       public void onSettingsChanged(ActionEvent e) {
76         myDispatcher.getMulticaster().settingsChanged();
77         myOptions.getColorAndFontGlobalState().stateChanged();
78       }
79
80       @Override
81       public void onHyperLinkClicked(HyperlinkEvent e) {
82         if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
83           Settings settings = Settings.KEY.getData(DataManager.getInstance().getDataContext(OptionsPanelImpl.this));
84           String attrName = e.getDescription();
85           Element element = e.getSourceElement();
86           String pageName;
87           try {
88             pageName = element.getDocument().getText(element.getStartOffset(), element.getEndOffset() - element.getStartOffset());
89           }
90           catch (BadLocationException e1) {
91             return;
92           }
93           final SearchableConfigurable page = myOptions.findSubConfigurable(pageName);
94           if (page != null && settings != null) {
95             Runnable runnable = page.enableSearch(attrName);
96             ActionCallback callback = settings.select(page);
97             if (runnable != null) callback.doWhenDone(runnable);
98           }
99         }
100       }
101     });
102
103     myOptions.getColorAndFontGlobalState().addListener(new ColorAndFontSettingsListener.Abstract() {
104       @Override
105       public void settingsChanged() {
106         if (!mySchemesProvider.areSchemesLoaded()) return;
107         if (myOptionsTree.getSelectedValue() != null) {
108           // update options & preview after global state change
109           processListValueChanged();
110         }
111       }
112     });
113
114     myOptionsTree = new ColorOptionsTree(myCategoryName);
115
116     myOptionsTree.addTreeSelectionListener(new TreeSelectionListener() {
117       @Override
118       public void valueChanged(TreeSelectionEvent e) {
119         if (!mySchemesProvider.areSchemesLoaded()) return;
120         processListValueChanged();
121       }
122     });
123
124     JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myOptionsTree);
125     add(scrollPane, BorderLayout.CENTER);
126     add(myOptionsPanel.getPanel(), BorderLayout.EAST);
127
128   }
129
130   @Override
131   public void addListener(ColorAndFontSettingsListener listener) {
132     myDispatcher.addListener(listener);
133   }
134
135   private void processListValueChanged() {
136     Object selectedValue = myOptionsTree.getSelectedValue();
137     EditorSchemeAttributeDescriptor description = selectedValue instanceof EditorSchemeAttributeDescriptor
138                                                   ? (EditorSchemeAttributeDescriptor)selectedValue
139                                                   : null;
140     if (description == null) {
141       if (selectedValue == null) {
142         String preselectedType = myProperties.getValue(SELECTED_COLOR_OPTION_PROPERTY);
143         if (preselectedType != null) {
144           myOptionsTree.selectOptionByType(preselectedType);
145           description = myOptionsTree.getSelectedDescriptor();
146         }
147       }
148     }
149     if (description != null) {
150       myProperties.setValue(SELECTED_COLOR_OPTION_PROPERTY, description.getType());
151       myOptionsPanel.reset(description);
152       myDispatcher.getMulticaster().selectedOptionChanged(description);
153     }
154     else {
155       myOptionsPanel.resetDefault();
156     }
157   }
158
159   private void fillOptionsList() {
160     myOptionsTree.fillOptions(myOptions);
161   }
162
163   @Override
164   public JPanel getPanel() {
165     return this;
166   }
167
168   @Override
169   public void updateOptionsList() {
170     fillOptionsList();
171     processListValueChanged();
172   }
173
174   @Override
175   public Runnable showOption(final String attributeDisplayName) {
176     return () -> myOptionsTree.selectOptionByName(attributeDisplayName);
177   }
178
179   @Override
180   public void applyChangesToScheme() {
181     EditorSchemeAttributeDescriptor descriptor = myOptionsTree.getSelectedDescriptor();
182     if (descriptor != null) {
183       myOptionsPanel.apply(descriptor, myOptions.getSelectedScheme());
184     }
185   }
186
187   @Override
188   public void selectOption(String attributeType) {
189     myOptionsTree.selectOptionByType(attributeType);
190   }
191
192   @Override
193   public Set<String> processListOptions() {
194     HashSet<String> result = new HashSet<>();
195     EditorSchemeAttributeDescriptor[] descriptions = myOptions.getCurrentDescriptions();
196     for (EditorSchemeAttributeDescriptor description : descriptions) {
197       if (description.getGroup().equals(myCategoryName)) {
198         result.add(description.toString());
199       }
200     }
201     return result;
202   }
203
204   public interface ColorDescriptionPanel {
205     @NotNull
206     JComponent getPanel();
207
208     void resetDefault();
209
210     void reset(@NotNull EditorSchemeAttributeDescriptor description);
211
212     void apply(@NotNull EditorSchemeAttributeDescriptor descriptor, EditorColorsScheme scheme);
213
214     void addListener(@NotNull Listener listener);
215
216     interface Listener extends EventListener {
217       void onSettingsChanged(ActionEvent e);
218
219       void onHyperLinkClicked(HyperlinkEvent e);
220     }
221   }
222 }