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.diagnostic.Logger;
31 import com.intellij.openapi.editor.colors.ColorKey;
32 import com.intellij.openapi.editor.colors.EditorColorsManager;
33 import com.intellij.openapi.editor.colors.EditorColorsScheme;
34 import com.intellij.openapi.editor.colors.TextAttributesKey;
35 import com.intellij.openapi.editor.colors.impl.*;
36 import com.intellij.openapi.editor.markup.EffectType;
37 import com.intellij.openapi.editor.markup.TextAttributes;
38 import com.intellij.openapi.extensions.Extensions;
39 import com.intellij.openapi.options.Configurable;
40 import com.intellij.openapi.options.ConfigurationException;
41 import com.intellij.openapi.options.SchemesManager;
42 import com.intellij.openapi.options.SearchableConfigurable;
43 import com.intellij.openapi.options.colors.*;
44 import com.intellij.openapi.project.Project;
45 import com.intellij.openapi.project.ProjectManager;
46 import com.intellij.openapi.ui.DialogWrapper;
47 import com.intellij.openapi.ui.Messages;
48 import com.intellij.openapi.util.Comparing;
49 import com.intellij.openapi.util.Disposer;
50 import com.intellij.openapi.util.Pair;
51 import com.intellij.openapi.vcs.FileStatus;
52 import com.intellij.openapi.vcs.FileStatusFactory;
53 import com.intellij.packageDependencies.DependencyValidationManager;
54 import com.intellij.packageDependencies.DependencyValidationManagerImpl;
55 import com.intellij.psi.codeStyle.DisplayPriority;
56 import com.intellij.psi.codeStyle.DisplayPrioritySortable;
57 import com.intellij.psi.search.scope.packageSet.NamedScope;
58 import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
59 import com.intellij.psi.search.scope.packageSet.PackageSet;
60 import com.intellij.ui.ColorUtil;
61 import com.intellij.util.ArrayUtil;
62 import com.intellij.util.ui.UIUtil;
63 import gnu.trove.THashMap;
64 import gnu.trove.THashSet;
65 import gnu.trove.TObjectHashingStrategy;
66 import org.jetbrains.annotations.Nls;
67 import org.jetbrains.annotations.NonNls;
68 import org.jetbrains.annotations.NotNull;
69 import org.jetbrains.annotations.Nullable;
74 import java.util.List;
76 public class ColorAndFontOptions extends SearchableConfigurable.Parent.Abstract implements EditorOptionsProvider {
77 private static final Logger LOG = Logger.getInstance(ColorAndFontOptions.class);
79 public static final String ID = "reference.settingsdialog.IDE.editor.colors";
81 private Map<String, MyColorScheme> mySchemes;
82 private MyColorScheme mySelectedScheme;
83 public static final String FILE_STATUS_GROUP = ApplicationBundle.message("title.file.status");
84 public static final String SCOPES_GROUP = ApplicationBundle.message("title.scope.based");
86 private boolean mySomeSchemesDeleted = false;
87 private Map<ColorAndFontPanelFactory, InnerSearchableConfigurable> mySubPanelFactories;
89 private SchemesPanel myRootSchemesPanel;
91 private boolean myInitResetCompleted = false;
92 private boolean myInitResetInvoked = false;
94 private boolean myRevertChangesCompleted = false;
96 private boolean myApplyCompleted = false;
97 private boolean myDisposeCompleted = false;
98 private final Disposable myDisposable = Disposer.newDisposable();
101 public boolean isModified() {
102 boolean listModified = isSchemeListModified();
103 boolean schemeModified = isSomeSchemeModified();
105 if (listModified || schemeModified) {
106 myApplyCompleted = false;
112 private boolean isSchemeListModified(){
113 if (mySomeSchemesDeleted) return true;
115 if (!mySelectedScheme.getName().equals(EditorColorsManager.getInstance().getGlobalScheme().getName())) return true;
117 for (MyColorScheme scheme : mySchemes.values()) {
118 if (scheme.isNew()) return true;
124 private boolean isSomeSchemeModified() {
125 for (MyColorScheme scheme : mySchemes.values()) {
126 if (scheme.isModified()) return true;
131 public EditorColorsScheme selectScheme(@NotNull String name) {
132 mySelectedScheme = getScheme(name);
133 return mySelectedScheme;
136 private MyColorScheme getScheme(String name) {
137 return mySchemes.get(name);
141 public String getUniqueName(@NotNull String preferredName) {
143 if (mySchemes.containsKey(preferredName)) {
144 for (int i = 1; ; i++) {
145 name = preferredName + " (" + i + ")";
146 if (!mySchemes.containsKey(name)) {
152 name = preferredName;
157 public EditorColorsScheme getSelectedScheme() {
158 return mySelectedScheme;
161 public EditorSchemeAttributeDescriptor[] getCurrentDescriptions() {
162 return mySelectedScheme.getDescriptors();
165 public static boolean isReadOnly(@NotNull final EditorColorsScheme scheme) {
166 return ((MyColorScheme)scheme).isReadOnly();
170 public String[] getSchemeNames() {
171 List<MyColorScheme> schemes = new ArrayList<MyColorScheme>(mySchemes.values());
172 Collections.sort(schemes, (o1, o2) -> {
173 if (isReadOnly(o1) && !isReadOnly(o2)) return -1;
174 if (!isReadOnly(o1) && isReadOnly(o2)) return 1;
176 return o1.getName().compareToIgnoreCase(o2.getName());
179 List<String> names = new ArrayList<String>(schemes.size());
180 for (MyColorScheme scheme : schemes) {
181 names.add(scheme.getName());
184 return ArrayUtil.toStringArray(names);
188 public Collection<EditorColorsScheme> getSchemes() {
189 return new ArrayList<EditorColorsScheme>(mySchemes.values());
192 public void saveSchemeAs(String name) {
193 MyColorScheme scheme = mySelectedScheme;
194 if (scheme == null) return;
196 EditorColorsScheme clone = (EditorColorsScheme)scheme.getOriginalScheme().clone();
201 MyColorScheme newScheme = new MyColorScheme(clone);
202 initScheme(newScheme);
204 newScheme.setIsNew();
206 mySchemes.put(name, newScheme);
207 selectScheme(newScheme.getName());
208 resetSchemesCombo(null);
211 public void addImportedScheme(@NotNull EditorColorsScheme imported) {
212 MyColorScheme newScheme = new MyColorScheme(imported);
213 initScheme(newScheme);
215 mySchemes.put(imported.getName(), newScheme);
216 selectScheme(newScheme.getName());
217 resetSchemesCombo(null);
220 public void removeScheme(String name) {
221 if (mySelectedScheme.getName().equals(name)) {
222 //noinspection HardCodedStringLiteral
223 selectScheme("Default");
226 boolean deletedNewlyCreated = false;
228 MyColorScheme toDelete = mySchemes.get(name);
230 if (toDelete != null) {
231 deletedNewlyCreated = toDelete.isNew();
234 mySchemes.remove(name);
235 resetSchemesCombo(null);
236 mySomeSchemesDeleted = mySomeSchemesDeleted || !deletedNewlyCreated;
240 public void apply() throws ConfigurationException {
241 if (myApplyCompleted) {
246 EditorColorsManager myColorsManager = EditorColorsManager.getInstance();
247 SchemesManager<EditorColorsScheme, EditorColorsSchemeImpl> schemeManager = ((EditorColorsManagerImpl)myColorsManager).getSchemeManager();
249 List<EditorColorsScheme> result = new ArrayList<EditorColorsScheme>(mySchemes.values().size());
250 boolean activeSchemeModified = false;
251 EditorColorsScheme activeOriginalScheme = mySelectedScheme.getOriginalScheme();
252 for (MyColorScheme scheme : mySchemes.values()) {
253 if (!activeSchemeModified && activeOriginalScheme == scheme.getOriginalScheme()) {
254 activeSchemeModified = scheme.isModified();
257 if (!scheme.isDefault()) {
260 result.add(scheme.getOriginalScheme());
263 // refresh only if scheme is not switched
264 boolean refreshEditors = activeSchemeModified && schemeManager.getCurrentScheme() == activeOriginalScheme;
265 schemeManager.setSchemes(result, activeOriginalScheme);
266 if (refreshEditors) {
267 EditorColorsManagerImpl.schemeChangedOrSwitched();
270 final boolean isEditorThemeDark = ColorUtil.isDark(activeOriginalScheme.getDefaultBackground());
271 changeLafIfNecessary(isEditorThemeDark);
276 myApplyCompleted = true;
280 private static void changeLafIfNecessary(boolean isDarkEditorTheme) {
281 String propKey = "change.laf.on.editor.theme.change";
282 String value = PropertiesComponent.getInstance().getValue(propKey);
283 if ("false".equals(value)) return;
284 boolean applyAlways = "true".equals(value);
285 DialogWrapper.DoNotAskOption doNotAskOption = new DialogWrapper.DoNotAskOption.Adapter() {
287 public void rememberChoice(boolean isSelected, int exitCode) {
289 PropertiesComponent.getInstance().setValue(propKey, Boolean.toString(exitCode == Messages.YES));
294 public boolean shouldSaveOptionsOnCancel() {
299 final String productName = ApplicationNamesInfo.getInstance().getFullProductName();
300 final LafManager lafManager = LafManager.getInstance();
301 if (isDarkEditorTheme && !UIUtil.isUnderDarcula()) {
302 if (applyAlways || Messages.showYesNoDialog(
303 "Looks like you have set a dark editor theme. Would you like to set dark theme for entire " + productName,
304 "Change " + productName + " theme", Messages.YES_BUTTON, Messages.NO_BUTTON,
305 Messages.getQuestionIcon(), doNotAskOption) == Messages.YES) {
306 lafManager.setCurrentLookAndFeel(new DarculaLookAndFeelInfo());
307 lafManager.updateUI();
308 //noinspection SSBasedInspection
309 SwingUtilities.invokeLater(DarculaInstaller::install);
311 } else if (!isDarkEditorTheme && UIUtil.isUnderDarcula()) {
313 if (lafManager instanceof LafManagerImpl
315 (applyAlways || Messages.showYesNoDialog(
316 "Looks like you have set a bright editor theme. Would you like to set bright theme for entire " + productName,
317 "Change " + productName + " theme", Messages.YES_BUTTON, Messages.NO_BUTTON,
318 Messages.getQuestionIcon(), doNotAskOption) == Messages.YES)) {
319 lafManager.setCurrentLookAndFeel(((LafManagerImpl)lafManager).getDefaultLaf());
320 lafManager.updateUI();
321 //noinspection SSBasedInspection
322 SwingUtilities.invokeLater(DarculaInstaller::uninstall);
327 private boolean myIsReset = false;
329 private void resetSchemesCombo(Object source) {
332 myRootSchemesPanel.resetSchemesCombo(source);
333 if (mySubPanelFactories != null) {
334 for (NewColorAndFontPanel subPartialConfigurable : getPanels()) {
335 subPartialConfigurable.reset(source);
345 public JComponent createComponent() {
346 if (myRootSchemesPanel == null) {
347 ensureSchemesPanel();
349 return myRootSchemesPanel;
353 public boolean hasOwnContent() {
359 public Configurable[] buildConfigurables() {
360 myDisposeCompleted = false;
363 List<ColorAndFontPanelFactory> panelFactories = createPanelFactories();
365 List<Configurable> result = new ArrayList<Configurable>();
366 mySubPanelFactories = new LinkedHashMap<ColorAndFontPanelFactory, InnerSearchableConfigurable>(panelFactories.size());
367 for (ColorAndFontPanelFactory panelFactory : panelFactories) {
368 mySubPanelFactories.put(panelFactory, new InnerSearchableConfigurable(panelFactory));
371 result.addAll(new ArrayList<SearchableConfigurable>(mySubPanelFactories.values()));
372 return result.toArray(new Configurable[result.size()]);
376 private Set<NewColorAndFontPanel> getPanels() {
377 Set<NewColorAndFontPanel> result = new HashSet<NewColorAndFontPanel>();
378 for (InnerSearchableConfigurable configurable : mySubPanelFactories.values()) {
379 NewColorAndFontPanel panel = configurable.getSubPanelIfInitialized();
387 protected List<ColorAndFontPanelFactory> createPanelFactories() {
388 List<ColorAndFontPanelFactory> result = new ArrayList<ColorAndFontPanelFactory>();
389 result.add(new FontConfigurableFactory());
391 List<ColorAndFontPanelFactory> extensions = new ArrayList<ColorAndFontPanelFactory>();
392 extensions.add(new ConsoleFontConfigurableFactory());
393 ColorSettingsPage[] pages = ColorSettingsPages.getInstance().getRegisteredPages();
394 for (final ColorSettingsPage page : pages) {
395 extensions.add(new ColorAndFontPanelFactoryEx() {
398 public NewColorAndFontPanel createPanel(@NotNull ColorAndFontOptions options) {
399 final SimpleEditorPreview preview = new SimpleEditorPreview(options, page);
400 return NewColorAndFontPanel.create(preview, page.getDisplayName(), options, null, page);
405 public String getPanelDisplayName() {
406 return page.getDisplayName();
410 public DisplayPriority getPriority() {
411 if (page instanceof DisplayPrioritySortable) {
412 return ((DisplayPrioritySortable)page).getPriority();
414 return DisplayPriority.LANGUAGE_SETTINGS;
418 Collections.addAll(extensions, Extensions.getExtensions(ColorAndFontPanelFactory.EP_NAME));
419 Collections.sort(extensions, (f1, f2) -> {
420 if (f1 instanceof DisplayPrioritySortable) {
421 if (f2 instanceof DisplayPrioritySortable) {
422 int result1 = ((DisplayPrioritySortable)f1).getPriority().compareTo(((DisplayPrioritySortable)f2).getPriority());
423 if (result1 != 0) return result1;
429 else if (f2 instanceof DisplayPrioritySortable) {
432 return f1.getPanelDisplayName().compareToIgnoreCase(f2.getPanelDisplayName());
434 result.addAll(extensions);
436 result.add(new FileStatusColorsPageFactory());
437 result.add(new ScopeColorsPageFactory());
442 private static class FontConfigurableFactory implements ColorAndFontPanelFactory {
445 public NewColorAndFontPanel createPanel(@NotNull ColorAndFontOptions options) {
446 FontEditorPreview previewPanel = new FontEditorPreview(options, true);
447 return new NewColorAndFontPanel(new SchemesPanel(options), new FontOptions(options), previewPanel, "Font", null, null){
449 public boolean containsFontOptions() {
457 public String getPanelDisplayName() {
462 private static class ConsoleFontConfigurableFactory implements ColorAndFontPanelFactoryEx {
465 public NewColorAndFontPanel createPanel(@NotNull ColorAndFontOptions options) {
466 FontEditorPreview previewPanel = new FontEditorPreview(options, false) {
468 protected EditorColorsScheme updateOptionsScheme(EditorColorsScheme selectedScheme) {
469 return ConsoleViewUtil.updateConsoleColorScheme(selectedScheme);
472 return new NewColorAndFontPanel(new SchemesPanel(options), new ConsoleFontOptions(options), previewPanel, "Font", null, null){
474 public boolean containsFontOptions() {
482 public String getPanelDisplayName() {
483 return "Console Font";
488 public DisplayPriority getPriority() {
489 return DisplayPriority.COMMON_SETTINGS;
493 private void initAll() {
494 mySchemes = new THashMap<String, MyColorScheme>();
495 for (EditorColorsScheme allScheme : EditorColorsManager.getInstance().getAllSchemes()) {
496 MyColorScheme schemeDelegate = new MyColorScheme(allScheme);
497 initScheme(schemeDelegate);
498 mySchemes.put(schemeDelegate.getName(), schemeDelegate);
501 mySelectedScheme = mySchemes.get(EditorColorsManager.getInstance().getGlobalScheme().getName());
502 assert mySelectedScheme != null : EditorColorsManager.getInstance().getGlobalScheme().getName() + "; myschemes=" + mySchemes;
505 private static void initScheme(@NotNull MyColorScheme scheme) {
506 List<EditorSchemeAttributeDescriptor> descriptions = new ArrayList<EditorSchemeAttributeDescriptor>();
507 initPluggedDescriptions(descriptions, scheme);
508 initFileStatusDescriptors(descriptions, scheme);
509 initScopesDescriptors(descriptions, scheme);
511 scheme.setDescriptors(descriptions.toArray(new EditorSchemeAttributeDescriptor[descriptions.size()]));
514 private static void initPluggedDescriptions(@NotNull List<EditorSchemeAttributeDescriptor> descriptions, @NotNull MyColorScheme scheme) {
515 ColorSettingsPage[] pages = ColorSettingsPages.getInstance().getRegisteredPages();
516 for (ColorSettingsPage page : pages) {
517 initDescriptions(page, descriptions, scheme);
519 for (ColorAndFontDescriptorsProvider provider : Extensions.getExtensions(ColorAndFontDescriptorsProvider.EP_NAME)) {
520 initDescriptions(provider, descriptions, scheme);
524 private static void initDescriptions(@NotNull ColorAndFontDescriptorsProvider provider,
525 @NotNull List<EditorSchemeAttributeDescriptor> descriptions,
526 @NotNull MyColorScheme scheme) {
527 String group = provider.getDisplayName();
528 List<AttributesDescriptor> attributeDescriptors = ColorSettingsUtil.getAllAttributeDescriptors(provider);
529 for (AttributesDescriptor descriptor : attributeDescriptors) {
530 addSchemedDescription(descriptions, descriptor.getDisplayName(), group, descriptor.getKey(), scheme, null, null);
533 ColorDescriptor[] colorDescriptors = provider.getColorDescriptors();
534 for (ColorDescriptor descriptor : colorDescriptors) {
535 ColorKey back = descriptor.getKind() == ColorDescriptor.Kind.BACKGROUND ? descriptor.getKey() : null;
536 ColorKey fore = descriptor.getKind() == ColorDescriptor.Kind.FOREGROUND ? descriptor.getKey() : null;
537 addEditorSettingDescription(descriptions, descriptor.getDisplayName(), group, back, fore, scheme);
541 private static void initFileStatusDescriptors(@NotNull List<EditorSchemeAttributeDescriptor> descriptions, MyColorScheme scheme) {
543 FileStatus[] statuses = FileStatusFactory.getInstance().getAllFileStatuses();
545 for (FileStatus fileStatus : statuses) {
546 addEditorSettingDescription(descriptions,
547 fileStatus.getText(),
550 fileStatus.getColorKey(),
555 private static void initScopesDescriptors(@NotNull List<EditorSchemeAttributeDescriptor> descriptions, @NotNull MyColorScheme scheme) {
556 Set<Pair<NamedScope,NamedScopesHolder>> namedScopes = new THashSet<Pair<NamedScope,NamedScopesHolder>>(new TObjectHashingStrategy<Pair<NamedScope,NamedScopesHolder>>() {
558 public int computeHashCode(@NotNull final Pair<NamedScope, NamedScopesHolder> object) {
559 return object.getFirst().getName().hashCode();
563 public boolean equals(@NotNull final Pair<NamedScope, NamedScopesHolder> o1, @NotNull final Pair<NamedScope, NamedScopesHolder> o2) {
564 return o1.getFirst().getName().equals(o2.getFirst().getName());
567 Project[] projects = ProjectManager.getInstance().getOpenProjects();
568 for (Project project : projects) {
569 DependencyValidationManagerImpl validationManager = (DependencyValidationManagerImpl)DependencyValidationManager.getInstance(project);
570 List<Pair<NamedScope,NamedScopesHolder>> cachedScopes = validationManager.getScopeBasedHighlightingCachedScopes();
571 namedScopes.addAll(cachedScopes);
574 List<Pair<NamedScope, NamedScopesHolder>> list = new ArrayList<Pair<NamedScope, NamedScopesHolder>>(namedScopes);
576 Collections.sort(list, (o1, o2) -> o1.getFirst().getName().compareToIgnoreCase(o2.getFirst().getName()));
577 for (Pair<NamedScope,NamedScopesHolder> pair : list) {
578 NamedScope namedScope = pair.getFirst();
579 String name = namedScope.getName();
580 TextAttributesKey textAttributesKey = ScopeAttributesUtil.getScopeTextAttributeKey(name);
581 if (scheme.getAttributes(textAttributesKey) == null) {
582 scheme.setAttributes(textAttributesKey, new TextAttributes());
584 NamedScopesHolder holder = pair.getSecond();
586 PackageSet value = namedScope.getValue();
587 String toolTip = holder.getDisplayName() + (value==null ? "" : ": "+ value.getText());
588 addSchemedDescription(descriptions,
592 scheme, holder.getIcon(), toolTip);
597 private static String calcType(@Nullable ColorKey backgroundKey, @Nullable ColorKey foregroundKey) {
598 if (foregroundKey != null) {
599 return foregroundKey.getExternalName();
601 else if (backgroundKey != null) {
602 return backgroundKey.getExternalName();
607 private static void addEditorSettingDescription(@NotNull List<EditorSchemeAttributeDescriptor> list,
610 @Nullable ColorKey backgroundKey,
611 @Nullable ColorKey foregroundKey,
612 @NotNull EditorColorsScheme scheme) {
613 list.add(new EditorSettingColorDescription(name, group, backgroundKey, foregroundKey, calcType(backgroundKey, foregroundKey), scheme));
616 private static void addSchemedDescription(@NotNull List<EditorSchemeAttributeDescriptor> list,
619 @NotNull TextAttributesKey key,
620 @NotNull MyColorScheme scheme,
623 list.add(new SchemeTextAttributesDescription(name, group, key, scheme, icon, toolTip));
627 public String getDisplayName() {
628 return ApplicationBundle.message("title.colors.and.fonts");
631 private void revertChanges(){
632 if (isSchemeListModified() || isSomeSchemeModified()) {
633 myRevertChangesCompleted = false;
636 if (!myRevertChangesCompleted) {
637 ensureSchemesPanel();
644 myRevertChangesCompleted = true;
650 private void resetImpl() {
651 mySomeSchemesDeleted = false;
653 resetSchemesCombo(null);
657 public synchronized void reset() {
658 if (!myInitResetInvoked) {
660 if (!myInitResetCompleted) {
661 ensureSchemesPanel();
667 myInitResetCompleted = true;
672 myInitResetInvoked = true;
680 public synchronized void resetFromChild() {
681 if (!myInitResetCompleted) {
682 ensureSchemesPanel();
689 myInitResetCompleted = true;
695 private void ensureSchemesPanel() {
696 if (myRootSchemesPanel == null) {
697 myRootSchemesPanel = new SchemesPanel(this);
699 myRootSchemesPanel.addListener(new ColorAndFontSettingsListener.Abstract(){
701 public void schemeChanged(final Object source) {
703 resetSchemesCombo(source);
712 public void disposeUIResources() {
714 if (!myDisposeCompleted) {
716 super.disposeUIResources();
717 Disposer.dispose(myDisposable);
720 myDisposeCompleted = true;
725 mySubPanelFactories = null;
727 myInitResetCompleted = false;
728 myInitResetInvoked = false;
729 myRevertChangesCompleted = false;
731 myApplyCompleted = false;
732 myRootSchemesPanel = null;
736 public boolean currentSchemeIsReadOnly() {
737 return isReadOnly(mySelectedScheme);
740 public boolean currentSchemeIsShared() {
741 return ColorSettingsUtil.isSharedScheme(mySelectedScheme);
745 private static class SchemeTextAttributesDescription extends TextAttributesDescription {
746 @NotNull private final TextAttributes myInitialAttributes;
747 @NotNull private final TextAttributesKey key;
748 private TextAttributes myFallbackAttributes;
749 private Pair<ColorSettingsPage,AttributesDescriptor> myBaseAttributeDescriptor;
750 private boolean myIsInheritedInitial = false;
752 private SchemeTextAttributesDescription(String name, String group, @NotNull TextAttributesKey key, @NotNull MyColorScheme scheme, Icon icon,
755 getInitialAttributes(scheme, key).clone(),
756 key, scheme, icon, toolTip);
758 myInitialAttributes = getInitialAttributes(scheme, key);
759 TextAttributesKey fallbackKey = key.getFallbackAttributeKey();
760 if (fallbackKey != null) {
761 myFallbackAttributes = scheme.getAttributes(fallbackKey);
762 myBaseAttributeDescriptor = ColorSettingsPages.getInstance().getAttributeDescriptor(fallbackKey);
763 if (myBaseAttributeDescriptor == null) {
764 myBaseAttributeDescriptor =
765 new Pair<ColorSettingsPage, AttributesDescriptor>(null, new AttributesDescriptor(fallbackKey.getExternalName(), fallbackKey));
768 myIsInheritedInitial = scheme.isInherited(key);
769 setInherited(myIsInheritedInitial);
770 if (myIsInheritedInitial) {
771 setInheritedAttributes(getTextAttributes());
777 private void setInheritedAttributes(@NotNull TextAttributes attributes) {
778 attributes.setFontType(myFallbackAttributes.getFontType());
779 attributes.setForegroundColor(myFallbackAttributes.getForegroundColor());
780 attributes.setBackgroundColor(myFallbackAttributes.getBackgroundColor());
781 attributes.setErrorStripeColor(myFallbackAttributes.getErrorStripeColor());
782 attributes.setEffectColor(myFallbackAttributes.getEffectColor());
783 attributes.setEffectType(myFallbackAttributes.getEffectType());
788 private static TextAttributes getInitialAttributes(@NotNull MyColorScheme scheme, @NotNull TextAttributesKey key) {
789 TextAttributes attributes = scheme.getAttributes(key);
790 return attributes != null ? attributes : new TextAttributes();
794 public void apply(EditorColorsScheme scheme) {
795 if (scheme == null) scheme = getScheme();
796 scheme.setAttributes(key, isInherited() ? new TextAttributes() : getTextAttributes());
800 public boolean isModified() {
802 return !myIsInheritedInitial;
804 return !Comparing.equal(myInitialAttributes, getTextAttributes()) || myIsInheritedInitial;
808 public boolean isErrorStripeEnabled() {
814 public TextAttributes getBaseAttributes() {
815 return myFallbackAttributes;
820 public Pair<ColorSettingsPage,AttributesDescriptor> getBaseAttributeDescriptor() {
821 return myBaseAttributeDescriptor;
825 public void setInherited(boolean isInherited) {
826 super.setInherited(isInherited);
830 private static class GetSetColor {
831 private final ColorKey myKey;
832 private final EditorColorsScheme myScheme;
833 private boolean isModified = false;
834 private Color myColor;
836 private GetSetColor(ColorKey key, EditorColorsScheme scheme) {
839 myColor = myScheme.getColor(myKey);
842 public Color getColor() {
846 public void setColor(Color col) {
847 if (getColor() == null || !getColor().equals(col)) {
853 public void apply(EditorColorsScheme scheme) {
854 if (scheme == null) scheme = myScheme;
855 scheme.setColor(myKey, myColor);
858 public boolean isModified() {
863 private static class EditorSettingColorDescription extends ColorAndFontDescription {
864 private GetSetColor myGetSetForeground;
865 private GetSetColor myGetSetBackground;
867 private EditorSettingColorDescription(String name,
869 ColorKey backgroundKey,
870 ColorKey foregroundKey,
872 EditorColorsScheme scheme) {
873 super(name, group, type, scheme, null, null);
874 if (backgroundKey != null) {
875 myGetSetBackground = new GetSetColor(backgroundKey, scheme);
877 if (foregroundKey != null) {
878 myGetSetForeground = new GetSetColor(foregroundKey, scheme);
884 public int getFontType() {
889 public void setFontType(int type) {
893 public Color getExternalEffectColor() {
898 public void setExternalEffectColor(Color color) {
902 public void setExternalEffectType(EffectType type) {
907 public EffectType getExternalEffectType() {
908 return EffectType.LINE_UNDERSCORE;
912 public Color getExternalForeground() {
913 if (myGetSetForeground == null) {
916 return myGetSetForeground.getColor();
920 public void setExternalForeground(Color col) {
921 if (myGetSetForeground == null) {
924 myGetSetForeground.setColor(col);
928 public Color getExternalBackground() {
929 if (myGetSetBackground == null) {
932 return myGetSetBackground.getColor();
936 public void setExternalBackground(Color col) {
937 if (myGetSetBackground == null) {
940 myGetSetBackground.setColor(col);
944 public Color getExternalErrorStripe() {
949 public void setExternalErrorStripe(Color col) {
953 public boolean isFontEnabled() {
958 public boolean isForegroundEnabled() {
959 return myGetSetForeground != null;
963 public boolean isBackgroundEnabled() {
964 return myGetSetBackground != null;
968 public boolean isEffectsColorEnabled() {
973 public boolean isModified() {
974 return myGetSetBackground != null && myGetSetBackground.isModified()
975 || myGetSetForeground != null && myGetSetForeground.isModified();
979 public void apply(EditorColorsScheme scheme) {
980 if (myGetSetBackground != null) {
981 myGetSetBackground.apply(scheme);
983 if (myGetSetForeground != null) {
984 myGetSetForeground.apply(scheme);
991 public String getHelpTopic() {
995 private static class MyColorScheme extends EditorColorsSchemeImpl {
996 private EditorSchemeAttributeDescriptor[] myDescriptors;
997 private String myName;
998 private boolean myIsNew = false;
1000 private MyColorScheme(@NotNull EditorColorsScheme parentScheme) {
1001 super(parentScheme);
1003 parentScheme.getFontPreferences().copyTo(getFontPreferences());
1004 setLineSpacing(parentScheme.getLineSpacing());
1006 parentScheme.getConsoleFontPreferences().copyTo(getConsoleFontPreferences());
1007 setConsoleLineSpacing(parentScheme.getConsoleLineSpacing());
1009 setQuickDocFontSize(parentScheme.getQuickDocFontSize());
1010 myName = parentScheme.getName();
1016 public String getName() {
1021 public void setName(@NotNull String name) {
1025 public void setDescriptors(EditorSchemeAttributeDescriptor[] descriptors) {
1026 myDescriptors = descriptors;
1029 public EditorSchemeAttributeDescriptor[] getDescriptors() {
1030 return myDescriptors;
1033 public boolean isDefault() {
1034 return myParentScheme instanceof DefaultColorsScheme;
1038 public boolean isReadOnly() {
1039 return myParentScheme instanceof ReadOnlyColorsScheme;
1042 public boolean isModified() {
1043 if (isFontModified() || isConsoleFontModified()) return true;
1045 for (EditorSchemeAttributeDescriptor descriptor : myDescriptors) {
1046 if (descriptor.isModified()) {
1054 private boolean isFontModified() {
1055 if (!getFontPreferences().equals(myParentScheme.getFontPreferences())) return true;
1056 if (getLineSpacing() != myParentScheme.getLineSpacing()) return true;
1057 return getQuickDocFontSize() != myParentScheme.getQuickDocFontSize();
1060 private boolean isConsoleFontModified() {
1061 if (!getConsoleFontPreferences().equals(myParentScheme.getConsoleFontPreferences())) return true;
1062 return getConsoleLineSpacing() != myParentScheme.getConsoleLineSpacing();
1065 public void apply() {
1066 if (!(myParentScheme instanceof ReadOnlyColorsScheme)) {
1067 apply(myParentScheme);
1071 public void apply(@NotNull EditorColorsScheme scheme) {
1072 scheme.setFontPreferences(getFontPreferences());
1073 scheme.setLineSpacing(myLineSpacing);
1074 scheme.setQuickDocFontSize(getQuickDocFontSize());
1075 scheme.setConsoleFontPreferences(getConsoleFontPreferences());
1076 scheme.setConsoleLineSpacing(getConsoleLineSpacing());
1078 for (EditorSchemeAttributeDescriptor descriptor : myDescriptors) {
1079 descriptor.apply(scheme);
1082 if (scheme instanceof AbstractColorsScheme) {
1083 ((AbstractColorsScheme)scheme).setSaveNeeded(true);
1088 public Object clone() {
1093 public EditorColorsScheme getOriginalScheme() {
1094 return myParentScheme;
1097 public void setIsNew() {
1101 public boolean isNew() {
1107 public String toString() {
1108 return "temporary scheme for " + myName;
1111 public boolean isInherited(TextAttributesKey key) {
1112 TextAttributesKey fallbackKey = key.getFallbackAttributeKey();
1113 if (fallbackKey != null) {
1114 if (myParentScheme instanceof AbstractColorsScheme) {
1115 TextAttributes ownAttrs = ((AbstractColorsScheme)myParentScheme).getDirectlyDefinedAttributes(key);
1116 if (ownAttrs != null) {
1117 return ownAttrs.isFallbackEnabled();
1120 TextAttributes attributes = getAttributes(key);
1121 if (attributes != null) {
1122 TextAttributes fallbackAttributes = getAttributes(fallbackKey);
1123 return attributes == fallbackAttributes;
1132 public String getId() {
1133 return getHelpTopic();
1138 public Runnable enableSearch(final String option) {
1143 public SearchableConfigurable findSubConfigurable(@NotNull Class pageClass) {
1144 if (mySubPanelFactories == null) {
1145 buildConfigurables();
1147 for (Map.Entry<ColorAndFontPanelFactory, InnerSearchableConfigurable> entry : mySubPanelFactories.entrySet()) {
1148 if (pageClass.isInstance(entry.getValue().createPanel().getSettingsPage())) {
1149 return entry.getValue();
1156 public SearchableConfigurable findSubConfigurable(String pageName) {
1157 if (mySubPanelFactories == null) {
1158 buildConfigurables();
1160 for (InnerSearchableConfigurable configurable : mySubPanelFactories.values()) {
1161 if (configurable.getDisplayName().equals(pageName)) {
1162 return configurable;
1169 public NewColorAndFontPanel findPage(String pageName) {
1170 InnerSearchableConfigurable child = (InnerSearchableConfigurable)findSubConfigurable(pageName);
1171 return child == null ? null : child.createPanel();
1174 private class InnerSearchableConfigurable implements SearchableConfigurable, OptionsContainingConfigurable, NoScroll {
1175 private NewColorAndFontPanel mySubPanel;
1176 private boolean mySubInitInvoked = false;
1177 @NotNull private final ColorAndFontPanelFactory myFactory;
1179 private InnerSearchableConfigurable(@NotNull ColorAndFontPanelFactory factory) {
1180 myFactory = factory;
1186 public String getDisplayName() {
1187 return myFactory.getPanelDisplayName();
1190 public NewColorAndFontPanel getSubPanelIfInitialized() {
1194 private NewColorAndFontPanel createPanel() {
1195 if (mySubPanel == null) {
1196 mySubPanel = myFactory.createPanel(ColorAndFontOptions.this);
1197 mySubPanel.reset(this);
1198 mySubPanel.addSchemesListener(new ColorAndFontSettingsListener.Abstract(){
1200 public void schemeChanged(final Object source) {
1202 resetSchemesCombo(source);
1207 mySubPanel.addDescriptionListener(new ColorAndFontSettingsListener.Abstract(){
1209 public void fontChanged() {
1210 for (NewColorAndFontPanel panel : getPanels()) {
1211 panel.updatePreview();
1220 public String getHelpTopic() {
1225 public JComponent createComponent() {
1226 return createPanel().getPanel();
1230 public boolean isModified() {
1232 for (MyColorScheme scheme : mySchemes.values()) {
1233 if (mySubPanel.containsFontOptions()) {
1234 if (scheme.isFontModified() || scheme.isConsoleFontModified()) {
1235 myRevertChangesCompleted = false;
1240 for (EditorSchemeAttributeDescriptor descriptor : scheme.getDescriptors()) {
1241 if (mySubPanel.contains(descriptor) && descriptor.isModified()) {
1242 myRevertChangesCompleted = false;
1255 public void apply() throws ConfigurationException {
1256 ColorAndFontOptions.this.apply();
1260 public void reset() {
1261 if (!mySubInitInvoked) {
1262 if (!myInitResetCompleted) {
1265 mySubInitInvoked = true;
1273 public void disposeUIResources() {
1274 if (mySubPanel != null) {
1275 mySubPanel.disposeUIResources();
1282 public String getId() {
1283 return ColorAndFontOptions.this.getId() + "." + getDisplayName();
1287 public Runnable enableSearch(final String option) {
1288 return createPanel().showOption(option);
1293 public Set<String> processListOptions() {
1294 return createPanel().processListOptions();
1300 public String toString() {
1301 return "Color And Fonts for "+getDisplayName();