2 * Copyright 2000-2016 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.OptionsContainingConfigurable;
20 import com.intellij.application.options.editor.EditorOptionsProvider;
21 import com.intellij.execution.impl.ConsoleViewUtil;
22 import com.intellij.ide.ui.LafManager;
23 import com.intellij.ide.ui.laf.LafManagerImpl;
24 import com.intellij.ide.ui.laf.darcula.DarculaInstaller;
25 import com.intellij.ide.ui.laf.darcula.DarculaLookAndFeelInfo;
26 import com.intellij.ide.util.PropertiesComponent;
27 import com.intellij.openapi.Disposable;
28 import com.intellij.openapi.application.ApplicationBundle;
29 import com.intellij.openapi.application.ApplicationNamesInfo;
30 import com.intellij.openapi.editor.colors.*;
31 import com.intellij.openapi.editor.colors.impl.*;
32 import com.intellij.openapi.editor.markup.EffectType;
33 import com.intellij.openapi.editor.markup.TextAttributes;
34 import com.intellij.openapi.extensions.Extensions;
35 import com.intellij.openapi.options.Configurable;
36 import com.intellij.openapi.options.ConfigurationException;
37 import com.intellij.openapi.options.SchemeManager;
38 import com.intellij.openapi.options.SearchableConfigurable;
39 import com.intellij.openapi.options.colors.*;
40 import com.intellij.openapi.project.Project;
41 import com.intellij.openapi.project.ProjectManager;
42 import com.intellij.openapi.ui.DialogWrapper;
43 import com.intellij.openapi.ui.Messages;
44 import com.intellij.openapi.util.Comparing;
45 import com.intellij.openapi.util.Disposer;
46 import com.intellij.openapi.util.Pair;
47 import com.intellij.openapi.vcs.FileStatus;
48 import com.intellij.openapi.vcs.FileStatusFactory;
49 import com.intellij.packageDependencies.DependencyValidationManager;
50 import com.intellij.packageDependencies.DependencyValidationManagerImpl;
51 import com.intellij.psi.codeStyle.DisplayPriority;
52 import com.intellij.psi.codeStyle.DisplayPrioritySortable;
53 import com.intellij.psi.search.scope.packageSet.NamedScope;
54 import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
55 import com.intellij.psi.search.scope.packageSet.PackageSet;
56 import com.intellij.ui.ColorUtil;
57 import com.intellij.util.ArrayUtil;
58 import com.intellij.util.ui.UIUtil;
59 import gnu.trove.THashMap;
60 import gnu.trove.THashSet;
61 import gnu.trove.TObjectHashingStrategy;
62 import org.jetbrains.annotations.Nls;
63 import org.jetbrains.annotations.NonNls;
64 import org.jetbrains.annotations.NotNull;
65 import org.jetbrains.annotations.Nullable;
70 import java.util.List;
72 public class ColorAndFontOptions extends SearchableConfigurable.Parent.Abstract implements EditorOptionsProvider {
73 public static final String ID = "reference.settingsdialog.IDE.editor.colors";
75 private Map<String, MyColorScheme> mySchemes;
76 private MyColorScheme mySelectedScheme;
78 private final ColorAndFontGlobalState myColorAndFontGlobalState = new ColorAndFontGlobalState();
80 public static final String FILE_STATUS_GROUP = ApplicationBundle.message("title.file.status");
81 public static final String SCOPES_GROUP = ApplicationBundle.message("title.scope.based");
83 private boolean mySomeSchemesDeleted = false;
84 private Map<ColorAndFontPanelFactory, InnerSearchableConfigurable> mySubPanelFactories;
86 private SchemesPanel myRootSchemesPanel;
88 private boolean myInitResetCompleted = false;
89 private boolean myInitResetInvoked = false;
91 private boolean myRevertChangesCompleted = false;
93 private boolean myApplyCompleted = false;
94 private boolean myDisposeCompleted = false;
95 private final Disposable myDisposable = Disposer.newDisposable();
97 public ColorAndFontGlobalState getColorAndFontGlobalState() {
98 return myColorAndFontGlobalState;
102 public boolean isModified() {
103 boolean listModified = isSchemeListModified();
104 boolean schemeModified = isSomeSchemeModified();
106 if (listModified || schemeModified) {
107 myApplyCompleted = false;
113 private boolean isSchemeListModified() {
114 if (mySomeSchemesDeleted) return true;
116 if (!mySelectedScheme.getName().equals(EditorColorsManager.getInstance().getGlobalScheme().getName())) return true;
118 for (MyColorScheme scheme : mySchemes.values()) {
119 if (scheme.isNew()) return true;
125 private boolean isSomeSchemeModified() {
126 for (MyColorScheme scheme : mySchemes.values()) {
127 if (scheme.isModified()) return true;
132 public EditorColorsScheme selectScheme(@NotNull String name) {
133 mySelectedScheme = getScheme(name);
134 return mySelectedScheme;
137 private MyColorScheme getScheme(String name) {
138 return mySchemes.get(name);
142 public String getUniqueName(@NotNull String preferredName) {
144 if (mySchemes.containsKey(preferredName)) {
145 for (int i = 1; ; i++) {
146 name = preferredName + " (" + i + ")";
147 if (!mySchemes.containsKey(name)) {
153 name = preferredName;
158 public EditorColorsScheme getSelectedScheme() {
159 return mySelectedScheme;
162 public EditorSchemeAttributeDescriptor[] getCurrentDescriptions() {
163 return mySelectedScheme.getDescriptors();
166 public static boolean isReadOnly(@NotNull final EditorColorsScheme scheme) {
167 return ((MyColorScheme)scheme).isReadOnly();
171 public String[] getSchemeNames() {
172 List<MyColorScheme> schemes = new ArrayList<>(mySchemes.values());
173 Collections.sort(schemes, (o1, o2) -> {
174 if (isReadOnly(o1) && !isReadOnly(o2)) return -1;
175 if (!isReadOnly(o1) && isReadOnly(o2)) return 1;
177 return o1.getName().compareToIgnoreCase(o2.getName());
180 List<String> names = new ArrayList<>(schemes.size());
181 for (MyColorScheme scheme : schemes) {
182 names.add(scheme.getName());
185 return ArrayUtil.toStringArray(names);
189 public Collection<EditorColorsScheme> getSchemes() {
190 return new ArrayList<>(mySchemes.values());
193 public void saveSchemeAs(String name) {
194 MyColorScheme scheme = mySelectedScheme;
195 if (scheme == null) return;
197 EditorColorsScheme clone = (EditorColorsScheme)scheme.getOriginalScheme().clone();
202 MyColorScheme newScheme = new MyColorScheme(clone);
203 initScheme(myColorAndFontGlobalState, newScheme);
205 newScheme.setIsNew();
207 mySchemes.put(name, newScheme);
208 selectScheme(newScheme.getName());
209 resetSchemesCombo(null);
212 public void addImportedScheme(@NotNull EditorColorsScheme imported) {
213 MyColorScheme newScheme = new MyColorScheme(imported);
214 initScheme(myColorAndFontGlobalState, newScheme);
216 mySchemes.put(imported.getName(), newScheme);
217 selectScheme(newScheme.getName());
218 resetSchemesCombo(null);
221 public void removeScheme(String name) {
222 if (mySelectedScheme.getName().equals(name)) {
223 //noinspection HardCodedStringLiteral
224 selectScheme("Default");
227 boolean deletedNewlyCreated = false;
229 MyColorScheme toDelete = mySchemes.get(name);
231 if (toDelete != null) {
232 deletedNewlyCreated = toDelete.isNew();
235 mySchemes.remove(name);
236 resetSchemesCombo(null);
237 mySomeSchemesDeleted = mySomeSchemesDeleted || !deletedNewlyCreated;
241 public void apply() throws ConfigurationException {
242 if (myApplyCompleted) {
247 myColorAndFontGlobalState.apply();
249 EditorColorsManager myColorsManager = EditorColorsManager.getInstance();
250 SchemeManager<EditorColorsScheme> schemeManager = ((EditorColorsManagerImpl)myColorsManager).getSchemeManager();
252 List<EditorColorsScheme> result = new ArrayList<>(mySchemes.values().size());
253 boolean activeSchemeModified = false;
254 EditorColorsScheme activeOriginalScheme = mySelectedScheme.getOriginalScheme();
255 for (MyColorScheme scheme : mySchemes.values()) {
256 if (!activeSchemeModified && activeOriginalScheme == scheme.getOriginalScheme()) {
257 activeSchemeModified = scheme.isModified();
260 if (!scheme.isDefault()) {
263 result.add(scheme.getOriginalScheme());
266 // refresh only if scheme is not switched
267 boolean refreshEditors = activeSchemeModified && schemeManager.getCurrentScheme() == activeOriginalScheme;
268 schemeManager.setSchemes(result, activeOriginalScheme);
269 if (refreshEditors) {
270 EditorColorsManagerImpl.schemeChangedOrSwitched();
273 final boolean isEditorThemeDark = ColorUtil.isDark(activeOriginalScheme.getDefaultBackground());
274 changeLafIfNecessary(isEditorThemeDark);
279 myApplyCompleted = true;
283 private static void changeLafIfNecessary(boolean isDarkEditorTheme) {
284 String propKey = "change.laf.on.editor.theme.change";
285 String value = PropertiesComponent.getInstance().getValue(propKey);
286 if ("false".equals(value)) return;
287 boolean applyAlways = "true".equals(value);
288 DialogWrapper.DoNotAskOption doNotAskOption = new DialogWrapper.DoNotAskOption.Adapter() {
290 public void rememberChoice(boolean isSelected, int exitCode) {
292 PropertiesComponent.getInstance().setValue(propKey, Boolean.toString(exitCode == Messages.YES));
297 public boolean shouldSaveOptionsOnCancel() {
302 final String productName = ApplicationNamesInfo.getInstance().getFullProductName();
303 final LafManager lafManager = LafManager.getInstance();
304 if (isDarkEditorTheme && !UIUtil.isUnderDarcula()) {
305 if (applyAlways || Messages.showYesNoDialog(
306 "Looks like you have set a dark editor theme. Would you like to set dark theme for entire " + productName,
307 "Change " + productName + " theme", Messages.YES_BUTTON, Messages.NO_BUTTON,
308 Messages.getQuestionIcon(), doNotAskOption) == Messages.YES) {
309 lafManager.setCurrentLookAndFeel(new DarculaLookAndFeelInfo());
310 lafManager.updateUI();
311 //noinspection SSBasedInspection
312 SwingUtilities.invokeLater(DarculaInstaller::install);
314 } else if (!isDarkEditorTheme && UIUtil.isUnderDarcula()) {
316 if (lafManager instanceof LafManagerImpl
318 (applyAlways || Messages.showYesNoDialog(
319 "Looks like you have set a bright editor theme. Would you like to set bright theme for entire " + productName,
320 "Change " + productName + " theme", Messages.YES_BUTTON, Messages.NO_BUTTON,
321 Messages.getQuestionIcon(), doNotAskOption) == Messages.YES)) {
322 lafManager.setCurrentLookAndFeel(((LafManagerImpl)lafManager).getDefaultLaf());
323 lafManager.updateUI();
324 //noinspection SSBasedInspection
325 SwingUtilities.invokeLater(DarculaInstaller::uninstall);
330 private boolean myIsReset = false;
332 private void resetSchemesCombo(Object source) {
335 myRootSchemesPanel.resetSchemesCombo(source);
336 if (mySubPanelFactories != null) {
337 for (NewColorAndFontPanel subPartialConfigurable : getPanels()) {
338 subPartialConfigurable.reset(source);
348 public JComponent createComponent() {
349 if (myRootSchemesPanel == null) {
350 ensureSchemesPanel();
352 return myRootSchemesPanel;
356 public boolean hasOwnContent() {
362 public Configurable[] buildConfigurables() {
363 myDisposeCompleted = false;
366 List<ColorAndFontPanelFactory> panelFactories = createPanelFactories();
368 List<Configurable> result = new ArrayList<>();
369 mySubPanelFactories = new LinkedHashMap<>(panelFactories.size());
370 for (ColorAndFontPanelFactory panelFactory : panelFactories) {
371 mySubPanelFactories.put(panelFactory, new InnerSearchableConfigurable(panelFactory));
374 result.addAll(new ArrayList<SearchableConfigurable>(mySubPanelFactories.values()));
375 return result.toArray(new Configurable[result.size()]);
379 private Set<NewColorAndFontPanel> getPanels() {
380 Set<NewColorAndFontPanel> result = new HashSet<>();
381 for (InnerSearchableConfigurable configurable : mySubPanelFactories.values()) {
382 NewColorAndFontPanel panel = configurable.getSubPanelIfInitialized();
390 protected List<ColorAndFontPanelFactory> createPanelFactories() {
391 List<ColorAndFontPanelFactory> result = new ArrayList<>();
392 result.add(new FontConfigurableFactory());
394 List<ColorAndFontPanelFactory> extensions = new ArrayList<>();
395 extensions.add(new ConsoleFontConfigurableFactory());
396 ColorSettingsPage[] pages = ColorSettingsPages.getInstance().getRegisteredPages();
397 for (final ColorSettingsPage page : pages) {
398 extensions.add(new ColorAndFontPanelFactoryEx() {
401 public NewColorAndFontPanel createPanel(@NotNull ColorAndFontOptions options) {
402 final SimpleEditorPreview preview = new SimpleEditorPreview(options, page);
403 return NewColorAndFontPanel.create(preview, page.getDisplayName(), options, null, page);
408 public String getPanelDisplayName() {
409 return page.getDisplayName();
413 public DisplayPriority getPriority() {
414 if (page instanceof DisplayPrioritySortable) {
415 return ((DisplayPrioritySortable)page).getPriority();
417 return DisplayPriority.LANGUAGE_SETTINGS;
421 Collections.addAll(extensions, Extensions.getExtensions(ColorAndFontPanelFactory.EP_NAME));
422 Collections.sort(extensions, (f1, f2) -> {
423 if (f1 instanceof DisplayPrioritySortable) {
424 if (f2 instanceof DisplayPrioritySortable) {
425 int result1 = ((DisplayPrioritySortable)f1).getPriority().compareTo(((DisplayPrioritySortable)f2).getPriority());
426 if (result1 != 0) return result1;
432 else if (f2 instanceof DisplayPrioritySortable) {
435 return f1.getPanelDisplayName().compareToIgnoreCase(f2.getPanelDisplayName());
437 result.addAll(extensions);
439 result.add(new FileStatusColorsPageFactory());
440 result.add(new ScopeColorsPageFactory());
445 private static class FontConfigurableFactory implements ColorAndFontPanelFactory {
448 public NewColorAndFontPanel createPanel(@NotNull ColorAndFontOptions options) {
449 FontEditorPreview previewPanel = new FontEditorPreview(options, true);
450 return new NewColorAndFontPanel(new SchemesPanel(options), new FontOptions(options), previewPanel, "Font", null, null){
452 public boolean containsFontOptions() {
460 public String getPanelDisplayName() {
465 private static class ConsoleFontConfigurableFactory implements ColorAndFontPanelFactoryEx {
468 public NewColorAndFontPanel createPanel(@NotNull ColorAndFontOptions options) {
469 FontEditorPreview previewPanel = new FontEditorPreview(options, false) {
471 protected EditorColorsScheme updateOptionsScheme(EditorColorsScheme selectedScheme) {
472 return ConsoleViewUtil.updateConsoleColorScheme(selectedScheme);
475 return new NewColorAndFontPanel(new SchemesPanel(options), new ConsoleFontOptions(options), previewPanel, "Font", null, null){
477 public boolean containsFontOptions() {
485 public String getPanelDisplayName() {
486 return "Console Font";
491 public DisplayPriority getPriority() {
492 return DisplayPriority.COMMON_SETTINGS;
496 private void initAll() {
497 myColorAndFontGlobalState.reset();
498 mySchemes = new THashMap<>();
499 for (EditorColorsScheme allScheme : EditorColorsManager.getInstance().getAllSchemes()) {
500 MyColorScheme schemeDelegate = new MyColorScheme(allScheme);
501 initScheme(myColorAndFontGlobalState, schemeDelegate);
502 mySchemes.put(schemeDelegate.getName(), schemeDelegate);
505 mySelectedScheme = mySchemes.get(EditorColorsManager.getInstance().getGlobalScheme().getName());
506 assert mySelectedScheme != null : EditorColorsManager.getInstance().getGlobalScheme().getName() + "; myschemes=" + mySchemes;
509 private static void initScheme(@NotNull ColorAndFontGlobalState colorAndFontGlobalState, @NotNull MyColorScheme scheme) {
510 List<EditorSchemeAttributeDescriptor> descriptions = new ArrayList<>();
511 initPluggedDescriptions(colorAndFontGlobalState, descriptions, scheme);
512 initFileStatusDescriptors(descriptions, scheme);
513 initScopesDescriptors(descriptions, scheme);
515 scheme.setDescriptors(descriptions.toArray(new EditorSchemeAttributeDescriptor[descriptions.size()]));
518 private static void initPluggedDescriptions(@NotNull ColorAndFontGlobalState colorAndFontGlobalState,
519 @NotNull List<EditorSchemeAttributeDescriptor> descriptions,
520 @NotNull MyColorScheme scheme) {
521 ColorSettingsPage[] pages = ColorSettingsPages.getInstance().getRegisteredPages();
522 for (ColorSettingsPage page : pages) {
523 initDescriptions(colorAndFontGlobalState, page, descriptions, scheme);
525 for (ColorAndFontDescriptorsProvider provider : Extensions.getExtensions(ColorAndFontDescriptorsProvider.EP_NAME)) {
526 initDescriptions(colorAndFontGlobalState, provider, descriptions, scheme);
530 private static void initDescriptions(@NotNull ColorAndFontGlobalState colorAndFontGlobalState,
531 @NotNull ColorAndFontDescriptorsProvider provider,
532 @NotNull List<EditorSchemeAttributeDescriptor> descriptions,
533 @NotNull MyColorScheme scheme) {
534 String group = provider.getDisplayName();
535 List<AttributesDescriptor> attributeDescriptors = ColorSettingsUtil.getAllAttributeDescriptors(provider);
536 //todo: single point configuration?
537 if (provider instanceof RainbowColorSettingsPage) {
538 descriptions.add(new RainbowAttributeDescriptor(((RainbowColorSettingsPage)provider).getLanguage(),
539 colorAndFontGlobalState,
541 ApplicationBundle.message("rainbow.option.panel.display.name"),
543 scheme.getRainbowState()));
545 for (AttributesDescriptor descriptor : attributeDescriptors) {
546 addSchemedDescription(descriptions, descriptor.getDisplayName(), group, descriptor.getKey(), scheme, null, null);
547 // if (provider instanceof RainbowColorSettingsPage
548 // && ((RainbowColorSettingsPage)provider).isRainbowType(descriptor.getKey())) {
549 // //todo: joined sub-descriptor?
550 // descriptions.add(new RainbowAttributeDescriptor(group,
551 // descriptor.getDisplayName()
552 // + EditorSchemeAttributeDescriptorWithPath.NAME_SEPARATOR
553 // + ApplicationBundle.message("rainbow.option.panel.display.name"),
555 // scheme.getInitRainbowState(),
556 // scheme.getCurrentRainbowState()));
560 ColorDescriptor[] colorDescriptors = provider.getColorDescriptors();
561 for (ColorDescriptor descriptor : colorDescriptors) {
562 ColorKey back = descriptor.getKind() == ColorDescriptor.Kind.BACKGROUND ? descriptor.getKey() : null;
563 ColorKey fore = descriptor.getKind() == ColorDescriptor.Kind.FOREGROUND ? descriptor.getKey() : null;
564 addEditorSettingDescription(descriptions, descriptor.getDisplayName(), group, back, fore, scheme);
568 private static void initFileStatusDescriptors(@NotNull List<EditorSchemeAttributeDescriptor> descriptions, MyColorScheme scheme) {
570 FileStatus[] statuses = FileStatusFactory.getInstance().getAllFileStatuses();
572 for (FileStatus fileStatus : statuses) {
573 addEditorSettingDescription(descriptions,
574 fileStatus.getText(),
577 fileStatus.getColorKey(),
582 private static void initScopesDescriptors(@NotNull List<EditorSchemeAttributeDescriptor> descriptions, @NotNull MyColorScheme scheme) {
583 Set<Pair<NamedScope,NamedScopesHolder>> namedScopes = new THashSet<>(new TObjectHashingStrategy<Pair<NamedScope, NamedScopesHolder>>() {
585 public int computeHashCode(@NotNull final Pair<NamedScope, NamedScopesHolder> object) {
586 return object.getFirst().getName().hashCode();
590 public boolean equals(@NotNull final Pair<NamedScope, NamedScopesHolder> o1, @NotNull final Pair<NamedScope, NamedScopesHolder> o2) {
591 return o1.getFirst().getName().equals(o2.getFirst().getName());
594 Project[] projects = ProjectManager.getInstance().getOpenProjects();
595 for (Project project : projects) {
596 DependencyValidationManagerImpl validationManager = (DependencyValidationManagerImpl)DependencyValidationManager.getInstance(project);
597 List<Pair<NamedScope,NamedScopesHolder>> cachedScopes = validationManager.getScopeBasedHighlightingCachedScopes();
598 namedScopes.addAll(cachedScopes);
601 List<Pair<NamedScope, NamedScopesHolder>> list = new ArrayList<>(namedScopes);
603 Collections.sort(list, (o1, o2) -> o1.getFirst().getName().compareToIgnoreCase(o2.getFirst().getName()));
604 for (Pair<NamedScope,NamedScopesHolder> pair : list) {
605 NamedScope namedScope = pair.getFirst();
606 String name = namedScope.getName();
607 TextAttributesKey textAttributesKey = ScopeAttributesUtil.getScopeTextAttributeKey(name);
608 if (scheme.getAttributes(textAttributesKey) == null) {
609 scheme.setAttributes(textAttributesKey, new TextAttributes());
611 NamedScopesHolder holder = pair.getSecond();
613 PackageSet value = namedScope.getValue();
614 String toolTip = holder.getDisplayName() + (value==null ? "" : ": "+ value.getText());
615 addSchemedDescription(descriptions,
619 scheme, holder.getIcon(), toolTip);
624 private static String calcType(@Nullable ColorKey backgroundKey, @Nullable ColorKey foregroundKey) {
625 if (foregroundKey != null) {
626 return foregroundKey.getExternalName();
628 else if (backgroundKey != null) {
629 return backgroundKey.getExternalName();
634 private static void addEditorSettingDescription(@NotNull List<EditorSchemeAttributeDescriptor> list,
637 @Nullable ColorKey backgroundKey,
638 @Nullable ColorKey foregroundKey,
639 @NotNull EditorColorsScheme scheme) {
640 list.add(new EditorSettingColorDescription(name, group, backgroundKey, foregroundKey, calcType(backgroundKey, foregroundKey), scheme));
643 private static void addSchemedDescription(@NotNull List<EditorSchemeAttributeDescriptor> list,
646 @NotNull TextAttributesKey key,
647 @NotNull MyColorScheme scheme,
650 list.add(new SchemeTextAttributesDescription(name, group, key, scheme, icon, toolTip));
654 public String getDisplayName() {
655 return ApplicationBundle.message("title.colors.and.fonts");
658 private void revertChanges(){
659 if (isSchemeListModified() || isSomeSchemeModified()) {
660 myRevertChangesCompleted = false;
663 if (!myRevertChangesCompleted) {
664 ensureSchemesPanel();
671 myRevertChangesCompleted = true;
677 private void resetImpl() {
678 mySomeSchemesDeleted = false;
680 resetSchemesCombo(null);
684 public synchronized void reset() {
685 if (!myInitResetInvoked) {
687 if (!myInitResetCompleted) {
688 ensureSchemesPanel();
694 myInitResetCompleted = true;
699 myInitResetInvoked = true;
707 public synchronized void resetFromChild() {
708 if (!myInitResetCompleted) {
709 ensureSchemesPanel();
716 myInitResetCompleted = true;
722 private void ensureSchemesPanel() {
723 if (myRootSchemesPanel == null) {
724 myRootSchemesPanel = new SchemesPanel(this);
726 myRootSchemesPanel.addListener(new ColorAndFontSettingsListener.Abstract(){
728 public void schemeChanged(final Object source) {
730 resetSchemesCombo(source);
739 public void disposeUIResources() {
741 if (!myDisposeCompleted) {
743 super.disposeUIResources();
744 Disposer.dispose(myDisposable);
747 myDisposeCompleted = true;
752 mySubPanelFactories = null;
754 myInitResetCompleted = false;
755 myInitResetInvoked = false;
756 myRevertChangesCompleted = false;
758 myApplyCompleted = false;
759 myRootSchemesPanel = null;
763 private static class SchemeTextAttributesDescription extends TextAttributesDescription {
764 @NotNull private final TextAttributes myInitialAttributes;
765 @NotNull private final TextAttributesKey key;
766 private TextAttributes myFallbackAttributes;
767 private Pair<ColorSettingsPage,AttributesDescriptor> myBaseAttributeDescriptor;
768 private boolean myIsInheritedInitial = false;
770 private SchemeTextAttributesDescription(String name, String group, @NotNull TextAttributesKey key, @NotNull MyColorScheme scheme, Icon icon,
773 getInitialAttributes(scheme, key).clone(),
774 key, scheme, icon, toolTip);
776 myInitialAttributes = getInitialAttributes(scheme, key);
777 TextAttributesKey fallbackKey = key.getFallbackAttributeKey();
778 if (fallbackKey != null) {
779 myFallbackAttributes = scheme.getAttributes(fallbackKey);
780 myBaseAttributeDescriptor = ColorSettingsPages.getInstance().getAttributeDescriptor(fallbackKey);
781 if (myBaseAttributeDescriptor == null) {
782 myBaseAttributeDescriptor =
783 new Pair<>(null, new AttributesDescriptor(fallbackKey.getExternalName(), fallbackKey));
786 myIsInheritedInitial = scheme.isInherited(key);
787 setInherited(myIsInheritedInitial);
788 if (myIsInheritedInitial) {
789 setInheritedAttributes(getTextAttributes());
795 private void setInheritedAttributes(@NotNull TextAttributes attributes) {
796 attributes.setFontType(myFallbackAttributes.getFontType());
797 attributes.setForegroundColor(myFallbackAttributes.getForegroundColor());
798 attributes.setBackgroundColor(myFallbackAttributes.getBackgroundColor());
799 attributes.setErrorStripeColor(myFallbackAttributes.getErrorStripeColor());
800 attributes.setEffectColor(myFallbackAttributes.getEffectColor());
801 attributes.setEffectType(myFallbackAttributes.getEffectType());
806 private static TextAttributes getInitialAttributes(@NotNull MyColorScheme scheme, @NotNull TextAttributesKey key) {
807 TextAttributes attributes = scheme.getAttributes(key);
808 return attributes != null ? attributes : new TextAttributes();
812 public void apply(EditorColorsScheme scheme) {
813 if (scheme == null) scheme = getScheme();
814 scheme.setAttributes(key, isInherited() ? new TextAttributes() : getTextAttributes());
818 public boolean isModified() {
820 return !myIsInheritedInitial;
822 return !Comparing.equal(myInitialAttributes, getTextAttributes()) || myIsInheritedInitial;
826 public boolean isErrorStripeEnabled() {
832 public TextAttributes getBaseAttributes() {
833 return myFallbackAttributes;
838 public Pair<ColorSettingsPage,AttributesDescriptor> getBaseAttributeDescriptor() {
839 return myBaseAttributeDescriptor;
843 public void setInherited(boolean isInherited) {
844 super.setInherited(isInherited);
848 private static class GetSetColor {
849 private final ColorKey myKey;
850 private final EditorColorsScheme myScheme;
851 private final Color myInitialColor;
852 private Color myColor;
854 private GetSetColor(ColorKey key, EditorColorsScheme scheme) {
857 myColor = myScheme.getColor(myKey);
858 myInitialColor = myColor;
861 public Color getColor() {
865 public void setColor(Color col) {
866 if (getColor() == null || !getColor().equals(col)) {
871 public void apply(EditorColorsScheme scheme) {
872 if (scheme == null) scheme = myScheme;
873 scheme.setColor(myKey, myColor);
876 public boolean isModified() {
877 return !Comparing.equal(myColor, myInitialColor);
881 private static class EditorSettingColorDescription extends ColorAndFontDescription {
882 private GetSetColor myGetSetForeground;
883 private GetSetColor myGetSetBackground;
885 private EditorSettingColorDescription(String name,
887 ColorKey backgroundKey,
888 ColorKey foregroundKey,
890 EditorColorsScheme scheme) {
891 super(name, group, type, scheme, null, null);
892 if (backgroundKey != null) {
893 myGetSetBackground = new GetSetColor(backgroundKey, scheme);
895 if (foregroundKey != null) {
896 myGetSetForeground = new GetSetColor(foregroundKey, scheme);
902 public int getFontType() {
907 public void setFontType(int type) {
911 public Color getExternalEffectColor() {
916 public void setExternalEffectColor(Color color) {
920 public void setExternalEffectType(EffectType type) {
925 public EffectType getExternalEffectType() {
926 return EffectType.LINE_UNDERSCORE;
930 public Color getExternalForeground() {
931 if (myGetSetForeground == null) {
934 return myGetSetForeground.getColor();
938 public void setExternalForeground(Color col) {
939 if (myGetSetForeground == null) {
942 myGetSetForeground.setColor(col);
946 public Color getExternalBackground() {
947 if (myGetSetBackground == null) {
950 return myGetSetBackground.getColor();
954 public void setExternalBackground(Color col) {
955 if (myGetSetBackground == null) {
958 myGetSetBackground.setColor(col);
962 public Color getExternalErrorStripe() {
967 public void setExternalErrorStripe(Color col) {
971 public boolean isFontEnabled() {
976 public boolean isForegroundEnabled() {
977 return myGetSetForeground != null;
981 public boolean isBackgroundEnabled() {
982 return myGetSetBackground != null;
986 public boolean isEffectsColorEnabled() {
991 public boolean isModified() {
992 return myGetSetBackground != null && myGetSetBackground.isModified()
993 || myGetSetForeground != null && myGetSetForeground.isModified();
997 public void apply(EditorColorsScheme scheme) {
998 if (myGetSetBackground != null) {
999 myGetSetBackground.apply(scheme);
1001 if (myGetSetForeground != null) {
1002 myGetSetForeground.apply(scheme);
1009 public String getHelpTopic() {
1013 private static class MyColorScheme extends EditorColorsSchemeImpl {
1014 private EditorSchemeAttributeDescriptor[] myDescriptors;
1015 private String myName;
1016 private boolean myIsNew = false;
1017 private RainbowColorsInSchemeState myRainbowState;
1019 private MyColorScheme(@NotNull EditorColorsScheme parentScheme) {
1020 super(parentScheme);
1022 parentScheme.getFontPreferences().copyTo(getFontPreferences());
1023 setLineSpacing(parentScheme.getLineSpacing());
1025 parentScheme.getConsoleFontPreferences().copyTo(getConsoleFontPreferences());
1026 setConsoleLineSpacing(parentScheme.getConsoleLineSpacing());
1028 setQuickDocFontSize(parentScheme.getQuickDocFontSize());
1029 myName = parentScheme.getName();
1035 public String getName() {
1040 public void setName(@NotNull String name) {
1044 public void setDescriptors(EditorSchemeAttributeDescriptor[] descriptors) {
1045 myDescriptors = descriptors;
1048 public EditorSchemeAttributeDescriptor[] getDescriptors() {
1049 return myDescriptors;
1052 public boolean isDefault() {
1053 return myParentScheme instanceof DefaultColorsScheme;
1057 public boolean isReadOnly() {
1058 return myParentScheme instanceof ReadOnlyColorsScheme;
1061 public boolean isModified() {
1062 if (isFontModified() || isConsoleFontModified()) return true;
1064 for (EditorSchemeAttributeDescriptor descriptor : myDescriptors) {
1065 if (descriptor.isModified()) {
1073 private boolean isFontModified() {
1074 if (!getFontPreferences().equals(myParentScheme.getFontPreferences())) return true;
1075 if (getLineSpacing() != myParentScheme.getLineSpacing()) return true;
1076 return getQuickDocFontSize() != myParentScheme.getQuickDocFontSize();
1079 private boolean isConsoleFontModified() {
1080 if (!getConsoleFontPreferences().equals(myParentScheme.getConsoleFontPreferences())) return true;
1081 return getConsoleLineSpacing() != myParentScheme.getConsoleLineSpacing();
1084 public void apply() {
1085 if (!(myParentScheme instanceof ReadOnlyColorsScheme)) {
1086 apply(myParentScheme);
1090 public void apply(@NotNull EditorColorsScheme scheme) {
1091 scheme.setFontPreferences(getFontPreferences());
1092 scheme.setLineSpacing(myLineSpacing);
1093 scheme.setQuickDocFontSize(getQuickDocFontSize());
1094 scheme.setConsoleFontPreferences(getConsoleFontPreferences());
1095 scheme.setConsoleLineSpacing(getConsoleLineSpacing());
1097 for (EditorSchemeAttributeDescriptor descriptor : myDescriptors) {
1098 descriptor.apply(scheme);
1101 if (scheme instanceof AbstractColorsScheme) {
1102 ((AbstractColorsScheme)scheme).setSaveNeeded(true);
1107 public Object clone() {
1112 public EditorColorsScheme getOriginalScheme() {
1113 return myParentScheme;
1116 public void setIsNew() {
1120 public boolean isNew() {
1126 public String toString() {
1127 return "temporary scheme for " + myName;
1130 public boolean isInherited(TextAttributesKey key) {
1131 TextAttributesKey fallbackKey = key.getFallbackAttributeKey();
1132 if (fallbackKey != null) {
1133 if (myParentScheme instanceof AbstractColorsScheme) {
1134 TextAttributes ownAttrs = ((AbstractColorsScheme)myParentScheme).getDirectlyDefinedAttributes(key);
1135 if (ownAttrs != null) {
1136 return ownAttrs.isFallbackEnabled();
1139 TextAttributes attributes = getAttributes(key);
1140 if (attributes != null) {
1141 TextAttributes fallbackAttributes = getAttributes(fallbackKey);
1142 return attributes == fallbackAttributes;
1148 public RainbowColorsInSchemeState getRainbowState() {
1149 if (myRainbowState == null) {
1150 myRainbowState = new RainbowColorsInSchemeState(this);
1152 return myRainbowState;
1158 public String getId() {
1159 return getHelpTopic();
1163 public SearchableConfigurable findSubConfigurable(@NotNull Class pageClass) {
1164 if (mySubPanelFactories == null) {
1165 buildConfigurables();
1167 for (Map.Entry<ColorAndFontPanelFactory, InnerSearchableConfigurable> entry : mySubPanelFactories.entrySet()) {
1168 if (pageClass.isInstance(entry.getValue().createPanel().getSettingsPage())) {
1169 return entry.getValue();
1176 public SearchableConfigurable findSubConfigurable(String pageName) {
1177 if (mySubPanelFactories == null) {
1178 buildConfigurables();
1180 for (InnerSearchableConfigurable configurable : mySubPanelFactories.values()) {
1181 if (configurable.getDisplayName().equals(pageName)) {
1182 return configurable;
1189 public NewColorAndFontPanel findPage(String pageName) {
1190 InnerSearchableConfigurable child = (InnerSearchableConfigurable)findSubConfigurable(pageName);
1191 return child == null ? null : child.createPanel();
1194 private class InnerSearchableConfigurable implements SearchableConfigurable, OptionsContainingConfigurable, NoScroll {
1195 private NewColorAndFontPanel mySubPanel;
1196 private boolean mySubInitInvoked = false;
1197 @NotNull private final ColorAndFontPanelFactory myFactory;
1199 private InnerSearchableConfigurable(@NotNull ColorAndFontPanelFactory factory) {
1200 myFactory = factory;
1206 public String getDisplayName() {
1207 return myFactory.getPanelDisplayName();
1210 public NewColorAndFontPanel getSubPanelIfInitialized() {
1214 private NewColorAndFontPanel createPanel() {
1215 if (mySubPanel == null) {
1216 mySubPanel = myFactory.createPanel(ColorAndFontOptions.this);
1217 mySubPanel.reset(this);
1218 mySubPanel.addSchemesListener(new ColorAndFontSettingsListener.Abstract(){
1220 public void schemeChanged(final Object source) {
1222 resetSchemesCombo(source);
1227 mySubPanel.addDescriptionListener(new ColorAndFontSettingsListener.Abstract(){
1229 public void fontChanged() {
1230 for (NewColorAndFontPanel panel : getPanels()) {
1231 panel.updatePreview();
1240 public String getHelpTopic() {
1245 public JComponent createComponent() {
1246 return createPanel().getPanel();
1250 public boolean isModified() {
1252 for (MyColorScheme scheme : mySchemes.values()) {
1253 if (mySubPanel.containsFontOptions()) {
1254 if (scheme.isFontModified() || scheme.isConsoleFontModified()) {
1255 myRevertChangesCompleted = false;
1260 for (EditorSchemeAttributeDescriptor descriptor : scheme.getDescriptors()) {
1261 if (mySubPanel.contains(descriptor) && descriptor.isModified()) {
1262 myRevertChangesCompleted = false;
1275 public void apply() throws ConfigurationException {
1276 ColorAndFontOptions.this.apply();
1280 public void reset() {
1281 if (!mySubInitInvoked) {
1282 if (!myInitResetCompleted) {
1285 mySubInitInvoked = true;
1293 public void disposeUIResources() {
1294 if (mySubPanel != null) {
1295 mySubPanel.disposeUIResources();
1302 public String getId() {
1303 return ColorAndFontOptions.this.getId() + "." + getDisplayName();
1307 public Runnable enableSearch(final String option) {
1308 return createPanel().showOption(option);
1313 public Set<String> processListOptions() {
1314 return createPanel().processListOptions();
1320 public String toString() {
1321 return "Color And Fonts for "+getDisplayName();