/* * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.ui.tabs; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.packageDependencies.DefaultScopesProvider; import com.intellij.psi.PsiFile; import com.intellij.psi.search.scope.NonProjectFilesScope; import com.intellij.psi.search.scope.TestsScope; import com.intellij.psi.search.scope.packageSet.NamedScope; import com.intellij.psi.search.scope.packageSet.NamedScopesHolder; import com.intellij.psi.search.scope.packageSet.PackageSet; import com.intellij.psi.search.scope.packageSet.PackageSetBase; import com.intellij.ui.ColorUtil; import com.intellij.util.PlatformUtils; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashMap; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @author spleaner * @author Konstantin Bulenkov */ // todo[spL]: listen to scope rename public class FileColorsModel implements Cloneable { public static final String FILE_COLOR = "fileColor"; private final List<FileColorConfiguration> myApplicationLevelConfigurations; private final List<FileColorConfiguration> myProjectLevelConfigurations; private static final Map<String, String> predefinedScopeNameToPropertyKey; private static final Map<String, String> predefinedScopeNameToColor; static { predefinedScopeNameToPropertyKey = new THashMap<>(); predefinedScopeNameToPropertyKey.put(NonProjectFilesScope.NAME, "file.colors.enable.non.project"); // These IDEs have TestScope registered (via PackagesScopesProvider or TestScopeProvider) if (PlatformUtils.isIntelliJ() || PlatformUtils.isRubyMine() || PlatformUtils.isPhpStorm() || PlatformUtils.isWebStorm() || PlatformUtils.isGoIde()) { predefinedScopeNameToPropertyKey.put(TestsScope.NAME, "file.colors.enable.tests"); } predefinedScopeNameToColor = new THashMap<>(); } @NotNull private final Project myProject; FileColorsModel(@NotNull final Project project) { myProject = project; myApplicationLevelConfigurations = new ArrayList<>(); myProjectLevelConfigurations = new ArrayList<>(); if (predefinedScopeNameToColor.size() < predefinedScopeNameToPropertyKey.size()) { DefaultScopesProvider defaultScopesProvider = DefaultScopesProvider.getInstance(project); for (String scopeName : predefinedScopeNameToPropertyKey.keySet()) { final NamedScope scope = defaultScopesProvider.findCustomScope(scopeName); assert scope != null : "There is no custom scope with name " + scopeName; final Color color = ColorUtil.getColor(scope.getClass()); assert color != null : scope.getClass().getName() + " is not annotated with @Colored"; final String colorName = FileColorManagerImpl.getColorName(color); predefinedScopeNameToColor.put(scopeName, colorName == null ? ColorUtil.toHex(color) : colorName); } } initGlobalScopes(); } private FileColorsModel(@NotNull Project project, @NotNull List<FileColorConfiguration> applicationLevel, @NotNull List<FileColorConfiguration> projectLevel) { myProject = project; myApplicationLevelConfigurations = new ArrayList<>(); myProjectLevelConfigurations = new ArrayList<>(); myApplicationLevelConfigurations.addAll(applicationLevel); myProjectLevelConfigurations.addAll(projectLevel); initGlobalScopes(); } private void initGlobalScopes() { PropertiesComponent propertyComponent = PropertiesComponent.getInstance(); for (String scopeName : predefinedScopeNameToPropertyKey.keySet()) { if (findConfiguration(scopeName, false) == null) { String color = propertyComponent.getValue(predefinedScopeNameToPropertyKey.get(scopeName)); if (color == null) { // backward compatibility, previously it was saved incorrectly as scope name instead of specified property key color = propertyComponent.getValue(scopeName); if (color == null) { color = predefinedScopeNameToColor.get(scopeName); } } if (!color.isEmpty()) { final Color col = ColorUtil.fromHex(color, null); final String name = col == null ? null : FileColorManagerImpl.getColorName(col); myApplicationLevelConfigurations.add(new FileColorConfiguration(scopeName, name == null ? color : name)); } } } } public void save(@NotNull Element e, boolean isProjectLevel) { List<FileColorConfiguration> configurations = isProjectLevel ? myProjectLevelConfigurations : myApplicationLevelConfigurations; for (FileColorConfiguration configuration : configurations) { String scopeName = configuration.getScopeName(); String propertyKey = isProjectLevel ? null : predefinedScopeNameToPropertyKey.get(scopeName); if (propertyKey == null) { configuration.save(e); } else { PropertiesComponent propertyComponent = PropertiesComponent.getInstance(); propertyComponent.setValue(propertyKey, configuration.getColorName(), predefinedScopeNameToColor.get(scopeName)); // previously it was saved incorrectly as scope name instead of specified property key PropertiesComponent.getInstance().setValue(scopeName, null); } } } public void load(@NotNull Element e, boolean isProjectLevel) { List<FileColorConfiguration> configurations = isProjectLevel ? myProjectLevelConfigurations : myApplicationLevelConfigurations; configurations.clear(); Map<String, String> predefinedScopeNameToPropertyKey = new THashMap<>(FileColorsModel.predefinedScopeNameToPropertyKey); for (Element child : e.getChildren(FILE_COLOR)) { FileColorConfiguration configuration = FileColorConfiguration.load(child); if (configuration != null) { if (!isProjectLevel) { predefinedScopeNameToPropertyKey.remove(configuration.getScopeName()); } configurations.add(configuration); } } if (!isProjectLevel) { PropertiesComponent properties = PropertiesComponent.getInstance(); for (String scopeName : predefinedScopeNameToPropertyKey.keySet()) { String colorName = properties.getValue(predefinedScopeNameToPropertyKey.get(scopeName)); if (colorName == null) { // backward compatibility, previously it was saved incorrectly as scope name instead of specified property key colorName = properties.getValue(scopeName); // so, default value if (colorName == null) { colorName = predefinedScopeNameToColor.get(scopeName); } } // empty means that value deleted if (!StringUtil.isEmpty(colorName)) { configurations.add(new FileColorConfiguration(scopeName, colorName)); } } } } @Override public FileColorsModel clone() throws CloneNotSupportedException { List<FileColorConfiguration> applicationLevel = new ArrayList<>(); for (FileColorConfiguration configuration : myApplicationLevelConfigurations) { applicationLevel.add(configuration.clone()); } List<FileColorConfiguration> projectLevel = new ArrayList<>(); for (FileColorConfiguration configuration : myProjectLevelConfigurations) { projectLevel.add(configuration.clone()); } return new FileColorsModel(myProject, applicationLevel, projectLevel); } public void add(@NotNull FileColorConfiguration configuration, boolean isProjectLevel) { List<FileColorConfiguration> configurations = isProjectLevel ? myProjectLevelConfigurations : myApplicationLevelConfigurations; if (!configurations.contains(configuration)) { configurations.add(configuration); } } public void add(@NotNull String scopeName, @NotNull String colorName, boolean isProjectLevel) { add(new FileColorConfiguration(scopeName, colorName), isProjectLevel); } @Nullable private FileColorConfiguration findConfiguration(@NotNull String scopeName, boolean isProjectLevel) { List<FileColorConfiguration> configurations = isProjectLevel ? myProjectLevelConfigurations : myApplicationLevelConfigurations; for (FileColorConfiguration configuration : configurations) { if (scopeName.equals(configuration.getScopeName())) { return configuration; } } return null; } public boolean isProjectLevel(@NotNull String scopeName) { return findConfiguration(scopeName, true) != null; } @Nullable public String getColor(@NotNull PsiFile psiFile) { if (!psiFile.isValid()) { return null; } VirtualFile virtualFile = psiFile.getVirtualFile(); return virtualFile == null ? null : getColor(virtualFile, psiFile.getProject()); } @Nullable public String getColor(@NotNull VirtualFile file, Project project) { if (!file.isValid()) { return null; } final FileColorConfiguration configuration = findConfiguration(file); if (configuration != null && configuration.isValid(project)) { return configuration.getColorName(); } return null; } @Nullable public String getScopeColor(@NotNull String scopeName, Project project) { FileColorConfiguration configuration = null; for (FileColorConfiguration each : getConfigurations()) { if (scopeName.equals(each.getScopeName())) { configuration = each; break; } } if (configuration != null && configuration.isValid(project)) { return configuration.getColorName(); } return null; } @Nullable private FileColorConfiguration findConfiguration(@NotNull final VirtualFile colored) { for (FileColorConfiguration configuration : getConfigurations()) { NamedScope scope = NamedScopesHolder.getScope(myProject, configuration.getScopeName()); if (scope != null) { NamedScopesHolder namedScopesHolder = NamedScopesHolder.getHolder(myProject, configuration.getScopeName(), null); PackageSet packageSet = scope.getValue(); if (packageSet instanceof PackageSetBase && namedScopesHolder != null && ((PackageSetBase)packageSet).contains(colored, myProject, namedScopesHolder)) { return configuration; } } } return null; } @NotNull private List<FileColorConfiguration> getConfigurations() { return ContainerUtil.concat(myApplicationLevelConfigurations, myProjectLevelConfigurations); } public boolean isProjectLevel(@NotNull FileColorConfiguration configuration) { return myProjectLevelConfigurations.contains(configuration); } public void setConfigurations(@NotNull List<FileColorConfiguration> configurations, boolean isProjectLevel) { if (isProjectLevel) { myProjectLevelConfigurations.clear(); myProjectLevelConfigurations.addAll(configurations); } else { myApplicationLevelConfigurations.clear(); Map<String, String> predefinedScopeNameToPropertyKey = new THashMap<>(FileColorsModel.predefinedScopeNameToPropertyKey); PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(); for (FileColorConfiguration configuration : configurations) { myApplicationLevelConfigurations.add(configuration); String propertyKey = predefinedScopeNameToPropertyKey.remove(configuration.getScopeName()); if (propertyKey != null) { propertiesComponent.setValue(propertyKey, configuration.getColorName()); } } for (String scopeName : predefinedScopeNameToPropertyKey.keySet()) { // empty string means that value deleted propertiesComponent.setValue(predefinedScopeNameToPropertyKey.get(scopeName), ""); // previously it was saved incorrectly as scope name instead of specified property key propertiesComponent.setValue(scopeName, null); } } } public boolean isColored(@NotNull String scopeName, boolean isProjectLevel) { return findConfiguration(scopeName, isProjectLevel) != null; } public List<FileColorConfiguration> getLocalConfigurations() { return myApplicationLevelConfigurations; } @NotNull public List<FileColorConfiguration> getProjectLevelConfigurations() { return myProjectLevelConfigurations; } }