825f687420464268ccf6345d396f84fa0fa6b665
[idea/community.git] / platform / lang-impl / src / com / intellij / application / options / TabbedLanguageCodeStylePanel.java
1 /*
2  * Copyright 2000-2011 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;
17
18 import com.intellij.application.options.codeStyle.*;
19 import com.intellij.lang.Language;
20 import com.intellij.openapi.application.ApplicationBundle;
21 import com.intellij.openapi.editor.colors.EditorColorsScheme;
22 import com.intellij.openapi.editor.highlighter.EditorHighlighter;
23 import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
24 import com.intellij.openapi.fileTypes.FileType;
25 import com.intellij.openapi.fileTypes.FileTypes;
26 import com.intellij.openapi.options.Configurable;
27 import com.intellij.openapi.options.ConfigurationException;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.project.ProjectUtil;
30 import com.intellij.openapi.util.Disposer;
31 import com.intellij.psi.codeStyle.*;
32 import com.intellij.ui.components.JBTabbedPane;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
35
36 import javax.swing.*;
37 import java.awt.*;
38 import java.awt.event.ActionEvent;
39 import java.awt.event.ActionListener;
40 import java.util.ArrayList;
41 import java.util.Collections;
42 import java.util.Comparator;
43 import java.util.List;
44
45 /**
46  * @author Rustam Vishnyakov
47  */
48
49 public abstract class TabbedLanguageCodeStylePanel extends CodeStyleAbstractPanel {
50
51   private CodeStyleAbstractPanel myActiveTab;
52   private List<CodeStyleAbstractPanel> myTabs;
53   private JPanel myPanel;
54   private JTabbedPane myTabbedPane;
55   private PredefinedCodeStyle[] myPredefinedCodeStyles;
56
57   protected TabbedLanguageCodeStylePanel(@Nullable Language language, CodeStyleSettings currentSettings, CodeStyleSettings settings) {
58     super(language, currentSettings, settings);
59     myPredefinedCodeStyles = getPredefinedStyles();
60   }
61
62   /**
63    * Initializes all standard tabs: "Tabs and Indents", "Spaces", "Blank Lines" and "Wrapping and Braces" if relevant.
64    * For "Tabs and Indents" LanguageCodeStyleSettingsProvider must instantiate its own indent options, for other standard tabs it
65    * must return false in usesSharedPreview() method. You can override this method to add your own tabs by calling super.initTabs() and
66    * then addTab() methods or selectively add needed tabs with your own implementation.
67    * @param settings  Code style settings to be used with initialized panels.
68    * @see LanguageCodeStyleSettingsProvider
69    * @see #addIndentOptionsTab(com.intellij.psi.codeStyle.CodeStyleSettings)
70    * @see #addSpacesTab(com.intellij.psi.codeStyle.CodeStyleSettings)
71    * @see #addBlankLinesTab(com.intellij.psi.codeStyle.CodeStyleSettings)
72    * @see #addWrappingAndBracesTab(com.intellij.psi.codeStyle.CodeStyleSettings)
73    */
74   protected void initTabs(CodeStyleSettings settings) {
75     LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(getDefaultLanguage());
76     addIndentOptionsTab(settings);
77     if (provider != null && !provider.usesSharedPreview()) {
78       addSpacesTab(settings);
79       addWrappingAndBracesTab(settings);
80       addBlankLinesTab(settings);
81     }
82   }
83
84   /**
85    * Adds "Tabs and Indents" tab if the language has its own LanguageCodeStyleSettings provider and instantiates indent options in 
86    * getDefaultSettings() method.
87    * @param settings CodeStyleSettings to be used with "Tabs and Indents" panel.
88    */
89   protected void addIndentOptionsTab(CodeStyleSettings settings) {
90     LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(getDefaultLanguage());
91     if (provider != null) {
92       IndentOptionsEditor indentOptionsEditor = provider.getIndentOptionsEditor();
93       if (indentOptionsEditor != null) {
94         MyIndentOptionsWrapper indentOptionsWrapper = new MyIndentOptionsWrapper(settings, provider, indentOptionsEditor);
95         addTab(indentOptionsWrapper);
96       }
97     }
98   }
99
100   protected void addSpacesTab(CodeStyleSettings settings) {
101     addTab(new MySpacesPanel(settings));
102   }
103
104   protected void addBlankLinesTab(CodeStyleSettings settings) {
105     addTab(new MyBlankLinesPanel(settings));
106   }
107
108   protected void addWrappingAndBracesTab(CodeStyleSettings settings) {
109     addTab(new MyWrappingAndBracesPanel(settings));
110   }
111
112   private void ensureTabs() {
113     if (myTabs == null) {
114       myPanel = new JPanel();
115       myPanel.setLayout(new BorderLayout());
116       myTabbedPane = new JBTabbedPane();
117       myTabs = new ArrayList<CodeStyleAbstractPanel>();
118       myPanel.add(myTabbedPane);
119       initTabs(getSettings());
120     }
121     assert !myTabs.isEmpty();
122   }
123
124   /**
125    * Adds a tab with the given CodeStyleAbstractPanel. Tab title is taken from getTabTitle() method.
126    * @param tab The panel to use in a tab.
127    */
128   protected final void addTab(CodeStyleAbstractPanel tab) {
129     myTabs.add(tab);
130     tab.setShouldUpdatePreview(true);
131     addPanelToWatch(tab.getPanel());
132     myTabbedPane.addTab(tab.getTabTitle(), tab.getPanel());
133     if (myActiveTab == null) {
134       myActiveTab = tab;
135     }
136   }
137
138   private void addTab(Configurable configurable) {
139     ConfigurableWrapper wrapper = new ConfigurableWrapper(configurable, getSettings());
140     addTab(wrapper);
141   }
142
143   /**
144    * Creates and adds a tab from CodeStyleSettingsProvider. The provider may return false in hasSettingsPage() method in order not to be
145    * shown at top level of code style settings.
146    * @param provider The provider used to create a settings page.
147    */
148   protected final void createTab(CodeStyleSettingsProvider provider) {
149     if (provider.hasSettingsPage()) return;
150     Configurable configurable = provider.createSettingsPage(getCurrentSettings(), getSettings());
151     addTab(configurable);
152   }
153
154   @Override
155   public final void setModel(CodeStyleSchemesModel model) {
156     super.setModel(model);
157     ensureTabs();
158     for (CodeStyleAbstractPanel tab : myTabs) {
159       tab.setModel(model);
160     }
161   }
162
163   @Override
164   protected int getRightMargin() {
165     ensureTabs();
166     return myActiveTab.getRightMargin();
167   }
168
169   @Override
170   protected EditorHighlighter createHighlighter(EditorColorsScheme scheme) {
171     ensureTabs();
172     return myActiveTab.createHighlighter(scheme);
173   }
174
175   @NotNull
176   @Override
177   protected FileType getFileType() {
178     ensureTabs();
179     return myActiveTab.getFileType();
180   }
181
182   @Override
183   protected String getPreviewText() {
184     ensureTabs();
185     return myActiveTab.getPreviewText();
186   }
187
188   @Override
189   protected void updatePreview(boolean useDefaultSample) {
190     ensureTabs();
191     for (CodeStyleAbstractPanel tab : myTabs) {
192       tab.updatePreview(useDefaultSample);
193     }
194   }
195
196   @Override
197   public void onSomethingChanged() {
198     ensureTabs();
199     for (CodeStyleAbstractPanel tab : myTabs) {
200       tab.setShouldUpdatePreview(true);
201       tab.onSomethingChanged();
202     }
203   }
204
205   @Override
206   protected void somethingChanged() {
207     super.somethingChanged();
208   }
209
210   @Override
211   public void apply(CodeStyleSettings settings) {
212     ensureTabs();
213     for (CodeStyleAbstractPanel tab : myTabs) {
214       tab.apply(settings);
215     }
216   }
217
218   @Override
219   public void dispose() {
220     super.dispose();
221     for (CodeStyleAbstractPanel tab : myTabs) {
222       Disposer.dispose(tab);
223     }
224   }
225
226   @Override
227   public boolean isModified(CodeStyleSettings settings) {
228     ensureTabs();
229     for (CodeStyleAbstractPanel tab : myTabs) {
230       if (tab.isModified(settings)) {
231         return true;
232       }
233     }
234     return false;
235   }
236
237   @Override
238   public JComponent getPanel() {
239     return myPanel;
240   }
241
242   @Override
243   protected void resetImpl(CodeStyleSettings settings) {
244     ensureTabs();
245     for (CodeStyleAbstractPanel tab : myTabs) {
246       tab.resetImpl(settings);
247     }
248   }
249
250
251   @Override
252   public void setupCopyFromMenu(Menu copyMenu) {
253     super.setupCopyFromMenu(copyMenu);
254     if (myPredefinedCodeStyles.length > 0) {
255       Menu langs = new Menu("Language"); //TODO<rv>: Move to resource bundle
256       copyMenu.add(langs);
257       fillLanguages(langs);
258       Menu predefined = new Menu("Predefined Style"); //TODO<rv>: Move to resource bundle
259       copyMenu.add(predefined);
260       fillPredefined(predefined);
261     }
262     else {
263       fillLanguages(copyMenu);
264     }
265   }
266
267
268   private void fillLanguages(Menu parentMenu) {
269       Language[] languages = LanguageCodeStyleSettingsProvider.getLanguagesWithCodeStyleSettings();
270       @SuppressWarnings("UnnecessaryFullyQualifiedName")
271       java.util.List<MenuItem> langItems = new ArrayList<MenuItem>();
272       for (final Language lang : languages) {
273         if (!lang.equals(getDefaultLanguage())) {
274           final String langName = LanguageCodeStyleSettingsProvider.getLanguageName(lang);
275           MenuItem langItem = new MenuItem(langName);
276           langItem.addActionListener(new ActionListener(){
277             @Override
278             public void actionPerformed(ActionEvent e) {
279               applyLanguageSettings(lang);
280             }
281           });
282           langItems.add(langItem);
283         }
284       }
285       Collections.sort(langItems, new Comparator<MenuItem>() {
286         @Override
287         public int compare(MenuItem item1, MenuItem item2) {
288           return item1.getLabel().compareToIgnoreCase(item2.getLabel());
289         }
290       });
291       for (MenuItem langItem : langItems) {
292         parentMenu.add(langItem);
293       }
294     }
295
296   private void fillPredefined(Menu parentMenu) {
297     for (final PredefinedCodeStyle predefinedCodeStyle : myPredefinedCodeStyles) {
298       MenuItem predefinedItem = new MenuItem(predefinedCodeStyle.getName());
299       parentMenu.add(predefinedItem);
300       predefinedItem.addActionListener(new ActionListener() {
301         @Override
302         public void actionPerformed(ActionEvent e) {
303           applyPredefinedStyle(predefinedCodeStyle.getName());
304         }
305       });
306     }
307   }
308
309   private PredefinedCodeStyle[] getPredefinedStyles() {
310     LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(getDefaultLanguage());
311     if (provider == null) return new PredefinedCodeStyle[0];
312     return provider.getPredefinedCodeStyles();
313   }
314
315
316   private void applyLanguageSettings(Language lang) {
317     final Project currProject = ProjectUtil.guessCurrentProject(getPanel());
318     CodeStyleSettings rootSettings = CodeStyleSettingsManager.getSettings(currProject);
319     CommonCodeStyleSettings sourceSettings = rootSettings.getCommonSettings(lang);
320     CommonCodeStyleSettings targetSettings = getSettings().getCommonSettings(getDefaultLanguage());
321     if (sourceSettings == null || targetSettings == null) return;
322     CommonCodeStyleSettingsManager.copy(sourceSettings, targetSettings);
323     reset(getSettings());
324     onSomethingChanged();
325   }
326
327   private void applyPredefinedStyle(String styleName) {
328     for (PredefinedCodeStyle style : myPredefinedCodeStyles) {
329       if (style.getName().equals(styleName)) {
330         applyPredefinedSettings(style);
331       }
332     }
333   }
334
335 //========================================================================================================================================
336
337   private class MySpacesPanel extends CodeStyleSpacesPanel {
338
339     public MySpacesPanel(CodeStyleSettings settings) {
340       super(settings);
341       setPanelLanguage(TabbedLanguageCodeStylePanel.this.getDefaultLanguage());
342     }
343
344     @Override
345     protected void installPreviewPanel(JPanel previewPanel) {
346       previewPanel.setLayout(new BorderLayout());
347       previewPanel.add(getEditor().getComponent(), BorderLayout.CENTER);
348     }
349
350     @Override
351     protected void customizeSettings() {
352       customizePanel(this);
353     }
354
355     @Override
356     protected boolean shouldHideOptions() {
357       return true;
358     }
359   }
360
361   private class MyBlankLinesPanel extends CodeStyleBlankLinesPanel {
362
363     public MyBlankLinesPanel(CodeStyleSettings settings) {
364       super(settings);
365       setPanelLanguage(TabbedLanguageCodeStylePanel.this.getDefaultLanguage());
366     }
367
368     @Override
369     protected void customizeSettings() {
370       customizePanel(this);
371     }
372
373     @Override
374     protected void installPreviewPanel(JPanel previewPanel) {
375       previewPanel.setLayout(new BorderLayout());
376       previewPanel.add(getEditor().getComponent(), BorderLayout.CENTER);
377     }
378
379   }
380
381   private class MyWrappingAndBracesPanel extends WrappingAndBracesPanel {
382
383     public MyWrappingAndBracesPanel(CodeStyleSettings settings) {
384       super(settings);
385       setPanelLanguage(TabbedLanguageCodeStylePanel.this.getDefaultLanguage());
386     }
387
388     @Override
389     protected void customizeSettings() {
390       customizePanel(this);
391     }
392
393     @Override
394     protected void installPreviewPanel(JPanel previewPanel) {
395       previewPanel.setLayout(new BorderLayout());
396       previewPanel.add(getEditor().getComponent(), BorderLayout.CENTER);
397     }
398   }
399
400   private void customizePanel(MultilanguageCodeStyleAbstractPanel panel) {
401     LanguageCodeStyleSettingsProvider provider = LanguageCodeStyleSettingsProvider.forLanguage(getDefaultLanguage());
402     if (provider != null) {
403       provider.customizeSettings(panel, panel.getSettingsType());
404     }
405   }
406
407
408   //========================================================================================================================================
409
410   private class ConfigurableWrapper extends CodeStyleAbstractPanel {
411
412     private Configurable myConfigurable;
413
414     public ConfigurableWrapper(@NotNull Configurable configurable, CodeStyleSettings settings) {
415       super(settings);
416       myConfigurable = configurable;
417     }
418
419     @Override
420     protected int getRightMargin() {
421       return 0;
422     }
423
424     @Nullable
425     @Override
426     protected EditorHighlighter createHighlighter(EditorColorsScheme scheme) {
427       return null;
428     }
429
430     @SuppressWarnings("ConstantConditions")
431     @NotNull
432     @Override
433     protected FileType getFileType() {
434       Language language = getDefaultLanguage();
435       return language != null ? language.getAssociatedFileType() : FileTypes.PLAIN_TEXT;
436     }
437
438     @Override
439     public Language getDefaultLanguage() {
440       return TabbedLanguageCodeStylePanel.this.getDefaultLanguage();
441     }
442
443     @Override
444     protected String getTabTitle() {
445       return myConfigurable.getDisplayName();
446     }
447
448     @Override
449     protected String getPreviewText() {
450       return null;
451     }
452
453     @Override
454     public void apply(CodeStyleSettings settings) {
455       try {
456         myConfigurable.apply();
457       }
458       catch (ConfigurationException e) {
459         // Ignore
460       }
461     }
462
463     @Override
464     public boolean isModified(CodeStyleSettings settings) {
465       return myConfigurable.isModified();
466     }
467
468     @Nullable
469     @Override
470     public JComponent getPanel() {
471       return myConfigurable.createComponent();
472     }
473
474     @Override
475     protected void resetImpl(CodeStyleSettings settings) {
476       myConfigurable.reset();
477     }
478   }
479
480   @Override
481   public boolean isCopyFromMenuAvailable() {
482     return true;
483   }
484
485   //========================================================================================================================================
486   
487   private class MyIndentOptionsWrapper extends CodeStyleAbstractPanel {
488
489     private final IndentOptionsEditor myEditor;
490     private final LanguageCodeStyleSettingsProvider myProvider;
491     private JPanel myTopPanel;
492     private JPanel myLeftPanel;
493     private JPanel myRightPanel;
494
495     protected MyIndentOptionsWrapper(CodeStyleSettings settings, LanguageCodeStyleSettingsProvider provider, IndentOptionsEditor editor) {
496       super(settings);
497       myProvider = provider;
498       myTopPanel = new JPanel();
499       myTopPanel.setLayout(new BorderLayout());
500       myLeftPanel = new JPanel();
501       myTopPanel.add(myLeftPanel, BorderLayout.WEST);
502       myRightPanel = new JPanel();
503       installPreviewPanel(myRightPanel);
504       myEditor = editor;
505       if (myEditor != null) {
506         myLeftPanel.add(myEditor.createPanel());
507       }
508       myTopPanel.add(myRightPanel, BorderLayout.CENTER);
509     }
510
511     @Override
512     protected int getRightMargin() {
513       return getSettings().RIGHT_MARGIN;
514     }
515
516     @Override
517     protected EditorHighlighter createHighlighter(EditorColorsScheme scheme) {
518       //noinspection NullableProblems
519       return EditorHighlighterFactory.getInstance().createEditorHighlighter(getFileType(), scheme, null);
520     }
521
522     @SuppressWarnings("ConstantConditions")
523     @NotNull
524     @Override
525     protected FileType getFileType() {
526       Language language = TabbedLanguageCodeStylePanel.this.getDefaultLanguage();
527       return language != null ? language.getAssociatedFileType() : FileTypes.PLAIN_TEXT;
528     }
529
530     @Override
531     protected String getPreviewText() {
532       return myProvider != null ? myProvider.getCodeSample(LanguageCodeStyleSettingsProvider.SettingsType.INDENT_SETTINGS) : "Loading...";
533     }
534
535     @Override
536     public void apply(CodeStyleSettings settings) {
537       CommonCodeStyleSettings.IndentOptions indentOptions = getIndentOptions(settings);
538       if (indentOptions == null) return;
539       myEditor.apply(settings, indentOptions);
540     }
541
542     @Override
543     public boolean isModified(CodeStyleSettings settings) {
544       CommonCodeStyleSettings.IndentOptions indentOptions = getIndentOptions(settings);
545       if (indentOptions == null) return false;
546       return myEditor.isModified(settings, indentOptions);
547     }
548
549     @Override
550     public JComponent getPanel() {
551       return myTopPanel;
552     }
553
554     @Override
555     protected void resetImpl(CodeStyleSettings settings) {
556       CommonCodeStyleSettings.IndentOptions indentOptions = getIndentOptions(settings);
557       if (indentOptions == null) {
558         myEditor.setEnabled(false);
559         indentOptions = settings.getIndentOptions(myProvider.getLanguage().getAssociatedFileType());
560       }
561       myEditor.reset(settings, indentOptions);
562     }
563
564     @Nullable
565     private CommonCodeStyleSettings.IndentOptions getIndentOptions(CodeStyleSettings settings) {
566       return settings.getCommonSettings(getDefaultLanguage()).getIndentOptions();
567     }
568
569     @Override
570     public Language getDefaultLanguage() {
571       return TabbedLanguageCodeStylePanel.this.getDefaultLanguage();
572     }
573
574     @Override
575     protected String getTabTitle() {
576       return ApplicationBundle.message("title.tabs.and.indents");
577     }
578
579     @Override
580     public void onSomethingChanged() {
581       super.onSomethingChanged();
582       myEditor.setEnabled(true);
583     }
584
585   }
586 }