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.SchemeManager;
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 public static final String ID = "reference.settingsdialog.IDE.editor.colors";
79 private Map<String, MyColorScheme> mySchemes;
80 private MyColorScheme mySelectedScheme;
81 public static final String FILE_STATUS_GROUP = ApplicationBundle.message("title.file.status");
82 public static final String SCOPES_GROUP = ApplicationBundle.message("title.scope.based");
84 private boolean mySomeSchemesDeleted = false;
85 private Map<ColorAndFontPanelFactory, InnerSearchableConfigurable> mySubPanelFactories;
87 private SchemesPanel myRootSchemesPanel;
89 private boolean myInitResetCompleted = false;
90 private boolean myInitResetInvoked = false;
92 private boolean myRevertChangesCompleted = false;
94 private boolean myApplyCompleted = false;
95 private boolean myDisposeCompleted = false;
96 private final Disposable myDisposable = Disposer.newDisposable();
99 public boolean isModified() {
100 boolean listModified = isSchemeListModified();
101 boolean schemeModified = isSomeSchemeModified();
103 if (listModified || schemeModified) {
104 myApplyCompleted = false;
110 private boolean isSchemeListModified(){
111 if (mySomeSchemesDeleted) return true;
113 if (!mySelectedScheme.getName().equals(EditorColorsManager.getInstance().getGlobalScheme().getName())) return true;
115 for (MyColorScheme scheme : mySchemes.values()) {
116 if (scheme.isNew()) return true;
122 private boolean isSomeSchemeModified() {
123 for (MyColorScheme scheme : mySchemes.values()) {
124 if (scheme.isModified()) return true;
129 public EditorColorsScheme selectScheme(@NotNull String name) {
130 mySelectedScheme = getScheme(name);
131 return mySelectedScheme;
134 private MyColorScheme getScheme(String name) {
135 return mySchemes.get(name);
139 public String getUniqueName(@NotNull String preferredName) {
141 if (mySchemes.containsKey(preferredName)) {
142 for (int i = 1; ; i++) {
143 name = preferredName + " (" + i + ")";
144 if (!mySchemes.containsKey(name)) {
150 name = preferredName;
155 public EditorColorsScheme getSelectedScheme() {
156 return mySelectedScheme;
159 public EditorSchemeAttributeDescriptor[] getCurrentDescriptions() {
160 return mySelectedScheme.getDescriptors();
163 public static boolean isReadOnly(@NotNull final EditorColorsScheme scheme) {
164 return ((MyColorScheme)scheme).isReadOnly();
168 public String[] getSchemeNames() {
169 List<MyColorScheme> schemes = new ArrayList<MyColorScheme>(mySchemes.values());
170 Collections.sort(schemes, (o1, o2) -> {
171 if (isReadOnly(o1) && !isReadOnly(o2)) return -1;
172 if (!isReadOnly(o1) && isReadOnly(o2)) return 1;
174 return o1.getName().compareToIgnoreCase(o2.getName());
177 List<String> names = new ArrayList<String>(schemes.size());
178 for (MyColorScheme scheme : schemes) {
179 names.add(scheme.getName());
182 return ArrayUtil.toStringArray(names);
186 public Collection<EditorColorsScheme> getSchemes() {
187 return new ArrayList<EditorColorsScheme>(mySchemes.values());
190 public void saveSchemeAs(String name) {
191 MyColorScheme scheme = mySelectedScheme;
192 if (scheme == null) return;
194 EditorColorsScheme clone = (EditorColorsScheme)scheme.getOriginalScheme().clone();
199 MyColorScheme newScheme = new MyColorScheme(clone);
200 initScheme(newScheme);
202 newScheme.setIsNew();
204 mySchemes.put(name, newScheme);
205 selectScheme(newScheme.getName());
206 resetSchemesCombo(null);
209 public void addImportedScheme(@NotNull EditorColorsScheme imported) {
210 MyColorScheme newScheme = new MyColorScheme(imported);
211 initScheme(newScheme);
213 mySchemes.put(imported.getName(), newScheme);
214 selectScheme(newScheme.getName());
215 resetSchemesCombo(null);
218 public void removeScheme(String name) {
219 if (mySelectedScheme.getName().equals(name)) {
220 //noinspection HardCodedStringLiteral
221 selectScheme("Default");
224 boolean deletedNewlyCreated = false;
226 MyColorScheme toDelete = mySchemes.get(name);
228 if (toDelete != null) {
229 deletedNewlyCreated = toDelete.isNew();
232 mySchemes.remove(name);
233 resetSchemesCombo(null);
234 mySomeSchemesDeleted = mySomeSchemesDeleted || !deletedNewlyCreated;
238 public void apply() throws ConfigurationException {
239 if (myApplyCompleted) {
244 EditorColorsManager myColorsManager = EditorColorsManager.getInstance();
245 SchemeManager<EditorColorsScheme> schemeManager = ((EditorColorsManagerImpl)myColorsManager).getSchemeManager();
247 List<EditorColorsScheme> result = new ArrayList<EditorColorsScheme>(mySchemes.values().size());
248 boolean activeSchemeModified = false;
249 EditorColorsScheme activeOriginalScheme = mySelectedScheme.getOriginalScheme();
250 for (MyColorScheme scheme : mySchemes.values()) {
251 if (!activeSchemeModified && activeOriginalScheme == scheme.getOriginalScheme()) {
252 activeSchemeModified = scheme.isModified();
255 if (!scheme.isDefault()) {
258 result.add(scheme.getOriginalScheme());
261 // refresh only if scheme is not switched
262 boolean refreshEditors = activeSchemeModified && schemeManager.getCurrentScheme() == activeOriginalScheme;
263 schemeManager.setSchemes(result, activeOriginalScheme);
264 if (refreshEditors) {
265 EditorColorsManagerImpl.schemeChangedOrSwitched();
268 final boolean isEditorThemeDark = ColorUtil.isDark(activeOriginalScheme.getDefaultBackground());
269 changeLafIfNecessary(isEditorThemeDark);
274 myApplyCompleted = true;
278 private static void changeLafIfNecessary(boolean isDarkEditorTheme) {
279 String propKey = "change.laf.on.editor.theme.change";
280 String value = PropertiesComponent.getInstance().getValue(propKey);
281 if ("false".equals(value)) return;
282 boolean applyAlways = "true".equals(value);
283 DialogWrapper.DoNotAskOption doNotAskOption = new DialogWrapper.DoNotAskOption.Adapter() {
285 public void rememberChoice(boolean isSelected, int exitCode) {
287 PropertiesComponent.getInstance().setValue(propKey, Boolean.toString(exitCode == Messages.YES));
292 public boolean shouldSaveOptionsOnCancel() {
297 final String productName = ApplicationNamesInfo.getInstance().getFullProductName();
298 final LafManager lafManager = LafManager.getInstance();
299 if (isDarkEditorTheme && !UIUtil.isUnderDarcula()) {
300 if (applyAlways || Messages.showYesNoDialog(
301 "Looks like you have set a dark editor theme. Would you like to set dark theme for entire " + productName,
302 "Change " + productName + " theme", Messages.YES_BUTTON, Messages.NO_BUTTON,
303 Messages.getQuestionIcon(), doNotAskOption) == Messages.YES) {
304 lafManager.setCurrentLookAndFeel(new DarculaLookAndFeelInfo());
305 lafManager.updateUI();
306 //noinspection SSBasedInspection
307 SwingUtilities.invokeLater(DarculaInstaller::install);
309 } else if (!isDarkEditorTheme && UIUtil.isUnderDarcula()) {
311 if (lafManager instanceof LafManagerImpl
313 (applyAlways || Messages.showYesNoDialog(
314 "Looks like you have set a bright editor theme. Would you like to set bright theme for entire " + productName,
315 "Change " + productName + " theme", Messages.YES_BUTTON, Messages.NO_BUTTON,
316 Messages.getQuestionIcon(), doNotAskOption) == Messages.YES)) {
317 lafManager.setCurrentLookAndFeel(((LafManagerImpl)lafManager).getDefaultLaf());
318 lafManager.updateUI();
319 //noinspection SSBasedInspection
320 SwingUtilities.invokeLater(DarculaInstaller::uninstall);
325 private boolean myIsReset = false;
327 private void resetSchemesCombo(Object source) {
330 myRootSchemesPanel.resetSchemesCombo(source);
331 if (mySubPanelFactories != null) {
332 for (NewColorAndFontPanel subPartialConfigurable : getPanels()) {
333 subPartialConfigurable.reset(source);
343 public JComponent createComponent() {
344 if (myRootSchemesPanel == null) {
345 ensureSchemesPanel();
347 return myRootSchemesPanel;
351 public boolean hasOwnContent() {
357 public Configurable[] buildConfigurables() {
358 myDisposeCompleted = false;
361 List<ColorAndFontPanelFactory> panelFactories = createPanelFactories();
363 List<Configurable> result = new ArrayList<Configurable>();
364 mySubPanelFactories = new LinkedHashMap<ColorAndFontPanelFactory, InnerSearchableConfigurable>(panelFactories.size());
365 for (ColorAndFontPanelFactory panelFactory : panelFactories) {
366 mySubPanelFactories.put(panelFactory, new InnerSearchableConfigurable(panelFactory));
369 result.addAll(new ArrayList<SearchableConfigurable>(mySubPanelFactories.values()));
370 return result.toArray(new Configurable[result.size()]);
374 private Set<NewColorAndFontPanel> getPanels() {
375 Set<NewColorAndFontPanel> result = new HashSet<NewColorAndFontPanel>();
376 for (InnerSearchableConfigurable configurable : mySubPanelFactories.values()) {
377 NewColorAndFontPanel panel = configurable.getSubPanelIfInitialized();
385 protected List<ColorAndFontPanelFactory> createPanelFactories() {
386 List<ColorAndFontPanelFactory> result = new ArrayList<ColorAndFontPanelFactory>();
387 result.add(new FontConfigurableFactory());
389 List<ColorAndFontPanelFactory> extensions = new ArrayList<ColorAndFontPanelFactory>();
390 extensions.add(new ConsoleFontConfigurableFactory());
391 ColorSettingsPage[] pages = ColorSettingsPages.getInstance().getRegisteredPages();
392 for (final ColorSettingsPage page : pages) {
393 extensions.add(new ColorAndFontPanelFactoryEx() {
396 public NewColorAndFontPanel createPanel(@NotNull ColorAndFontOptions options) {
397 final SimpleEditorPreview preview = new SimpleEditorPreview(options, page);
398 return NewColorAndFontPanel.create(preview, page.getDisplayName(), options, null, page);
403 public String getPanelDisplayName() {
404 return page.getDisplayName();
408 public DisplayPriority getPriority() {
409 if (page instanceof DisplayPrioritySortable) {
410 return ((DisplayPrioritySortable)page).getPriority();
412 return DisplayPriority.LANGUAGE_SETTINGS;
416 Collections.addAll(extensions, Extensions.getExtensions(ColorAndFontPanelFactory.EP_NAME));
417 Collections.sort(extensions, (f1, f2) -> {
418 if (f1 instanceof DisplayPrioritySortable) {
419 if (f2 instanceof DisplayPrioritySortable) {
420 int result1 = ((DisplayPrioritySortable)f1).getPriority().compareTo(((DisplayPrioritySortable)f2).getPriority());
421 if (result1 != 0) return result1;
427 else if (f2 instanceof DisplayPrioritySortable) {
430 return f1.getPanelDisplayName().compareToIgnoreCase(f2.getPanelDisplayName());
432 result.addAll(extensions);
434 result.add(new FileStatusColorsPageFactory());
435 result.add(new ScopeColorsPageFactory());
440 private static class FontConfigurableFactory implements ColorAndFontPanelFactory {
443 public NewColorAndFontPanel createPanel(@NotNull ColorAndFontOptions options) {
444 FontEditorPreview previewPanel = new FontEditorPreview(options, true);
445 return new NewColorAndFontPanel(new SchemesPanel(options), new FontOptions(options), previewPanel, "Font", null, null){
447 public boolean containsFontOptions() {
455 public String getPanelDisplayName() {
460 private static class ConsoleFontConfigurableFactory implements ColorAndFontPanelFactoryEx {
463 public NewColorAndFontPanel createPanel(@NotNull ColorAndFontOptions options) {
464 FontEditorPreview previewPanel = new FontEditorPreview(options, false) {
466 protected EditorColorsScheme updateOptionsScheme(EditorColorsScheme selectedScheme) {
467 return ConsoleViewUtil.updateConsoleColorScheme(selectedScheme);
470 return new NewColorAndFontPanel(new SchemesPanel(options), new ConsoleFontOptions(options), previewPanel, "Font", null, null){
472 public boolean containsFontOptions() {
480 public String getPanelDisplayName() {
481 return "Console Font";
486 public DisplayPriority getPriority() {
487 return DisplayPriority.COMMON_SETTINGS;
491 private void initAll() {
492 mySchemes = new THashMap<String, MyColorScheme>();
493 for (EditorColorsScheme allScheme : EditorColorsManager.getInstance().getAllSchemes()) {
494 MyColorScheme schemeDelegate = new MyColorScheme(allScheme);
495 initScheme(schemeDelegate);
496 mySchemes.put(schemeDelegate.getName(), schemeDelegate);
499 mySelectedScheme = mySchemes.get(EditorColorsManager.getInstance().getGlobalScheme().getName());
500 assert mySelectedScheme != null : EditorColorsManager.getInstance().getGlobalScheme().getName() + "; myschemes=" + mySchemes;
503 private static void initScheme(@NotNull MyColorScheme scheme) {
504 List<EditorSchemeAttributeDescriptor> descriptions = new ArrayList<EditorSchemeAttributeDescriptor>();
505 initPluggedDescriptions(descriptions, scheme);
506 initFileStatusDescriptors(descriptions, scheme);
507 initScopesDescriptors(descriptions, scheme);
509 scheme.setDescriptors(descriptions.toArray(new EditorSchemeAttributeDescriptor[descriptions.size()]));
512 private static void initPluggedDescriptions(@NotNull List<EditorSchemeAttributeDescriptor> descriptions, @NotNull MyColorScheme scheme) {
513 ColorSettingsPage[] pages = ColorSettingsPages.getInstance().getRegisteredPages();
514 for (ColorSettingsPage page : pages) {
515 initDescriptions(page, descriptions, scheme);
517 for (ColorAndFontDescriptorsProvider provider : Extensions.getExtensions(ColorAndFontDescriptorsProvider.EP_NAME)) {
518 initDescriptions(provider, descriptions, scheme);
522 private static void initDescriptions(@NotNull ColorAndFontDescriptorsProvider provider,
523 @NotNull List<EditorSchemeAttributeDescriptor> descriptions,
524 @NotNull MyColorScheme scheme) {
525 String group = provider.getDisplayName();
526 List<AttributesDescriptor> attributeDescriptors = ColorSettingsUtil.getAllAttributeDescriptors(provider);
527 for (AttributesDescriptor descriptor : attributeDescriptors) {
528 addSchemedDescription(descriptions, descriptor.getDisplayName(), group, descriptor.getKey(), scheme, null, null);
531 ColorDescriptor[] colorDescriptors = provider.getColorDescriptors();
532 for (ColorDescriptor descriptor : colorDescriptors) {
533 ColorKey back = descriptor.getKind() == ColorDescriptor.Kind.BACKGROUND ? descriptor.getKey() : null;
534 ColorKey fore = descriptor.getKind() == ColorDescriptor.Kind.FOREGROUND ? descriptor.getKey() : null;
535 addEditorSettingDescription(descriptions, descriptor.getDisplayName(), group, back, fore, scheme);
539 private static void initFileStatusDescriptors(@NotNull List<EditorSchemeAttributeDescriptor> descriptions, MyColorScheme scheme) {
541 FileStatus[] statuses = FileStatusFactory.getInstance().getAllFileStatuses();
543 for (FileStatus fileStatus : statuses) {
544 addEditorSettingDescription(descriptions,
545 fileStatus.getText(),
548 fileStatus.getColorKey(),
553 private static void initScopesDescriptors(@NotNull List<EditorSchemeAttributeDescriptor> descriptions, @NotNull MyColorScheme scheme) {
554 Set<Pair<NamedScope,NamedScopesHolder>> namedScopes = new THashSet<Pair<NamedScope,NamedScopesHolder>>(new TObjectHashingStrategy<Pair<NamedScope,NamedScopesHolder>>() {
556 public int computeHashCode(@NotNull final Pair<NamedScope, NamedScopesHolder> object) {
557 return object.getFirst().getName().hashCode();
561 public boolean equals(@NotNull final Pair<NamedScope, NamedScopesHolder> o1, @NotNull final Pair<NamedScope, NamedScopesHolder> o2) {
562 return o1.getFirst().getName().equals(o2.getFirst().getName());
565 Project[] projects = ProjectManager.getInstance().getOpenProjects();
566 for (Project project : projects) {
567 DependencyValidationManagerImpl validationManager = (DependencyValidationManagerImpl)DependencyValidationManager.getInstance(project);
568 List<Pair<NamedScope,NamedScopesHolder>> cachedScopes = validationManager.getScopeBasedHighlightingCachedScopes();
569 namedScopes.addAll(cachedScopes);
572 List<Pair<NamedScope, NamedScopesHolder>> list = new ArrayList<Pair<NamedScope, NamedScopesHolder>>(namedScopes);
574 Collections.sort(list, (o1, o2) -> o1.getFirst().getName().compareToIgnoreCase(o2.getFirst().getName()));
575 for (Pair<NamedScope,NamedScopesHolder> pair : list) {
576 NamedScope namedScope = pair.getFirst();
577 String name = namedScope.getName();
578 TextAttributesKey textAttributesKey = ScopeAttributesUtil.getScopeTextAttributeKey(name);
579 if (scheme.getAttributes(textAttributesKey) == null) {
580 scheme.setAttributes(textAttributesKey, new TextAttributes());
582 NamedScopesHolder holder = pair.getSecond();
584 PackageSet value = namedScope.getValue();
585 String toolTip = holder.getDisplayName() + (value==null ? "" : ": "+ value.getText());
586 addSchemedDescription(descriptions,
590 scheme, holder.getIcon(), toolTip);
595 private static String calcType(@Nullable ColorKey backgroundKey, @Nullable ColorKey foregroundKey) {
596 if (foregroundKey != null) {
597 return foregroundKey.getExternalName();
599 else if (backgroundKey != null) {
600 return backgroundKey.getExternalName();
605 private static void addEditorSettingDescription(@NotNull List<EditorSchemeAttributeDescriptor> list,
608 @Nullable ColorKey backgroundKey,
609 @Nullable ColorKey foregroundKey,
610 @NotNull EditorColorsScheme scheme) {
611 list.add(new EditorSettingColorDescription(name, group, backgroundKey, foregroundKey, calcType(backgroundKey, foregroundKey), scheme));
614 private static void addSchemedDescription(@NotNull List<EditorSchemeAttributeDescriptor> list,
617 @NotNull TextAttributesKey key,
618 @NotNull MyColorScheme scheme,
621 list.add(new SchemeTextAttributesDescription(name, group, key, scheme, icon, toolTip));
625 public String getDisplayName() {
626 return ApplicationBundle.message("title.colors.and.fonts");
629 private void revertChanges(){
630 if (isSchemeListModified() || isSomeSchemeModified()) {
631 myRevertChangesCompleted = false;
634 if (!myRevertChangesCompleted) {
635 ensureSchemesPanel();
642 myRevertChangesCompleted = true;
648 private void resetImpl() {
649 mySomeSchemesDeleted = false;
651 resetSchemesCombo(null);
655 public synchronized void reset() {
656 if (!myInitResetInvoked) {
658 if (!myInitResetCompleted) {
659 ensureSchemesPanel();
665 myInitResetCompleted = true;
670 myInitResetInvoked = true;
678 public synchronized void resetFromChild() {
679 if (!myInitResetCompleted) {
680 ensureSchemesPanel();
687 myInitResetCompleted = true;
693 private void ensureSchemesPanel() {
694 if (myRootSchemesPanel == null) {
695 myRootSchemesPanel = new SchemesPanel(this);
697 myRootSchemesPanel.addListener(new ColorAndFontSettingsListener.Abstract(){
699 public void schemeChanged(final Object source) {
701 resetSchemesCombo(source);
710 public void disposeUIResources() {
712 if (!myDisposeCompleted) {
714 super.disposeUIResources();
715 Disposer.dispose(myDisposable);
718 myDisposeCompleted = true;
723 mySubPanelFactories = null;
725 myInitResetCompleted = false;
726 myInitResetInvoked = false;
727 myRevertChangesCompleted = false;
729 myApplyCompleted = false;
730 myRootSchemesPanel = null;
734 private static class SchemeTextAttributesDescription extends TextAttributesDescription {
735 @NotNull private final TextAttributes myInitialAttributes;
736 @NotNull private final TextAttributesKey key;
737 private TextAttributes myFallbackAttributes;
738 private Pair<ColorSettingsPage,AttributesDescriptor> myBaseAttributeDescriptor;
739 private boolean myIsInheritedInitial = false;
741 private SchemeTextAttributesDescription(String name, String group, @NotNull TextAttributesKey key, @NotNull MyColorScheme scheme, Icon icon,
744 getInitialAttributes(scheme, key).clone(),
745 key, scheme, icon, toolTip);
747 myInitialAttributes = getInitialAttributes(scheme, key);
748 TextAttributesKey fallbackKey = key.getFallbackAttributeKey();
749 if (fallbackKey != null) {
750 myFallbackAttributes = scheme.getAttributes(fallbackKey);
751 myBaseAttributeDescriptor = ColorSettingsPages.getInstance().getAttributeDescriptor(fallbackKey);
752 if (myBaseAttributeDescriptor == null) {
753 myBaseAttributeDescriptor =
754 new Pair<ColorSettingsPage, AttributesDescriptor>(null, new AttributesDescriptor(fallbackKey.getExternalName(), fallbackKey));
757 myIsInheritedInitial = scheme.isInherited(key);
758 setInherited(myIsInheritedInitial);
759 if (myIsInheritedInitial) {
760 setInheritedAttributes(getTextAttributes());
766 private void setInheritedAttributes(@NotNull TextAttributes attributes) {
767 attributes.setFontType(myFallbackAttributes.getFontType());
768 attributes.setForegroundColor(myFallbackAttributes.getForegroundColor());
769 attributes.setBackgroundColor(myFallbackAttributes.getBackgroundColor());
770 attributes.setErrorStripeColor(myFallbackAttributes.getErrorStripeColor());
771 attributes.setEffectColor(myFallbackAttributes.getEffectColor());
772 attributes.setEffectType(myFallbackAttributes.getEffectType());
777 private static TextAttributes getInitialAttributes(@NotNull MyColorScheme scheme, @NotNull TextAttributesKey key) {
778 TextAttributes attributes = scheme.getAttributes(key);
779 return attributes != null ? attributes : new TextAttributes();
783 public void apply(EditorColorsScheme scheme) {
784 if (scheme == null) scheme = getScheme();
785 scheme.setAttributes(key, isInherited() ? new TextAttributes() : getTextAttributes());
789 public boolean isModified() {
791 return !myIsInheritedInitial;
793 return !Comparing.equal(myInitialAttributes, getTextAttributes()) || myIsInheritedInitial;
797 public boolean isErrorStripeEnabled() {
803 public TextAttributes getBaseAttributes() {
804 return myFallbackAttributes;
809 public Pair<ColorSettingsPage,AttributesDescriptor> getBaseAttributeDescriptor() {
810 return myBaseAttributeDescriptor;
814 public void setInherited(boolean isInherited) {
815 super.setInherited(isInherited);
819 private static class GetSetColor {
820 private final ColorKey myKey;
821 private final EditorColorsScheme myScheme;
822 private boolean isModified = false;
823 private Color myColor;
825 private GetSetColor(ColorKey key, EditorColorsScheme scheme) {
828 myColor = myScheme.getColor(myKey);
831 public Color getColor() {
835 public void setColor(Color col) {
836 if (getColor() == null || !getColor().equals(col)) {
842 public void apply(EditorColorsScheme scheme) {
843 if (scheme == null) scheme = myScheme;
844 scheme.setColor(myKey, myColor);
847 public boolean isModified() {
852 private static class EditorSettingColorDescription extends ColorAndFontDescription {
853 private GetSetColor myGetSetForeground;
854 private GetSetColor myGetSetBackground;
856 private EditorSettingColorDescription(String name,
858 ColorKey backgroundKey,
859 ColorKey foregroundKey,
861 EditorColorsScheme scheme) {
862 super(name, group, type, scheme, null, null);
863 if (backgroundKey != null) {
864 myGetSetBackground = new GetSetColor(backgroundKey, scheme);
866 if (foregroundKey != null) {
867 myGetSetForeground = new GetSetColor(foregroundKey, scheme);
873 public int getFontType() {
878 public void setFontType(int type) {
882 public Color getExternalEffectColor() {
887 public void setExternalEffectColor(Color color) {
891 public void setExternalEffectType(EffectType type) {
896 public EffectType getExternalEffectType() {
897 return EffectType.LINE_UNDERSCORE;
901 public Color getExternalForeground() {
902 if (myGetSetForeground == null) {
905 return myGetSetForeground.getColor();
909 public void setExternalForeground(Color col) {
910 if (myGetSetForeground == null) {
913 myGetSetForeground.setColor(col);
917 public Color getExternalBackground() {
918 if (myGetSetBackground == null) {
921 return myGetSetBackground.getColor();
925 public void setExternalBackground(Color col) {
926 if (myGetSetBackground == null) {
929 myGetSetBackground.setColor(col);
933 public Color getExternalErrorStripe() {
938 public void setExternalErrorStripe(Color col) {
942 public boolean isFontEnabled() {
947 public boolean isForegroundEnabled() {
948 return myGetSetForeground != null;
952 public boolean isBackgroundEnabled() {
953 return myGetSetBackground != null;
957 public boolean isEffectsColorEnabled() {
962 public boolean isModified() {
963 return myGetSetBackground != null && myGetSetBackground.isModified()
964 || myGetSetForeground != null && myGetSetForeground.isModified();
968 public void apply(EditorColorsScheme scheme) {
969 if (myGetSetBackground != null) {
970 myGetSetBackground.apply(scheme);
972 if (myGetSetForeground != null) {
973 myGetSetForeground.apply(scheme);
980 public String getHelpTopic() {
984 private static class MyColorScheme extends EditorColorsSchemeImpl {
985 private EditorSchemeAttributeDescriptor[] myDescriptors;
986 private String myName;
987 private boolean myIsNew = false;
989 private MyColorScheme(@NotNull EditorColorsScheme parentScheme) {
992 parentScheme.getFontPreferences().copyTo(getFontPreferences());
993 setLineSpacing(parentScheme.getLineSpacing());
995 parentScheme.getConsoleFontPreferences().copyTo(getConsoleFontPreferences());
996 setConsoleLineSpacing(parentScheme.getConsoleLineSpacing());
998 setQuickDocFontSize(parentScheme.getQuickDocFontSize());
999 myName = parentScheme.getName();
1005 public String getName() {
1010 public void setName(@NotNull String name) {
1014 public void setDescriptors(EditorSchemeAttributeDescriptor[] descriptors) {
1015 myDescriptors = descriptors;
1018 public EditorSchemeAttributeDescriptor[] getDescriptors() {
1019 return myDescriptors;
1022 public boolean isDefault() {
1023 return myParentScheme instanceof DefaultColorsScheme;
1027 public boolean isReadOnly() {
1028 return myParentScheme instanceof ReadOnlyColorsScheme;
1031 public boolean isModified() {
1032 if (isFontModified() || isConsoleFontModified()) return true;
1034 for (EditorSchemeAttributeDescriptor descriptor : myDescriptors) {
1035 if (descriptor.isModified()) {
1043 private boolean isFontModified() {
1044 if (!getFontPreferences().equals(myParentScheme.getFontPreferences())) return true;
1045 if (getLineSpacing() != myParentScheme.getLineSpacing()) return true;
1046 return getQuickDocFontSize() != myParentScheme.getQuickDocFontSize();
1049 private boolean isConsoleFontModified() {
1050 if (!getConsoleFontPreferences().equals(myParentScheme.getConsoleFontPreferences())) return true;
1051 return getConsoleLineSpacing() != myParentScheme.getConsoleLineSpacing();
1054 public void apply() {
1055 if (!(myParentScheme instanceof ReadOnlyColorsScheme)) {
1056 apply(myParentScheme);
1060 public void apply(@NotNull EditorColorsScheme scheme) {
1061 scheme.setFontPreferences(getFontPreferences());
1062 scheme.setLineSpacing(myLineSpacing);
1063 scheme.setQuickDocFontSize(getQuickDocFontSize());
1064 scheme.setConsoleFontPreferences(getConsoleFontPreferences());
1065 scheme.setConsoleLineSpacing(getConsoleLineSpacing());
1067 for (EditorSchemeAttributeDescriptor descriptor : myDescriptors) {
1068 descriptor.apply(scheme);
1071 if (scheme instanceof AbstractColorsScheme) {
1072 ((AbstractColorsScheme)scheme).setSaveNeeded(true);
1077 public Object clone() {
1082 public EditorColorsScheme getOriginalScheme() {
1083 return myParentScheme;
1086 public void setIsNew() {
1090 public boolean isNew() {
1096 public String toString() {
1097 return "temporary scheme for " + myName;
1100 public boolean isInherited(TextAttributesKey key) {
1101 TextAttributesKey fallbackKey = key.getFallbackAttributeKey();
1102 if (fallbackKey != null) {
1103 if (myParentScheme instanceof AbstractColorsScheme) {
1104 TextAttributes ownAttrs = ((AbstractColorsScheme)myParentScheme).getDirectlyDefinedAttributes(key);
1105 if (ownAttrs != null) {
1106 return ownAttrs.isFallbackEnabled();
1109 TextAttributes attributes = getAttributes(key);
1110 if (attributes != null) {
1111 TextAttributes fallbackAttributes = getAttributes(fallbackKey);
1112 return attributes == fallbackAttributes;
1121 public String getId() {
1122 return getHelpTopic();
1127 public Runnable enableSearch(final String option) {
1132 public SearchableConfigurable findSubConfigurable(@NotNull Class pageClass) {
1133 if (mySubPanelFactories == null) {
1134 buildConfigurables();
1136 for (Map.Entry<ColorAndFontPanelFactory, InnerSearchableConfigurable> entry : mySubPanelFactories.entrySet()) {
1137 if (pageClass.isInstance(entry.getValue().createPanel().getSettingsPage())) {
1138 return entry.getValue();
1145 public SearchableConfigurable findSubConfigurable(String pageName) {
1146 if (mySubPanelFactories == null) {
1147 buildConfigurables();
1149 for (InnerSearchableConfigurable configurable : mySubPanelFactories.values()) {
1150 if (configurable.getDisplayName().equals(pageName)) {
1151 return configurable;
1158 public NewColorAndFontPanel findPage(String pageName) {
1159 InnerSearchableConfigurable child = (InnerSearchableConfigurable)findSubConfigurable(pageName);
1160 return child == null ? null : child.createPanel();
1163 private class InnerSearchableConfigurable implements SearchableConfigurable, OptionsContainingConfigurable, NoScroll {
1164 private NewColorAndFontPanel mySubPanel;
1165 private boolean mySubInitInvoked = false;
1166 @NotNull private final ColorAndFontPanelFactory myFactory;
1168 private InnerSearchableConfigurable(@NotNull ColorAndFontPanelFactory factory) {
1169 myFactory = factory;
1175 public String getDisplayName() {
1176 return myFactory.getPanelDisplayName();
1179 public NewColorAndFontPanel getSubPanelIfInitialized() {
1183 private NewColorAndFontPanel createPanel() {
1184 if (mySubPanel == null) {
1185 mySubPanel = myFactory.createPanel(ColorAndFontOptions.this);
1186 mySubPanel.reset(this);
1187 mySubPanel.addSchemesListener(new ColorAndFontSettingsListener.Abstract(){
1189 public void schemeChanged(final Object source) {
1191 resetSchemesCombo(source);
1196 mySubPanel.addDescriptionListener(new ColorAndFontSettingsListener.Abstract(){
1198 public void fontChanged() {
1199 for (NewColorAndFontPanel panel : getPanels()) {
1200 panel.updatePreview();
1209 public String getHelpTopic() {
1214 public JComponent createComponent() {
1215 return createPanel().getPanel();
1219 public boolean isModified() {
1221 for (MyColorScheme scheme : mySchemes.values()) {
1222 if (mySubPanel.containsFontOptions()) {
1223 if (scheme.isFontModified() || scheme.isConsoleFontModified()) {
1224 myRevertChangesCompleted = false;
1229 for (EditorSchemeAttributeDescriptor descriptor : scheme.getDescriptors()) {
1230 if (mySubPanel.contains(descriptor) && descriptor.isModified()) {
1231 myRevertChangesCompleted = false;
1244 public void apply() throws ConfigurationException {
1245 ColorAndFontOptions.this.apply();
1249 public void reset() {
1250 if (!mySubInitInvoked) {
1251 if (!myInitResetCompleted) {
1254 mySubInitInvoked = true;
1262 public void disposeUIResources() {
1263 if (mySubPanel != null) {
1264 mySubPanel.disposeUIResources();
1271 public String getId() {
1272 return ColorAndFontOptions.this.getId() + "." + getDisplayName();
1276 public Runnable enableSearch(final String option) {
1277 return createPanel().showOption(option);
1282 public Set<String> processListOptions() {
1283 return createPanel().processListOptions();
1289 public String toString() {
1290 return "Color And Fonts for "+getDisplayName();