diff options
Diffstat (limited to 'platform/platform-impl/src/com/intellij/openapi/options')
18 files changed, 982 insertions, 879 deletions
diff --git a/platform/platform-impl/src/com/intellij/openapi/options/CompositeConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/CompositeConfigurable.java index 0b9e5ade8d29..aaba0fdbaf6b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/CompositeConfigurable.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/CompositeConfigurable.java @@ -20,18 +20,21 @@ import java.util.List; public abstract class CompositeConfigurable<T extends UnnamedConfigurable> extends BaseConfigurable { private List<T> myConfigurables; + @Override public void reset() { for (T configurable : getConfigurables()) { configurable.reset(); } } + @Override public void apply() throws ConfigurationException { for (T configurable : getConfigurables()) { configurable.apply(); } } + @Override public boolean isModified() { for (T configurable : getConfigurables()) { if (configurable.isModified()) { @@ -41,6 +44,7 @@ public abstract class CompositeConfigurable<T extends UnnamedConfigurable> exten return false; } + @Override public void disposeUIResources() { if (myConfigurables != null) { for (final T myConfigurable : myConfigurables) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java b/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java deleted file mode 100644 index d7c7dca2f19b..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableBase.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2000-2014 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.openapi.options; - -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; - -public abstract class ConfigurableBase<UI extends ConfigurableUi<S>, S> implements SearchableConfigurable, Configurable.NoScroll { - private final String id; - private final String displayName; - private final String helpTopic; - - private UI ui; - - protected ConfigurableBase(@NotNull String id, @NotNull String displayName, @Nullable String helpTopic) { - this.id = id; - this.displayName = displayName; - this.helpTopic = helpTopic; - } - - @NotNull - @Override - public final String getId() { - return id; - } - - @Nls - @Override - public final String getDisplayName() { - return displayName; - } - - @Nullable - @Override - public final String getHelpTopic() { - return helpTopic; - } - - @Nullable - @Override - public Runnable enableSearch(String option) { - return null; - } - - @NotNull - protected abstract S getSettings(); - - @Override - public void reset() { - if (ui != null) { - ui.reset(getSettings()); - } - } - - @Nullable - @Override - public final JComponent createComponent() { - if (ui == null) { - ui = createUi(); - } - return ui.getComponent(); - } - - protected abstract UI createUi(); - - @Override - public final boolean isModified() { - return ui != null && ui.isModified(getSettings()); - } - - @Override - public final void apply() { - if (ui != null) { - ui.apply(getSettings()); - } - } - - @Override - public void disposeUIResources() { - ui = null; - } -}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java b/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java deleted file mode 100644 index 0ecb19a7ce8e..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/ConfigurableUi.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2000-2014 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.openapi.options; - -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; - -public interface ConfigurableUi<S> { - void reset(@NotNull S settings); - - boolean isModified(@NotNull S settings); - - void apply(@NotNull S settings); - - @NotNull - JComponent getComponent(); -}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java index 323394ad7c94..449a2180e3c1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java @@ -21,6 +21,7 @@ import com.intellij.openapi.application.impl.ApplicationImpl; import com.intellij.openapi.components.RoamingType; import com.intellij.openapi.components.ServiceBean; import com.intellij.openapi.components.SettingsSavingComponent; +import com.intellij.openapi.components.impl.stores.IApplicationStore; import com.intellij.openapi.components.impl.stores.StreamProvider; import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.containers.ContainerUtil; @@ -32,21 +33,22 @@ import java.util.Collections; import java.util.List; public class SchemesManagerFactoryImpl extends SchemesManagerFactory implements SettingsSavingComponent { - - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.options.SchemesManagerFactoryImpl"); + private static final Logger LOG = Logger.getInstance(SchemesManagerFactoryImpl.class); private final List<SchemesManagerImpl> myRegisteredManagers = ContainerUtil.createLockFreeCopyOnWriteList(); @Override - public <T extends Scheme, E extends ExternalizableScheme> SchemesManager<T, E> createSchemesManager(final String fileSpec, - final SchemeProcessor<E> processor, - final RoamingType roamingType) { + public <T extends Scheme, E extends ExternalizableScheme> SchemesManager<T, E> createSchemesManager(@NotNull String fileSpec, + @NotNull SchemeProcessor<E> processor, + @NotNull RoamingType roamingType) { final Application application = ApplicationManager.getApplication(); - if (!(application instanceof ApplicationImpl)) return null; - String baseDirPath = ((ApplicationImpl)application).getStateStore().getStateStorageManager().expandMacros(fileSpec); - + if (!(application instanceof ApplicationImpl)) { + return null; + } + IApplicationStore applicationStore = ((ApplicationImpl)application).getStateStore(); + String baseDirPath = applicationStore.getStateStorageManager().expandMacros(fileSpec); if (baseDirPath != null) { - StreamProvider provider = ((ApplicationImpl)ApplicationManager.getApplication()).getStateStore().getStateStorageManager().getStreamProvider(); + StreamProvider provider = applicationStore.getStateStorageManager().getStreamProvider(); SchemesManagerImpl<T, E> manager = new SchemesManagerImpl<T, E>(fileSpec, processor, roamingType, provider, new File(baseDirPath)); myRegisteredManagers.add(manager); return manager; diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java index 1c7e768c7d83..4ff3ebba1ee9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java @@ -34,6 +34,7 @@ import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.util.Alarm; +import com.intellij.util.SmartList; import com.intellij.util.UniqueFileNamesProvider; import com.intellij.util.containers.HashSet; import com.intellij.util.text.UniqueNameGenerator; @@ -51,7 +52,7 @@ import java.io.IOException; import java.util.*; public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme> extends AbstractSchemesManager<T, E> { - private static final Logger LOG = Logger.getInstance("#" + SchemesManagerFactoryImpl.class.getName()); + private static final Logger LOG = Logger.getInstance(SchemesManagerFactoryImpl.class); @NonNls private static final String DEFAULT_EXT = ".xml"; @@ -292,12 +293,13 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } } + @NotNull private Collection<E> readSchemesFromProviders() { - Collection<E> result = new ArrayList<E>(); if (myProvider == null || !myProvider.isEnabled()) { - return result; + return Collections.emptyList(); } + Collection<E> result = new SmartList<E>(); for (String subPath : myProvider.listSubFiles(myFileSpec, myRoamingType)) { if (!subPath.equals(DELETED_XML)) { try { @@ -305,6 +307,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme if (subDocument != null) { E scheme = readScheme(subDocument); boolean fileRenamed = false; + assert scheme != null; T existing = findSchemeByName(scheme.getName()); if (existing != null && existing instanceof ExternalizableScheme) { String currentFileName = ((ExternalizableScheme)existing).getExternalInfo().getCurrentFileName(); @@ -315,7 +318,6 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } } String fileName = checkFileNameIsFree(subPath, scheme.getName()); - if (!fileRenamed && !fileName.equals(subPath)) { deleteServerFiles(subPath); } @@ -325,7 +327,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } } catch (Exception e) { - LOG.info("Cannot load data from IDEAServer: " + e.getLocalizedMessage()); + LOG.info("Cannot load data from stream provider: " + e.getLocalizedMessage()); } } } @@ -347,21 +349,22 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme }); } - private String checkFileNameIsFree(final String subPath, final String schemeName) { + @NotNull + private String checkFileNameIsFree(@NotNull String subPath, @NotNull String schemeName) { for (Scheme scheme : mySchemes) { if (scheme instanceof ExternalizableScheme) { - ExternalInfo externalInfo = ((ExternalizableScheme)scheme).getExternalInfo(); - String name = externalInfo.getCurrentFileName(); - if (name != null) { - String fileName = name + mySchemeExtension; - if (fileName.equals(subPath) && !Comparing.equal(schemeName, scheme.getName())) { - return createUniqueFileName(collectAllFileNames(), UniqueFileNamesProvider.convertName(schemeName)); + String name = ((ExternalizableScheme)scheme).getExternalInfo().getCurrentFileName(); + if (name != null && + !schemeName.equals(scheme.getName()) && + subPath.length() == (name.length() + mySchemeExtension.length()) && + subPath.startsWith(name) && + subPath.endsWith(mySchemeExtension)) { + return UniqueNameGenerator.generateUniqueName(UniqueFileNamesProvider.convertName(schemeName), collectAllFileNames()); /*VirtualFile oldFile = myVFSBaseDir.findChild(subPath); if (oldFile != null) { oldFile.copy(this, myVFSBaseDir, uniqueFileName + EXT); } externalInfo.setCurrentFileName(uniqueFileName);*/ - } } } } @@ -369,8 +372,9 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme return subPath; } + @NotNull private Collection<String> collectAllFileNames() { - HashSet<String> result = new HashSet<String>(); + Set<String> result = new THashSet<String>(); for (T scheme : mySchemes) { if (scheme instanceof ExternalizableScheme) { ExternalInfo externalInfo = ((ExternalizableScheme)scheme).getExternalInfo(); @@ -382,10 +386,6 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme return result; } - private static String createUniqueFileName(final Collection<String> strings, final String schemeName) { - return UniqueNameGenerator.generateUniqueName(schemeName, strings); - } - private void loadScheme(final E scheme, boolean forceAdd, final String name) { if (scheme != null && (!myDeletedNames.contains(scheme.getName()) || forceAdd)) { T existing = findSchemeByName(scheme.getName()); @@ -469,13 +469,6 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme } final E scheme = readScheme(document); if (scheme != null) { - if (scheme.getName() == null) { - String suggestedName = FileUtil.getNameWithoutExtension(file.getName()); - if (!"_".equals(suggestedName)) { - scheme.setName(suggestedName); - } - } - loadScheme(scheme, forceAdd, file.getName()); result.add(scheme); } @@ -653,8 +646,7 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme result = new SharedSchemeData(original, name, user, description); } else { - Document original = subDocument; - result = new SharedSchemeData(original, name, null, null); + result = new SharedSchemeData(subDocument, name, null, null); } return result; } @@ -673,8 +665,8 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme return false; } - private String getFileFullPath(final String subPath) { - return myFileSpec + "/" + subPath; + private String getFileFullPath(@NotNull String subPath) { + return myFileSpec + '/' + subPath; } @Override @@ -843,9 +835,9 @@ public class SchemesManagerImpl<T extends Scheme, E extends ExternalizableScheme deleteServerFiles(fileName); } - private void deleteServerFiles(final String fileName) { + private void deleteServerFiles(@NotNull String path) { if (myProvider != null && myProvider.isEnabled()) { - StorageUtil.deleteContent(myProvider, getFileFullPath(fileName), myRoamingType); + StorageUtil.delete(myProvider, getFileFullPath(path), myRoamingType); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SimpleConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/SimpleConfigurable.java deleted file mode 100644 index 9812165932a2..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/SimpleConfigurable.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2000-2014 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.openapi.options; - -import com.intellij.openapi.util.Getter; -import com.intellij.util.ReflectionUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class SimpleConfigurable<UI extends ConfigurableUi<S>, S> extends ConfigurableBase<UI, S> { - private final Class<UI> uiClass; - private final Getter<S> settingsGetter; - - private SimpleConfigurable(@NotNull String id, @NotNull String displayName, @Nullable String helpTopic, @NotNull Class<UI> uiClass, @NotNull Getter<S> settingsGetter) { - super(id, displayName, helpTopic); - - this.uiClass = uiClass; - this.settingsGetter = settingsGetter; - } - - public static <UI extends ConfigurableUi<S>, S> SimpleConfigurable<UI, S> create(@NotNull String id, @NotNull String displayName, @Nullable String helpTopic, @NotNull Class<UI> uiClass, @NotNull Getter<S> settingsGetter) { - return new SimpleConfigurable<UI, S>(id, displayName, helpTopic, uiClass, settingsGetter); - } - - public static <UI extends ConfigurableUi<S>, S> SimpleConfigurable<UI, S> create(@NotNull String id, @NotNull String displayName, @NotNull Class<UI> uiClass, @NotNull Getter<S> settingsGetter) { - return create(id, displayName, id, uiClass, settingsGetter); - } - - @NotNull - @Override - protected S getSettings() { - return settingsGetter.get(); - } - - @Override - protected UI createUi() { - return ReflectionUtil.newInstance(uiClass); - } -}
\ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java index 5f8dc0e890df..3b67a3478368 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java @@ -165,10 +165,6 @@ public class ConfigurableWrapper implements SearchableConfigurable { return myEp; } - public String getGroupId() { - return myEp.groupId; - } - public String getParentId() { return myEp.parentId; } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/GlassPanel.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/GlassPanel.java index 96cc9e451185..c80a222ffd96 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/GlassPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/GlassPanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -17,7 +17,10 @@ package com.intellij.openapi.options.ex; import com.intellij.ide.ui.search.SearchUtil; +import com.intellij.openapi.ui.GraphicsConfig; +import com.intellij.ui.ColorUtil; import com.intellij.ui.components.JBTabbedPane; +import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.Nullable; @@ -27,6 +30,7 @@ import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.awt.image.Kernel; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -64,17 +68,19 @@ public class GlassPanel extends JComponent { final Point leftPoint = SwingUtilities.convertPoint(myPanel, new Point(visibleRect.x, visibleRect.y), surfaceComponent); Area innerPanel = new Area(new Rectangle2D.Double(leftPoint.x, leftPoint.y, visibleRect.width, visibleRect.height)); Area mask = new Area(screen); - + ArrayList<JComponent> components = new ArrayList<JComponent>(); for (JComponent lightComponent : myLightComponents) { - final Area area = getComponentArea(surfaceComponent, lightComponent); + final Area area = getComponentArea(surfaceComponent, lightComponent, 1); if (area == null) continue; + components.add(lightComponent); if (lightComponent instanceof JLabel) { final JLabel label = (JLabel)lightComponent; final Component labelFor = label.getLabelFor(); if (labelFor instanceof JComponent) { - final Area labelForArea = getComponentArea(surfaceComponent, (JComponent)labelFor); + final Area labelForArea = getComponentArea(surfaceComponent, (JComponent)labelFor, 1); if (labelForArea != null) { + components.add((JComponent)labelFor); area.add(labelForArea); } } @@ -86,19 +92,32 @@ public class GlassPanel extends JComponent { Graphics2D g2 = (Graphics2D)g; - Color shieldColor = new Color(0.0f, 0.0f, 0.0f, 0.15f); + Color shieldColor = new Color(0.0f, 0.0f, 0.0f, 0.20f); Color boundsColor = Color.gray; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(shieldColor); g2.fill(mask); - g2.setColor(boundsColor); - g2.draw(mask); + g2.setColor(ColorUtil.toAlpha(Color.orange, 25)); + GraphicsConfig config = GraphicsUtil.setupAAPainting(g2); + for (int i = 2; i > 0; i--) { + g2.setStroke(new BasicStroke(i)); + Area arrr = new Area(); + for (JComponent component : components) { + Area area = getComponentArea(surfaceComponent, component, i-1); + if (area != null) { + arrr.add(area); + } + } + g2.draw(arrr); + } + + config.restore(); } } @Nullable - private Area getComponentArea(final JComponent surfaceComponent, final JComponent lightComponent) { + private Area getComponentArea(final JComponent surfaceComponent, final JComponent lightComponent, int offset) { if (!lightComponent.isShowing()) return null; final Point panelPoint = SwingUtilities.convertPoint(lightComponent, new Point(0, 0), surfaceComponent); @@ -115,12 +134,17 @@ public class GlassPanel extends JComponent { int hInset = isWithBorder ? 7 : isLabelFromTabbedPane ? 20 : 7; int vInset = isWithBorder ? 1 : isLabelFromTabbedPane ? 10 : 5; - final Area area = new Area(new RoundRectangle2D.Double(x - hInset + insetsToIgnore.left, - y - vInset + insetsToIgnore.top, - lightComponent.getWidth() + hInset * 2 - insetsToIgnore.right - insetsToIgnore.left, - lightComponent.getHeight() + vInset * 2 - insetsToIgnore.top - insetsToIgnore.bottom, - 6, 6)); - return area; + hInset += offset; + vInset += offset; + int xCoord = x - hInset + insetsToIgnore.left; + int yCoord = y - vInset + insetsToIgnore.top; + int width = lightComponent.getWidth() + hInset * 2 - insetsToIgnore.right - insetsToIgnore.left; + int height = lightComponent.getHeight() + vInset * 2 - insetsToIgnore.top - insetsToIgnore.bottom; + return new Area(new RoundRectangle2D.Double(xCoord, + yCoord, + width, + height, + Math.min(height, 30), Math.min(height, 30))); } protected static Kernel getBlurKernel(int blurSize) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java index f1411cf3551d..e3404ec017cd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java @@ -17,15 +17,21 @@ package com.intellij.openapi.options.ex; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; +import javax.swing.JComponent; -public final class MixedConfigurableGroup implements ConfigurableGroup { +public final class MixedConfigurableGroup implements SearchableConfigurable, ConfigurableGroup { private final String myGroupId; private Configurable[] myConfigurables; @@ -34,6 +40,7 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { myConfigurables = (configurables != null) ? configurables.toArray(new Configurable[configurables.size()]) : new Configurable[0]; + Arrays.sort(myConfigurables, COMPARATOR); } private MixedConfigurableGroup(String groupId, HashMap<String, ArrayList<Configurable>> configurables) { @@ -41,6 +48,45 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { } @Override + public JComponent createComponent() { + return null; + } + + @Override + public boolean isModified() { + return false; + } + + @Override + public void apply() throws ConfigurationException { + } + + @Override + public void reset() { + } + + @Override + public void disposeUIResources() { + myConfigurables = null; + } + + @Override + public Runnable enableSearch(String option) { + return null; + } + + @NotNull + @Override + public String getId() { + return "configurable.group." + myGroupId; + } + + @Override + public String getHelpTopic() { + return "configurable.group." + myGroupId + ".help.topic"; + } + + @Override public String getDisplayName() { return OptionsBundle.message("configurable.group." + myGroupId + ".settings.display.name"); } @@ -60,7 +106,7 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { for (Configurable configurable : configurables) { String groupId = null; if (configurable instanceof ConfigurableWrapper) { - groupId = ((ConfigurableWrapper)configurable).getGroupId(); + groupId = ((ConfigurableWrapper)configurable).getExtensionPoint().groupId; } ArrayList<Configurable> list = map.get(groupId); if (list == null) { @@ -70,7 +116,7 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { } ArrayList<Configurable> buildList = map.get("build"); if (buildList != null) { - NodeConfigurable buildTools = new NodeConfigurable("build.tools"); + NodeConfigurable buildTools = new NodeConfigurable("build.tools", 1000); buildTools.add(find("MavenSettings", buildList.iterator())); buildTools.add(find("reference.settingsdialog.project.gradle", buildList.iterator())); buildTools.add(find("reference.settingsdialog.project.gant", buildList.iterator())); @@ -106,4 +152,27 @@ public final class MixedConfigurableGroup implements ConfigurableGroup { } return null; } + + public static int getGroupWeight(Configurable configurable) { + if (configurable instanceof NodeConfigurable) { + return ((NodeConfigurable)configurable).getGroupWeight(); + } + if (configurable instanceof ConfigurableWrapper) { + return ((ConfigurableWrapper)configurable).getExtensionPoint().groupWeight; + } + return 0; + } + + private static final Comparator<Configurable> COMPARATOR = new Comparator<Configurable>() { + @Override + public int compare(Configurable configurable1, Configurable configurable2) { + if (configurable1 == null || configurable2 == null) { + return configurable2 != null ? -1 : configurable1 != null ? 1 : 0; + } + int weight1 = getGroupWeight(configurable1); + int weight2 = getGroupWeight(configurable2); + return weight1 > weight2 ? -1 : weight1 < weight2 ? 1 : StringUtil.naturalCompare(configurable1.getDisplayName(), + configurable2.getDisplayName()); + } + }; } diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java index 6571c7a63746..609943d02943 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java @@ -27,9 +27,15 @@ import java.util.ArrayList; public final class NodeConfigurable extends SearchableConfigurable.Parent.Abstract { private final ArrayList<Configurable> myConfigurables = new ArrayList<Configurable>(); private final String myId; + private final int myWeight; - public NodeConfigurable(@NotNull String id) { + public NodeConfigurable(@NotNull String id, int weight) { myId = id; + myWeight = weight; + } + + public int getGroupWeight() { + return myWeight; } public void add(Configurable configurable) { @@ -48,13 +54,13 @@ public final class NodeConfigurable extends SearchableConfigurable.Parent.Abstra @NotNull @Override public String getId() { - return myId; + return "node.configurable." + myId; } @Nullable @Override public String getHelpTopic() { - return myId; + return "node.configurable." + myId + ".help.topic"; } @Nls diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java index 80144d230eaf..def0dd16a2d2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/SingleConfigurableEditor.java @@ -46,7 +46,7 @@ public class SingleConfigurableEditor extends DialogWrapper { private Component myParentComponent; private Configurable myConfigurable; private JComponent myCenterPanel; - private String myDimensionKey; + private final String myDimensionKey; private final boolean myShowApplyButton; private boolean myChangesWereApplied; @@ -134,6 +134,7 @@ public class SingleConfigurableEditor extends DialogWrapper { return displayName.replaceAll("\n", " "); } + @Override protected String getDimensionServiceKey() { if (myDimensionKey == null) { return super.getDimensionServiceKey(); @@ -143,6 +144,7 @@ public class SingleConfigurableEditor extends DialogWrapper { } } + @Override @NotNull protected Action[] createActions() { List<Action> actions = new ArrayList<Action>(); @@ -157,6 +159,7 @@ public class SingleConfigurableEditor extends DialogWrapper { return actions.toArray(new Action[actions.size()]); } + @Override protected void doHelpAction() { HelpManager.getInstance().invokeHelp(myConfigurable.getHelpTopic()); } @@ -169,6 +172,7 @@ public class SingleConfigurableEditor extends DialogWrapper { super.doCancelAction(); } + @Override protected void doOKAction() { try { if (myConfigurable.isModified()) myConfigurable.apply(); @@ -201,10 +205,11 @@ public class SingleConfigurableEditor extends DialogWrapper { public ApplyAction() { super(CommonBundle.getApplyButtonText()); final Runnable updateRequest = new Runnable() { + @Override public void run() { - if (!SingleConfigurableEditor.this.isShowing()) return; + if (!isShowing()) return; try { - ApplyAction.this.setEnabled(myConfigurable != null && myConfigurable.isModified()); + setEnabled(myConfigurable != null && myConfigurable.isModified()); } catch (IndexNotReadyException ignored) { } @@ -214,6 +219,7 @@ public class SingleConfigurableEditor extends DialogWrapper { // invokeLater necessary to make sure dialog is already shown so we calculate modality state correctly. SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { addUpdateRequest(updateRequest); } @@ -224,6 +230,7 @@ public class SingleConfigurableEditor extends DialogWrapper { myUpdateAlarm.addRequest(updateRequest, 500, ModalityState.stateForComponent(getWindow())); } + @Override public void actionPerformed(ActionEvent event) { if (myPerformAction) return; try { @@ -248,11 +255,13 @@ public class SingleConfigurableEditor extends DialogWrapper { } } + @Override protected JComponent createCenterPanel() { myCenterPanel = myConfigurable.createComponent(); return myCenterPanel; } + @Override public JComponent getPreferredFocusedComponent() { if (myConfigurable instanceof BaseConfigurable) { JComponent preferred = ((BaseConfigurable)myConfigurable).getPreferredFocusedComponent(); @@ -261,6 +270,7 @@ public class SingleConfigurableEditor extends DialogWrapper { return IdeFocusTraversalPolicy.getPreferredFocusedComponent(myCenterPanel); } + @Override public void dispose() { super.dispose(); myConfigurable.disposeUIResources(); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java new file mode 100644 index 000000000000..c38bf6ff2033 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java @@ -0,0 +1,330 @@ +/* + * Copyright 2000-2014 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.openapi.options.newEditor; + +import com.intellij.CommonBundle; +import com.intellij.ide.ui.search.SearchUtil; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.help.HelpManager; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.Gray; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.JBColor; +import com.intellij.ui.border.CustomLineBorder; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Konstantin Bulenkov + */ +public class IdeSettingsDialog extends DialogWrapper implements DataProvider { + private Project myProject; + private ConfigurableGroup[] myGroups; + private Configurable myPreselected; + private OptionsEditor myEditor; + + private ApplyAction myApplyAction; + public static final String DIMENSION_KEY = "OptionsEditor"; + @NonNls static final String LAST_SELECTED_CONFIGURABLE = "options.lastSelected"; + + /** + * This constructor should be eliminated after the new modality approach + * will have been checked. See a {@code Registry} key ide.perProjectModality + * + * @deprecated + */ + public IdeSettingsDialog(Project project, ConfigurableGroup[] groups, + @Nullable Configurable preselectedConfigurable, boolean applicationModalIfPossible) { + super(project, true, applicationModalIfPossible); + init(project, groups, preselectedConfigurable != null ? preselectedConfigurable : findLastSavedConfigurable(groups, project)); + } + + /** + * This constructor should be eliminated after the new modality approach + * will have been checked. See a {@code Registry} key ide.perProjectModality + * + * @deprecated + */ + public IdeSettingsDialog(Project project, ConfigurableGroup[] groups, + @NotNull String preselectedConfigurableDisplayName, boolean applicationModalIfPossible) { + super(project, true, applicationModalIfPossible); + init(project, groups, getPreselectedByDisplayName(groups, preselectedConfigurableDisplayName, project)); + } + + public IdeSettingsDialog(Project project, ConfigurableGroup[] groups, @Nullable Configurable preselectedConfigurable) { + super(project, true); + init(project, groups, preselectedConfigurable != null ? preselectedConfigurable : findLastSavedConfigurable(groups, project)); + } + + public IdeSettingsDialog(Project project, ConfigurableGroup[] groups, @NotNull String preselectedConfigurableDisplayName) { + super(project, true); + init(project, groups, getPreselectedByDisplayName(groups, preselectedConfigurableDisplayName, project)); + } + + @Nullable + @Override + protected Border createContentPaneBorder() { + return IdeBorderFactory.createEmptyBorder(0); + } + + private void init(final Project project, final ConfigurableGroup[] groups, @Nullable final Configurable preselected) { + myProject = project; + myGroups = groups; + myPreselected = preselected; + + setTitle(CommonBundle.settingsTitle()); + + init(); + } + + @Nullable + private static Configurable getPreselectedByDisplayName(final ConfigurableGroup[] groups, final String preselectedConfigurableDisplayName, + final Project project) { + Configurable result = findPreselectedByDisplayName(preselectedConfigurableDisplayName, groups); + + return result == null ? findLastSavedConfigurable(groups, project) : result; + } + + @Override + public boolean isTypeAheadEnabled() { + return true; + } + + @Nullable + @Override + protected JComponent createSouthPanel() { + final JComponent panel = super.createSouthPanel(); + CustomLineBorder line = new CustomLineBorder(new JBColor(Gray._153.withAlpha(128), Gray._100.withAlpha(128)), 1, 0, 0, 0); + panel.setBorder(new CompoundBorder(line, new EmptyBorder(8, 12, 8, 12))); + return panel; + } + + protected JComponent createCenterPanel() { + myEditor = new OptionsEditor(myProject, myGroups, myPreselected); + myEditor.getContext().addColleague(new OptionsEditorColleague.Adapter() { + @Override + public ActionCallback onModifiedAdded(final Configurable configurable) { + updateStatus(); + return new ActionCallback.Done(); + } + + @Override + public ActionCallback onModifiedRemoved(final Configurable configurable) { + updateStatus(); + return new ActionCallback.Done(); + } + + @Override + public ActionCallback onErrorsChanged() { + updateStatus(); + return new ActionCallback.Done(); + } + }); + Disposer.register(myDisposable, myEditor); + return myEditor; + } + + public boolean updateStatus() { + myApplyAction.setEnabled(myEditor.canApply()); + + final Map<Configurable, ConfigurationException> errors = myEditor.getContext().getErrors(); + if (errors.size() == 0) { + setErrorText(null); + } + else { + String text = "Changes were not applied because of an error"; + + final String errorMessage = getErrorMessage(errors); + if (errorMessage != null) { + text += "<br>" + errorMessage; + } + + setErrorText(text); + } + + return errors.size() == 0; + } + + @Nullable + private static String getErrorMessage(final Map<Configurable, ConfigurationException> errors) { + final Collection<ConfigurationException> values = errors.values(); + final ConfigurationException[] exceptions = values.toArray(new ConfigurationException[values.size()]); + if (exceptions.length > 0) { + return exceptions[0].getMessage(); + } + return null; + } + + @Override + protected String getDimensionServiceKey() { + return DIMENSION_KEY; + } + + @Override + protected void doOKAction() { + myEditor.flushModifications(); + + if (myEditor.canApply()) { + myEditor.apply(); + if (!updateStatus()) return; + } + + saveCurrentConfigurable(); + + ApplicationManager.getApplication().saveAll(); + + super.doOKAction(); + } + + + private void saveCurrentConfigurable() { + final Configurable current = myEditor.getContext().getCurrentConfigurable(); + if (current == null) return; + + final PropertiesComponent props = PropertiesComponent.getInstance(myProject); + + if (current instanceof SearchableConfigurable) { + props.setValue(LAST_SELECTED_CONFIGURABLE, ((SearchableConfigurable)current).getId()); + } + else { + props.setValue(LAST_SELECTED_CONFIGURABLE, current.getClass().getName()); + } + } + + @Nullable + private static Configurable findLastSavedConfigurable(ConfigurableGroup[] groups, final Project project) { + final String id = PropertiesComponent.getInstance(project).getValue(LAST_SELECTED_CONFIGURABLE); + if (id == null) return null; + + return findConfigurableInGroups(id, groups); + } + + @Nullable + private static Configurable findConfigurableInGroups(String id, Configurable.Composite... groups) { + // avoid unnecessary group expand: check top-level configurables in all groups before looking at children + for (Configurable.Composite group : groups) { + final Configurable[] configurables = group.getConfigurables(); + for (Configurable c : configurables) { + if (c instanceof SearchableConfigurable && id.equals(((SearchableConfigurable)c).getId())) { + return c; + } + else if (id.equals(c.getClass().getName())) { + return c; + } + } + } + for (Configurable.Composite group : groups) { + final Configurable[] configurables = group.getConfigurables(); + for (Configurable c : configurables) { + if (c instanceof Configurable.Composite) { + Configurable result = findConfigurableInGroups(id, (Configurable.Composite)c); + if (result != null) { + return result; + } + } + } + } + return null; + } + + @Nullable + private static Configurable findPreselectedByDisplayName(final String preselectedConfigurableDisplayName, ConfigurableGroup[] groups) { + final List<Configurable> all = SearchUtil.expand(groups); + for (Configurable each : all) { + if (preselectedConfigurableDisplayName.equals(each.getDisplayName())) return each; + } + return null; + } + + @Override + public void doCancelAction(final AWTEvent source) { + if (source instanceof KeyEvent || source instanceof ActionEvent) { + if (myEditor.getContext().isHoldingFilter()) { + myEditor.clearFilter(); + return; + } + } + + super.doCancelAction(source); + } + + @Override + public void doCancelAction() { + saveCurrentConfigurable(); + super.doCancelAction(); + } + + @NotNull + @Override + protected Action[] createActions() { + myApplyAction = new ApplyAction(); + return new Action[]{getOKAction(), getCancelAction(), myApplyAction, getHelpAction()}; + } + + @Override + protected void doHelpAction() { + final String topic = myEditor.getHelpTopic(); + if (topic != null) { + HelpManager.getInstance().invokeHelp(topic); + } + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myEditor.getPreferredFocusedComponent(); + } + + public Object getData(@NonNls String dataId) { + if (OptionsEditor.KEY.is(dataId)) { + return myEditor; + } + return null; + } + + private class ApplyAction extends AbstractAction { + public ApplyAction() { + super(CommonBundle.getApplyButtonText()); + setEnabled(false); + } + + public void actionPerformed(final ActionEvent e) { + myEditor.apply(); + myEditor.revalidate(); + myEditor.repaint(); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java index 271539f038a0..c2119596a06e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java @@ -15,9 +15,12 @@ */ package com.intellij.openapi.options.newEditor; -import com.intellij.ide.ui.search.ConfigurableHit; +import com.intellij.AbstractBundle; +import com.intellij.CommonBundle; +import com.intellij.icons.AllIcons; +import com.intellij.ide.ui.laf.darcula.ui.DarculaTextBorder; +import com.intellij.ide.ui.laf.darcula.ui.DarculaTextFieldUI; import com.intellij.ide.ui.search.SearchUtil; -import com.intellij.ide.ui.search.SearchableOptionsRegistrar; import com.intellij.ide.util.PropertiesComponent; import com.intellij.internal.statistic.UsageTrigger; import com.intellij.internal.statistic.beans.ConvertUsagesUtil; @@ -36,18 +39,18 @@ import com.intellij.openapi.ui.*; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.EdtRunnable; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeGlassPaneUtil; -import com.intellij.ui.DocumentAdapter; import com.intellij.ui.LightColors; +import com.intellij.ui.OnePixelSplitter; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.SearchTextField; +import com.intellij.ui.components.labels.LinkLabel; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.ui.components.panels.Wrapper; import com.intellij.ui.navigation.History; import com.intellij.ui.navigation.Place; -import com.intellij.ui.speedSearch.ElementFilter; import com.intellij.ui.treeStructure.SimpleNode; import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder; import com.intellij.util.ui.UIUtil; @@ -61,7 +64,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.event.DocumentEvent; +import javax.swing.border.EmptyBorder; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; @@ -77,23 +80,18 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat @NonNls private static final String MAIN_SPLITTER_PROPORTION = "options.splitter.main.proportions"; @NonNls private static final String DETAILS_SPLITTER_PROPORTION = "options.splitter.details.proportions"; - @NonNls private static final String SEARCH_VISIBLE = "options.searchVisible"; - @NonNls private static final String NOT_A_NEW_COMPONENT = "component.was.already.instantiated"; - private final Project myProject; - - private final OptionsEditorContext myContext; - private final History myHistory = new History(this); private final OptionsTree myTree; private final SettingsTreeView myTreeView; - private final MySearchField mySearch; + private final SearchTextField mySearch; private final Splitter myMainSplitter; //[back/forward] JComponent myToolbarComponent; - private final DetailsComponent myOwnDetails = new DetailsComponent().setEmptyContentText("Select configuration element in the tree to edit its settings"); + private final DetailsComponent myOwnDetails = + new DetailsComponent().setEmptyContentText("Select configuration element in the tree to edit its settings"); private final ContentWrapper myContentWrapper = new ContentWrapper(); @@ -101,30 +99,54 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat private final Map<Configurable, ActionCallback> myConfigurable2LoadCallback = new HashMap<Configurable, ActionCallback>(); private final MergingUpdateQueue myModificationChecker; - private final ConfigurableGroup[] myGroups; private final SpotlightPainter mySpotlightPainter = new SpotlightPainter(); private final MergingUpdateQueue mySpotlightUpdate; private final LoadingDecorator myLoadingDecorator; - private final Filter myFilter; + private final SettingsFilter myFilter; private final Wrapper mySearchWrapper = new Wrapper(); private final JPanel myLeftSide; - private boolean myFilterDocumentWasChanged; //[back/forward] private ActionToolbar myToolbar; private Window myWindow; private final PropertiesComponent myProperties; private volatile boolean myDisposed; + private final KeyListener myTreeKeyListener = new KeyListener() { + @Override + public void keyPressed(KeyEvent event) { + keyTyped(event); + } + + @Override + public void keyReleased(KeyEvent event) { + keyTyped(event); + } + + @Override + public void keyTyped(KeyEvent event) { + Object source = event.getSource(); + if (source instanceof JTree) { + JTree tree = (JTree)source; + if (tree.getInputMap().get(KeyStroke.getKeyStrokeForEvent(event)) == null) { + myFilter.myDocumentWasChanged = false; + try { + mySearch.keyEventToTextField(event); + } + finally { + if (myFilter.myDocumentWasChanged && !isFilterFieldVisible()) { + setFilterFieldVisible(true, false, false); + } + } + } + } + } + }; + public OptionsEditor(Project project, ConfigurableGroup[] groups, Configurable preselectedConfigurable) { - myProject = project; - myGroups = groups; myProperties = PropertiesComponent.getInstance(project); - myFilter = new Filter(); - myContext = new OptionsEditorContext(myFilter); - mySearch = new MySearchField() { @Override protected void onTextKeyEvent(final KeyEvent e) { @@ -137,67 +159,53 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } }; - mySearch.getTextEditor().addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - boolean hasText = mySearch.getText().length() > 0; - if (!myContext.isHoldingFilter() && hasText) { - myFilter.reenable(); - } - - if (!isSearchFieldFocused() && hasText) { - mySearch.selectText(); - } - } - }); - - final KeyListener listener = new KeyListener() { + myFilter = new SettingsFilter(project, groups, mySearch) { @Override - public void keyTyped(KeyEvent event) { - myFilterDocumentWasChanged = false; - try { - mySearch.keyEventToTextField(event); - } - finally { - if (myFilterDocumentWasChanged && !isFilterFieldVisible()) { - setFilterFieldVisible(true, false, false); - } + Configurable getConfigurable(SimpleNode node) { + if (node instanceof OptionsTree.EditorNode) { + return ((OptionsTree.EditorNode)node).getConfigurable(); } + return SettingsTreeView.getConfigurable(node); } @Override - public void keyPressed(KeyEvent event) { - keyTyped(event); + SimpleNode findNode(Configurable configurable) { + return myTreeView != null + ? myTreeView.findNode(configurable) + : myTree.findNodeFor(configurable); } @Override - public void keyReleased(KeyEvent event) { - keyTyped(event); + void updateSpotlight(boolean now) { + if (!now) { + mySpotlightUpdate.queue(new Update(this) { + @Override + public void run() { + if (!mySpotlightPainter.updateForCurrentConfigurable()) { + updateSpotlight(false); + } + } + }); + } + else if (!mySpotlightPainter.updateForCurrentConfigurable()) { + updateSpotlight(false); + } } }; - if (Registry.is("ide.file.settings.tree.new")) { - myTreeView = new SettingsTreeView(listener, getContext(), groups); + + if (Registry.is("ide.new.settings.dialog")) { + myTreeView = new SettingsTreeView(myFilter, groups); + myTreeView.myTree.addKeyListener(myTreeKeyListener); myTree = null; } else { myTreeView = null; - myTree = new OptionsTree(myProject, groups, getContext()) { - @Override - protected void onTreeKeyEvent(final KeyEvent e) { - listener.keyTyped(e); - } - }; + myTree = new OptionsTree(myFilter, groups); + myTree.addKeyListener(myTreeKeyListener); } getContext().addColleague(myTreeView != null ? myTreeView : myTree); Disposer.register(this, myTreeView != null ? myTreeView : myTree); - mySearch.addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(DocumentEvent e) { - myFilter.update(e.getType(), true, false); - } - }); - /* [back/forward] final DefaultActionGroup toolbarActions = new DefaultActionGroup(); @@ -246,7 +254,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat setLayout(new BorderLayout()); - myMainSplitter = new Splitter(false); + myMainSplitter = Registry.is("ide.new.settings.dialog") ? new OnePixelSplitter(false) : new Splitter(false); myMainSplitter.setFirstComponent(myLeftSide); myLoadingDecorator = new LoadingDecorator(myOwnDetails.getComponent(), this, 150); @@ -264,13 +272,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat mySpotlightUpdate = new MergingUpdateQueue("OptionsSpotlight", 200, false, this, this, this); if (preselectedConfigurable != null) { - if (myTreeView != null) { - myTreeView.select(preselectedConfigurable); - } - else { - myTree.select(preselectedConfigurable); - } - } else { + selectInTree(preselectedConfigurable); + } + else { if (myTreeView != null) { myTreeView.selectFirst(); } @@ -322,6 +326,12 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat }); } + private ActionCallback selectInTree(Configurable configurable) { + return myTreeView != null + ? myTreeView.select(configurable) + : myTree.select(configurable); + } + /** @see #select(com.intellij.openapi.options.Configurable) */ @Deprecated public ActionCallback select(Class<? extends Configurable> configurableClass) { @@ -354,18 +364,17 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } public ActionCallback select(Configurable configurable) { - if (StringUtil.isEmpty(mySearch.getText())) { + if (myFilter.getFilterText().isEmpty()) { return select(configurable, ""); - } else { - return myFilter.refilterFor(mySearch.getText(), true, true); + } + else { + return myFilter.update(true, true); } } public ActionCallback select(Configurable configurable, final String text) { - myFilter.refilterFor(text, false, true); - return myTreeView != null - ? myTreeView.select(configurable) - : myTree.select(configurable); + myFilter.update(text, false, true); + return selectInTree(configurable); } private float readProportion(final float defaultValue, final String propertyName) { @@ -390,7 +399,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat if (configurable == null) { myOwnDetails.setContent(null); - updateSpotlight(true); + myFilter.updateSpotlight(true); checkModified(oldConfigurable); result.setDone(); @@ -417,7 +426,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat if (myTreeView != null) { myOwnDetails.forProject(myTreeView.findConfigurableProject(configurable)); } - else if (Registry.is("ide.file.settings.order.new")) { + else if (Registry.is("ide.new.settings.dialog")) { myOwnDetails.forProject(myTree.getConfigurableProject(configurable)); } @@ -430,7 +439,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myLoadingDecorator.stopLoading(); - updateSpotlight(false); + myFilter.updateSpotlight(false); checkModified(oldConfigurable); checkModified(configurable); @@ -480,7 +489,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat ((ApplicationEx)app).runEdtSafeAction(new Runnable() { @Override public void run() { - if (myProject.isDisposed()) { + if (myFilter.myProject.isDisposed()) { result.setRejected(); } else { @@ -537,26 +546,6 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat return result; } - - private void updateSpotlight(boolean now) { - if (now) { - final boolean success = mySpotlightPainter.updateForCurrentConfigurable(); - if (!success) { - updateSpotlight(false); - } - } else { - mySpotlightUpdate.queue(new Update(this) { - @Override - public void run() { - final boolean success = mySpotlightPainter.updateForCurrentConfigurable(); - if (!success) { - updateSpotlight(false); - } - } - }); - } - } - private String[] getBannerText(Configurable configurable) { if (myTreeView != null) { return myTreeView.getPathNames(configurable); @@ -712,7 +701,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat @Override public boolean isEnabled() { - return myContext.isModified(myConfigurable) || getContext().getErrors().containsKey(myConfigurable); + return myFilter.myContext.isModified(myConfigurable) || getContext().getErrors().containsKey(myConfigurable); } } @@ -849,12 +838,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat getContext().fireErrorsChanged(errors, null); if (!errors.isEmpty()) { - if (myTreeView != null) { - myTreeView.select(errors.keySet().iterator().next()); - } - else { - myTree.select(errors.keySet().iterator().next()); - } + selectInTree(errors.keySet().iterator().next()); } } @@ -868,7 +852,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } public JComponent getPreferredFocusedComponent() { - return mySearch;//myTree.getTree(); + return myTreeView != null ? myTreeView.myTree : mySearch;//myTree.getTree(); } @Override @@ -876,145 +860,6 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat return new Dimension(1200, 768); } - private class Filter extends ElementFilter.Active.Impl<SimpleNode> { - - SearchableOptionsRegistrar myIndex = SearchableOptionsRegistrar.getInstance(); - Set<Configurable> myFiltered = null; - ConfigurableHit myHits; - - boolean myUpdateEnabled = true; - private Configurable myLastSelected; - - @Override - public boolean shouldBeShowing(final SimpleNode value) { - if (myFiltered == null) return true; - - if (value instanceof OptionsTree.EditorNode) { - final OptionsTree.EditorNode node = (OptionsTree.EditorNode)value; - return myFiltered.contains(node.getConfigurable()) || isChildOfNameHit(node); - } - - return SettingsTreeView.isFiltered(myFiltered, myHits, value); - } - - private boolean isChildOfNameHit(OptionsTree.EditorNode node) { - if (myHits != null) { - OptionsTree.Base eachParent = node; - while (eachParent != null) { - if (eachParent instanceof OptionsTree.EditorNode) { - final OptionsTree.EditorNode eachEditorNode = (OptionsTree.EditorNode)eachParent; - if (myHits.getNameFullHits().contains(eachEditorNode.myConfigurable)) return true; - } - eachParent = (OptionsTree.Base)eachParent.getParent(); - } - - return false; - } - - return false; - } - - public ActionCallback refilterFor(String text, boolean adjustSelection, final boolean now) { - try { - myUpdateEnabled = false; - mySearch.setText(text); - } - finally { - myUpdateEnabled = true; - } - - return update(DocumentEvent.EventType.CHANGE, adjustSelection, now); - } - - public void clearTemporary() { - myContext.setHoldingFilter(false); - updateSpotlight(false); - } - - public void reenable() { - myContext.setHoldingFilter(true); - updateSpotlight(false); - } - - public ActionCallback update(DocumentEvent.EventType type, boolean adjustSelection, boolean now) { - if (!myUpdateEnabled) return new ActionCallback.Rejected(); - - final String text = mySearch.getText(); - if (getFilterText().length() == 0) { - myContext.setHoldingFilter(false); - myFiltered = null; - } else { - myContext.setHoldingFilter(true); - myHits = myIndex.getConfigurables(myGroups, type, myFiltered, text, myProject); - myFiltered = myHits.getAll(); - } - - if (myFiltered != null && myFiltered.isEmpty()) { - mySearch.getTextEditor().setBackground(LightColors.RED); - } else { - mySearch.getTextEditor().setBackground(UIUtil.getTextFieldBackground()); - } - - - final Configurable current = getContext().getCurrentConfigurable(); - - boolean shouldMoveSelection = true; - - if (myHits != null && (myHits.getNameFullHits().contains(current) || myHits.getContentHits().contains(current))) { - shouldMoveSelection = false; - } - - if (shouldMoveSelection && type != DocumentEvent.EventType.INSERT && (myFiltered == null || myFiltered.contains(current))) { - shouldMoveSelection = false; - } - - Configurable toSelect = adjustSelection ? current : null; - if (shouldMoveSelection && myHits != null) { - if (!myHits.getNameHits().isEmpty()) { - toSelect = suggestToSelect(myHits.getNameHits(), myHits.getNameFullHits()); - } else if (!myHits.getContentHits().isEmpty()) { - toSelect = suggestToSelect(myHits.getContentHits(), null); - } - } - - updateSpotlight(false); - - if ((myFiltered == null || !myFiltered.isEmpty()) && toSelect == null && myLastSelected != null) { - toSelect = myLastSelected; - myLastSelected = null; - } - - if (toSelect == null && current != null) { - myLastSelected = current; - } - - SimpleNode node = !adjustSelection ? null : myTreeView != null ? myTreeView.findNode(toSelect) : myTree.findNodeFor(toSelect); - final ActionCallback callback = fireUpdate(node, adjustSelection, now); - - myFilterDocumentWasChanged = true; - - return callback; - } - - private boolean isEmptyParent(Configurable configurable) { - return configurable instanceof SearchableConfigurable.Parent && !((SearchableConfigurable.Parent)configurable).hasOwnContent(); - } - - @Nullable - private Configurable suggestToSelect(Set<Configurable> set, Set<Configurable> fullHits) { - Configurable candidate = null; - for (Configurable each : set) { - if (fullHits != null && fullHits.contains(each)) return each; - if (!isEmptyParent(each) && candidate == null) { - candidate = each; - } - } - - return candidate; - } - - } - @Override public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) { final Configurable config = (Configurable)place.getPath("configurable"); @@ -1022,15 +867,10 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat final ActionCallback result = new ActionCallback(); - myFilter.refilterFor(filter, false, true).doWhenDone(new Runnable() { + myFilter.update(filter, false, true).doWhenDone(new Runnable() { @Override public void run() { - if (myTreeView != null) { - myTreeView.select(config).notifyWhenDone(result); - } - else { - myTree.select(config).notifyWhenDone(result); - } + selectInTree(config).notifyWhenDone(result); } }); @@ -1041,7 +881,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat public void queryPlace(@NotNull final Place place) { final Configurable current = getContext().getCurrentConfigurable(); place.putPath("configurable", current); - place.putPath("filter", getFilterText()); + place.putPath("filter", myFilter.getFilterText()); if (current instanceof Place.Navigator) { ((Place.Navigator)current).queryPlace(place); @@ -1060,7 +900,6 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myProperties.setValue(MAIN_SPLITTER_PROPORTION, String.valueOf(myMainSplitter.getProportion())); myProperties.setValue(DETAILS_SPLITTER_PROPORTION, String.valueOf(myContentWrapper.myLastSplitterProportion)); - myProperties.setValue(SEARCH_VISIBLE, Boolean.valueOf(isFilterFieldVisible()).toString()); Toolkit.getDefaultToolkit().removeAWTEventListener(this); @@ -1087,7 +926,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } public OptionsEditorContext getContext() { - return myContext; + return myFilter.myContext; } private class MyColleague extends OptionsEditorColleague.Adapter { @@ -1137,7 +976,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat final MouseEvent me = (MouseEvent)event; if (SwingUtilities.isDescendingFrom(me.getComponent(), SwingUtilities.getWindowAncestor(myContentWrapper)) || isPopupOverEditor(me.getComponent())) { queueModificationCheck(); - myFilter.clearTemporary(); + myFilter.setHoldingFilter(false); } } else if (event.getID() == KeyEvent.KEY_PRESSED || event.getID() == KeyEvent.KEY_RELEASED) { @@ -1175,6 +1014,23 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat private MySearchField() { super(false); addKeyListener(new KeyAdapter() {}); + if (Registry.is("ide.new.settings.dialog")) { + final JTextField editor = getTextEditor(); + if (!SystemInfo.isMac) { + editor.putClientProperty("JTextField.variant", "search"); + if (!(editor.getUI() instanceof DarculaTextFieldUI)) { + editor.setUI((DarculaTextFieldUI)DarculaTextFieldUI.createUI(editor)); + editor.setBorder(new DarculaTextBorder()); + } + } + setBackground(UIUtil.getSidePanelColor()); + setBorder(new EmptyBorder(5, 10, 2, 10)); + } + } + + @Override + protected boolean isSearchControlUISupported() { + return true; } @Override @@ -1236,7 +1092,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat return ApplicationManager.getApplication().isUnitTestMode(); } - String text = getFilterText(); + String text = myFilter.getFilterText(); try { final boolean sameText = @@ -1263,14 +1119,7 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat myVisible = true;//myContext.isHoldingFilter(); runnable.run(); - boolean pushFilteringFurther = true; - if (sameText) { - pushFilteringFurther = false; - } else { - if (myFilter.myHits != null) { - pushFilteringFurther = !myFilter.myHits.getNameHits().contains(current); - } - } + boolean pushFilteringFurther = !sameText && !myFilter.contains(current); final Runnable ownSearch = searchable.enableSearch(text); if (pushFilteringFurther && ownSearch != null) { @@ -1295,10 +1144,6 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat } } - private String getFilterText() { - return mySearch.getText() != null ? mySearch.getText().trim() : ""; - } - private static class SearachableWrappper implements SearchableConfigurable { private final Configurable myConfigurable; @@ -1366,6 +1211,49 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat abstract void setText(final String[] bannerText); } + /** + * Returns default view for the specified configurable. + * It uses the configurable identifier to retrieve description. + * + * @param searchable the configurable that does not have any view + * @return default view for the specified configurable + */ + private JComponent createDefaultComponent(SearchableConfigurable searchable) { + JPanel box = new JPanel(); + box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); + try { + box.add(new JLabel(getDefaultDescription(searchable))); + } + catch (AssertionError error) { + return null; // description is not set + } + if (searchable instanceof Configurable.Composite) { + box.add(Box.createVerticalStrut(10)); + Configurable.Composite composite = (Configurable.Composite)searchable; + for (final Configurable configurable : composite.getConfigurables()) { + box.add(new LinkLabel(configurable.getDisplayName(), AllIcons.Ide.Link) { + @Override + public void doClick() { + select(configurable, null); + } + }); + } + } + return box; + } + + @NotNull + private static String getDefaultDescription(SearchableConfigurable configurable) { + String key = configurable.getId() + ".settings.description"; + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper) configurable; + ConfigurableEP ep = wrapper.getExtensionPoint(); + ResourceBundle resourceBundle = AbstractBundle.getResourceBundle(ep.bundle, ep.getPluginDescriptor().getPluginClassLoader()); + return CommonBundle.message(resourceBundle, key); + } + return OptionsBundle.message(key); + } + private class Simple extends ConfigurableContent { JComponent myComponent; Configurable myConfigurable; @@ -1373,7 +1261,9 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat Simple(final Configurable configurable) { myConfigurable = configurable; myComponent = configurable.createComponent(); - + if (myComponent == null && configurable instanceof SearchableConfigurable) { + myComponent = createDefaultComponent((SearchableConfigurable)configurable); + } if (myComponent != null) { final Object clientProperty = myComponent.getClientProperty(NOT_A_NEW_COMPONENT); if (clientProperty != null && ApplicationManager.getApplication().isInternal()) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java index 175b994122ef..aee9e889fdf1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java @@ -37,6 +37,7 @@ import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure; import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; +import com.intellij.util.ui.tree.WideSelectionTreeUI; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import org.jetbrains.annotations.NotNull; @@ -49,33 +50,32 @@ import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.TreeUI; -import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import java.awt.*; -import java.awt.event.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; import java.util.*; import java.util.List; public class OptionsTree extends JPanel implements Disposable, OptionsEditorColleague { - Project myProject; + private final SettingsFilter myFilter; final SimpleTree myTree; List<ConfigurableGroup> myGroups; FilteringTreeBuilder myBuilder; Root myRoot; - OptionsEditorContext myContext; Map<Configurable, EditorNode> myConfigurable2Node = new HashMap<Configurable, EditorNode>(); MergingUpdateQueue mySelection; private final OptionsTree.Renderer myRenderer; - public OptionsTree(Project project, ConfigurableGroup[] groups, OptionsEditorContext context) { - myProject = project; + public OptionsTree(SettingsFilter filter, ConfigurableGroup... groups) { + myFilter = filter; myGroups = Arrays.asList(groups); - myContext = context; - myRoot = new Root(); final SimpleTreeStructure structure = new SimpleTreeStructure() { @@ -132,35 +132,8 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } } }); - myTree.addKeyListener(new KeyListener() { - public void keyTyped(final KeyEvent e) { - _onTreeKeyEvent(e); - } - - public void keyPressed(final KeyEvent e) { - _onTreeKeyEvent(e); - } - - public void keyReleased(final KeyEvent e) { - _onTreeKeyEvent(e); - } - }); - } - - protected void _onTreeKeyEvent(KeyEvent e) { - final KeyStroke stroke = KeyStroke.getKeyStrokeForEvent(e); - - final Object action = myTree.getInputMap().get(stroke); - if (action == null) { - onTreeKeyEvent(e); - } - } - - protected void onTreeKeyEvent(KeyEvent e) { - } - ActionCallback select(@Nullable Configurable configurable) { return queueSelection(configurable); } @@ -191,7 +164,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl if (configurable == null) { myTree.getSelectionModel().clearSelection(); - myContext.fireSelected(null, OptionsTree.this); + myFilter.myContext.fireSelected(null, OptionsTree.this); } else { myBuilder.getReady(this).doWhenDone(new Runnable() { @@ -232,7 +205,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } private void fireSelected(Configurable configurable, final ActionCallback callback) { - myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); + myFilter.myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); } @@ -297,7 +270,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl myRendererComponent.setOpaqueActive(false); mySeparator = new GroupSeparator(); - myRendererComponent.add(Registry.is("ide.file.settings.order.new") ? mySeparator : mySeparatorComponent, BorderLayout.NORTH); + myRendererComponent.add(Registry.is("ide.new.settings.dialog") ? mySeparator : mySeparatorComponent, BorderLayout.NORTH); final NonOpaquePanel content = new NonOpaquePanel(new BorderLayout()); myHandle = new JLabel("", SwingConstants.CENTER); @@ -401,12 +374,12 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl myTextLabel.setForeground(selected ? UIUtil.getTreeSelectionForeground() : fg); myTextLabel.setOpaque(selected); - if (Registry.is("ide.file.settings.order.new")) { + if (Registry.is("ide.new.settings.dialog")) { myTextLabel.setBorder(new EmptyBorder(1,2,1,0)); } Project project = null; - if (base != null && Registry.is("ide.file.settings.order.new")) { + if (base != null && Registry.is("ide.new.settings.dialog")) { SimpleNode parent = base.getParent(); if (parent == myRoot) { project = getConfigurableProject(base); // show icon for top-level nodes @@ -433,6 +406,9 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl } else { myProjectIcon.setVisible(false); } + if (Registry.is("ide.new.settings.dialog")) { + result.setBackground(selected ? UIUtil.getTreeSelectionBackground() : UIUtil.getSidePanelColor()); + } return result; } @@ -538,7 +514,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl final List<EditorNode> result = new ArrayList<EditorNode>(kids.length); for (Configurable child : kids) { result.add(new EditorNode(parent, child, group)); - myContext.registerKid(configurable, child); + myFilter.myContext.registerKid(configurable, child); } return result; // TODO: DECIDE IF INNERS SHOULD BE SORTED: sort(result); } @@ -602,12 +578,12 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl @Override boolean isModified() { - return myContext.getModified().contains(myConfigurable); + return myFilter.myContext.getModified().contains(myConfigurable); } @Override boolean isError() { - return myContext.getErrors().containsKey(myConfigurable); + return myFilter.myContext.getErrors().containsKey(myConfigurable); } } @@ -757,7 +733,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl super.processMouseEvent(e); } - private class MyTreeUi extends BasicTreeUI { + private class MyTreeUi extends WideSelectionTreeUI { @Override public void toggleExpandState(final TreePath path) { @@ -810,7 +786,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl boolean myWasHoldingFilter; public MyBuilder(SimpleTreeStructure structure) { - super(OptionsTree.this.myTree, myContext.getFilter(), structure, new WeightBasedComparator(false)); + super(myTree, myFilter, structure, new WeightBasedComparator(false)); myTree.addTreeExpansionListener(new TreeExpansionListener() { public void treeExpanded(TreeExpansionEvent event) { invalidateExpansions(); @@ -835,7 +811,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl @Override public boolean isAutoExpandNode(final NodeDescriptor nodeDescriptor) { - return myContext.isHoldingFilter(); + return myFilter.myContext.isHoldingFilter(); } @Override @@ -846,21 +822,21 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl @Override protected ActionCallback refilterNow(Object preferredSelection, boolean adjustSelection) { final List<Object> toRestore = new ArrayList<Object>(); - if (myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { + if (myFilter.myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { myToExpandOnResetFilter = myBuilder.getUi().getExpandedElements(); - } else if (!myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { + } else if (!myFilter.myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { toRestore.addAll(myToExpandOnResetFilter); myToExpandOnResetFilter = null; } - myWasHoldingFilter = myContext.isHoldingFilter(); + myWasHoldingFilter = myFilter.myContext.isHoldingFilter(); ActionCallback result = super.refilterNow(preferredSelection, adjustSelection); myRefilteringNow = true; return result.doWhenDone(new Runnable() { public void run() { myRefilteringNow = false; - if (!myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { + if (!myFilter.myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { restoreExpandedState(toRestore); } } @@ -968,7 +944,7 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl public void paint(Graphics g) { super.paint(g); - if (Registry.is("ide.file.settings.order.new")) { + if (Registry.is("ide.new.settings.dialog")) { ConfigurableGroup group = getGroup(GroupSeparator.SPACE + mySeparator.getFont().getSize()); if (group != null && group == getGroup(-GroupSeparator.SPACE)) { mySeparator.configure(group, false); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.form b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.form deleted file mode 100644 index bfd8e1f0e416..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.form +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.openapi.options.newEditor.PreferencesDialog"> - <grid id="27dc6" binding="myRoot" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> - <margin top="0" left="0" bottom="0" right="0"/> - <constraints> - <xy x="20" y="20" width="500" height="400"/> - </constraints> - <properties/> - <border type="none"/> - <children> - <grid id="81fb6" binding="myTopPanel" custom-create="true" layout-manager="BorderLayout" hgap="0" vgap="0"> - <constraints> - <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"> - <preferred-size width="-1" height="50"/> - </grid> - </constraints> - <properties/> - <border type="none"/> - <children/> - </grid> - <vspacer id="420b1"> - <constraints> - <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> - </constraints> - </vspacer> - <grid id="b87b5" binding="myCenterPanel" layout-manager="BorderLayout" hgap="0" vgap="0"> - <constraints> - <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> - </constraints> - <properties/> - <border type="none"/> - <children/> - </grid> - <hspacer id="e2eff"> - <constraints> - <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/> - </constraints> - </hspacer> - </children> - </grid> -</form> diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.java deleted file mode 100644 index 8ed3527e090a..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/PreferencesDialog.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2000-2013 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.openapi.options.newEditor; - -import com.intellij.icons.AllIcons; -import com.intellij.openapi.options.ConfigurableGroup; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.ui.Gray; -import com.intellij.ui.SearchTextField; -import com.intellij.ui.border.CustomLineBorder; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; -import java.awt.*; - -/** - * @author Konstantin Bulenkov - */ -public class PreferencesDialog extends DialogWrapper { - private JPanel myRoot; - private JPanel myTopPanel; - private JPanel myCenterPanel; - private SearchTextField mySearchTextField; - - public PreferencesDialog(@Nullable Project project, ConfigurableGroup[] groups) { - super(project); - init(); - ((JDialog)getPeer().getWindow()).setUndecorated(true); - if (SystemInfo.isMac) { - ((JComponent)((JDialog)getPeer().getWindow()).getContentPane()).setBorder(new EmptyBorder(0, 0, 0, 0)); - } - else { - ((JComponent)((JDialog)getPeer().getWindow()).getContentPane()).setBorder(new LineBorder(Gray._140, 1)); - } - - setTitle("Preferences"); - } - - @Nullable - @Override - protected JComponent createCenterPanel() { - final JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); - panel.setBorder(null); - panel.add(createApplicationSettings()); - panel.add(createProjectSettings()); - panel.add(createEditorSettings()); - panel.add(createOtherSettings()); - panel.setPreferredSize(new Dimension(700, 370)); - myCenterPanel.add(panel, BorderLayout.CENTER); - return myRoot; - } - - @Nullable - @Override - public JComponent getPreferredFocusedComponent() { - return mySearchTextField.getTextEditor(); - } - - private static JComponent createEditorSettings() { - final LabeledButtonsPanel panel = new LabeledButtonsPanel("Editor"); - panel.addButton(new PreferenceButton("Editor", AllIcons.Preferences.Editor)); - panel.addButton(new PreferenceButton("Code Style", AllIcons.Preferences.CodeStyle)); - panel.setBackground(Gray._229); - panel.setBorder(new CustomLineBorder(Gray._223, 0, 0, 1, 0)); - return panel; - } - - private static JComponent createProjectSettings() { - final LabeledButtonsPanel panel = new LabeledButtonsPanel("Project"); - panel.addButton(new PreferenceButton("Compiler", AllIcons.Preferences.Compiler)); - panel.addButton(new PreferenceButton("Version Control", AllIcons.Preferences.VersionControl)); - panel.addButton(new PreferenceButton("File Colors", AllIcons.Preferences.FileColors)); - panel.addButton(new PreferenceButton("Scopes", AllIcons.Preferences.Editor)); - panel.setBackground(Gray._236); - panel.setBorder(new CustomLineBorder(Gray._223, 0, 0, 1, 0)); - return panel; - } - - private static JComponent createApplicationSettings() { - final LabeledButtonsPanel panel = new LabeledButtonsPanel("IDE"); - panel.addButton(new PreferenceButton("Appearance", AllIcons.Preferences.Appearance)); - panel.addButton(new PreferenceButton("General", AllIcons.Preferences.General)); - panel.addButton(new PreferenceButton("Keymap", AllIcons.Preferences.Keymap)); - panel.addButton(new PreferenceButton("File Types", AllIcons.Preferences.FileTypes)); - panel.setBackground(Gray._229); - panel.setBorder(new CustomLineBorder(Gray._223, 0, 0, 1, 0)); - return panel; - } - - private static JComponent createOtherSettings() { - final LabeledButtonsPanel panel = new LabeledButtonsPanel("Other"); - panel.addButton(new PreferenceButton("Plugins", AllIcons.Preferences.Plugins)); - panel.addButton(new PreferenceButton("Updates", AllIcons.Preferences.Updates)); - panel.setBackground(Gray._236); - panel.setBorder(new CustomLineBorder(Gray._223, 0, 0, 1, 0)); - return panel; - } - - - @Nullable - @Override - protected JComponent createSouthPanel() { - if (SystemInfo.isMac) { - return null; - } - final JComponent panel = super.createSouthPanel(); - if (panel != null) { - panel.setBorder(new EmptyBorder(5, 5, 10, 20)); - } - return panel; - } - - private void createUIComponents() { - myTopPanel = new JPanel(new BorderLayout()) { - @Override - protected void paintComponent(Graphics g) { - ((Graphics2D)g).setPaint(new GradientPaint(0, 0, Gray._206, 0, getHeight() - 1, Gray._172)); - g.fillRect(0, 0, getWidth(), getHeight()); - g.setColor(Gray._145); - g.drawLine(0, getHeight() - 2, getWidth(), getHeight() - 2); - g.setColor(Gray._103); - g.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1); - } - }; - final JLabel title = new JLabel("Preferences"); - if (!SystemInfo.isMac) { - title.setFont(UIUtil.getLabelFont().deriveFont(Font.BOLD, 14)); - } - else { - title.setFont(new Font("Lucuda Grande", Font.PLAIN, 12)); - } - title.setHorizontalTextPosition(SwingConstants.CENTER); - title.setHorizontalAlignment(SwingConstants.CENTER); - title.setVerticalAlignment(SwingConstants.TOP); - myTopPanel.add(title, BorderLayout.NORTH); - mySearchTextField = new SearchTextField(); - mySearchTextField.setOpaque(false); - myTopPanel.add(mySearchTextField, BorderLayout.EAST); - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsFilter.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsFilter.java new file mode 100644 index 000000000000..1885a852c5da --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsFilter.java @@ -0,0 +1,211 @@ +/* + * Copyright 2000-2014 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.openapi.options.newEditor; + +import com.intellij.ide.ui.search.ConfigurableHit; +import com.intellij.ide.ui.search.SearchableOptionsRegistrar; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.ui.DocumentAdapter; +import com.intellij.ui.LightColors; +import com.intellij.ui.SearchTextField; +import com.intellij.ui.speedSearch.ElementFilter; +import com.intellij.ui.treeStructure.SimpleNode; +import com.intellij.util.ui.UIUtil; + +import javax.swing.event.DocumentEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Set; + +abstract class SettingsFilter extends ElementFilter.Active.Impl<SimpleNode> { + final OptionsEditorContext myContext = new OptionsEditorContext(this); + final Project myProject; + + boolean myDocumentWasChanged; + + private final SearchTextField mySearch; + private final ConfigurableGroup[] myGroups; + + private SearchableOptionsRegistrar myRegistrar = SearchableOptionsRegistrar.getInstance(); + private Set<Configurable> myFiltered; + private ConfigurableHit myHits; + + private boolean myUpdateRejected; + private Configurable myLastSelected; + + SettingsFilter(Project project, ConfigurableGroup[] groups, SearchTextField search) { + myProject = project; + myGroups = groups; + mySearch = search; + mySearch.addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent event) { + update(event.getType(), true, false); + } + }); + mySearch.getTextEditor().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent event) { + if (!mySearch.getText().isEmpty()) { + if (!myContext.isHoldingFilter()) { + setHoldingFilter(true); + } + if (!mySearch.getTextEditor().isFocusOwner()) { + mySearch.selectText(); + } + } + } + }); + } + + abstract Configurable getConfigurable(SimpleNode node); + + abstract SimpleNode findNode(Configurable configurable); + + abstract void updateSpotlight(boolean now); + + @Override + public boolean shouldBeShowing(SimpleNode node) { + if (myFiltered != null) { + Configurable configurable = getConfigurable(node); + if (configurable != null) { + if (!myFiltered.contains(configurable)) { + if (myHits != null) { + Set<Configurable> configurables = myHits.getNameFullHits(); + while (node != null) { + if (configurable != null) { + if (configurables.contains(configurable)) { + return true; + } + } + node = node.getParent(); + configurable = getConfigurable(node); + } + } + return false; + } + } + } + return true; + } + + String getFilterText() { + String text = mySearch.getText(); + return text == null ? "" : text.trim(); + } + + void setHoldingFilter(boolean holding) { + myContext.setHoldingFilter(holding); + updateSpotlight(false); + } + + boolean contains(Configurable configurable) { + return myHits != null && myHits.getNameHits().contains(configurable); + } + + ActionCallback update(boolean adjustSelection, boolean now) { + return update(DocumentEvent.EventType.CHANGE, adjustSelection, now); + } + + ActionCallback update(String text, boolean adjustSelection, boolean now) { + try { + myUpdateRejected = true; + mySearch.setText(text); + } + finally { + myUpdateRejected = false; + } + return update(adjustSelection, now); + } + + private ActionCallback update(DocumentEvent.EventType type, boolean adjustSelection, boolean now) { + if (myUpdateRejected) { + return new ActionCallback.Rejected(); + } + String text = getFilterText(); + if (text.isEmpty()) { + myContext.setHoldingFilter(false); + myFiltered = null; + } + else { + myContext.setHoldingFilter(true); + myHits = myRegistrar.getConfigurables(myGroups, type, myFiltered, text, myProject); + myFiltered = myHits.getAll(); + } + mySearch.getTextEditor().setBackground(myFiltered != null && myFiltered.isEmpty() + ? LightColors.RED + : UIUtil.getTextFieldBackground()); + + + Configurable current = myContext.getCurrentConfigurable(); + + boolean shouldMoveSelection = myHits == null || ( + !myHits.getNameFullHits().contains(current) && + !myHits.getContentHits().contains(current)); + + if (shouldMoveSelection && type != DocumentEvent.EventType.INSERT && (myFiltered == null || myFiltered.contains(current))) { + shouldMoveSelection = false; + } + + Configurable candidate = adjustSelection ? current : null; + if (shouldMoveSelection && myHits != null) { + if (!myHits.getNameHits().isEmpty()) { + candidate = findConfigurable(myHits.getNameHits(), myHits.getNameFullHits()); + } + else if (!myHits.getContentHits().isEmpty()) { + candidate = findConfigurable(myHits.getContentHits(), null); + } + } + updateSpotlight(false); + + if ((myFiltered == null || !myFiltered.isEmpty()) && candidate == null && myLastSelected != null) { + candidate = myLastSelected; + myLastSelected = null; + } + if (candidate == null && current != null) { + myLastSelected = current; + } + SimpleNode node = !adjustSelection ? null : findNode(candidate); + ActionCallback callback = fireUpdate(node, adjustSelection, now); + myDocumentWasChanged = true; + return callback; + } + + private static Configurable findConfigurable(Set<Configurable> configurables, Set<Configurable> hits) { + Configurable candidate = null; + for (Configurable configurable : configurables) { + if (hits != null && hits.contains(configurable)) { + return configurable; + } + if (candidate == null && !isEmptyParent(configurable)) { + candidate = configurable; + } + } + return candidate; + } + + private static boolean isEmptyParent(Configurable configurable) { + if (configurable instanceof SearchableConfigurable.Parent) { + SearchableConfigurable.Parent parent = (SearchableConfigurable.Parent)configurable; + return !parent.hasOwnContent(); + } + return false; + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java index 9269da7ab967..e10367d583bf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java @@ -16,7 +16,6 @@ package com.intellij.openapi.options.newEditor; import com.intellij.icons.AllIcons; -import com.intellij.ide.ui.search.ConfigurableHit; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.Disposable; import com.intellij.openapi.options.*; @@ -26,55 +25,68 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; import com.intellij.ui.*; -import com.intellij.ui.treeStructure.*; +import com.intellij.ui.treeStructure.CachingSimpleNode; +import com.intellij.ui.treeStructure.SimpleNode; +import com.intellij.ui.treeStructure.SimpleTree; +import com.intellij.ui.treeStructure.SimpleTreeStructure; import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder; import com.intellij.ui.treeStructure.filtered.FilteringTreeStructure; import com.intellij.util.ArrayUtil; +import com.intellij.util.ui.ButtonlessScrollBarUI; import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; +import com.intellij.util.ui.tree.WideSelectionTreeUI; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.awt.*; -import java.awt.event.*; -import java.util.*; -import java.util.List; import javax.swing.*; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.TreeUI; -import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; +import java.awt.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.*; +import java.util.List; /** * @author Sergey.Malenkov */ final class SettingsTreeView extends JComponent implements Disposable, OptionsEditorColleague { + private static final Color NORMAL_NODE = new JBColor(Gray._60, Gray._140); + private static final Color WRONG_CONTENT = JBColor.RED; + private static final Color MODIFIED_CONTENT = JBColor.BLUE; + final SimpleTree myTree; final FilteringTreeBuilder myBuilder; - private final OptionsEditorContext myContext; + private final SettingsFilter myFilter; private final MyRoot myRoot; private final JScrollPane myScroller; private JLabel mySeparator; private final MyRenderer myRenderer = new MyRenderer(); private final IdentityHashMap<Configurable, MyNode> myConfigurableToNodeMap = new IdentityHashMap<Configurable, MyNode>(); - private final MergingUpdateQueue myQueue = new MergingUpdateQueue("OptionsTree", 150, false, this, this, this).setRestartTimerOnAdd(true); + private final MergingUpdateQueue myQueue = new MergingUpdateQueue("SettingsTreeView", 150, false, this, this, this) + .setRestartTimerOnAdd(true); private Configurable myQueuedConfigurable; - SettingsTreeView(final KeyListener listener, OptionsEditorContext context, ConfigurableGroup... groups) { - myContext = context; + SettingsTreeView(SettingsFilter filter, ConfigurableGroup... groups) { + myFilter = filter; myRoot = new MyRoot(groups); - myTree = new MyTree(); + myTree.putClientProperty(WideSelectionTreeUI.TREE_TABLE_TREE_KEY, Boolean.TRUE); + myTree.setBackground(UIUtil.getSidePanelColor()); myTree.getInputMap().clear(); TreeUtil.installActions(myTree); @@ -88,8 +100,11 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myTree.setRootVisible(false); myTree.setShowsRootHandles(false); - myScroller = ScrollPaneFactory.createScrollPane(myTree); - myScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + myScroller = ScrollPaneFactory.createScrollPane(myTree, true); + myScroller.getVerticalScrollBar().setUI(ButtonlessScrollBarUI.createTransparent()); + myScroller.setBackground(UIUtil.getSidePanelColor()); + myScroller.getViewport().setBackground(UIUtil.getSidePanelColor()); + myScroller.getVerticalScrollBar().setBackground(UIUtil.getSidePanelColor()); add(myScroller); myTree.addComponentListener(new ComponentAdapter() { @@ -116,29 +131,6 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } }); - myTree.addKeyListener(new KeyListener() { - public void keyTyped(KeyEvent event) { - if (listener != null && isValid(event)) { - listener.keyTyped(event); - } - } - - public void keyPressed(KeyEvent event) { - if (listener != null && isValid(event)) { - listener.keyPressed(event); - } - } - - public void keyReleased(KeyEvent event) { - if (listener != null && isValid(event)) { - listener.keyReleased(event); - } - } - - private boolean isValid(KeyEvent event) { - return null == myTree.getInputMap().get(KeyStroke.getKeyStrokeForEvent(event)); - } - }); myBuilder = new MyBuilder(new SimpleTreeStructure.Impl(myRoot)); myBuilder.setFilteringMerge(300, null); Disposer.register(this, myBuilder); @@ -158,9 +150,15 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd return ArrayUtil.toStringArray(path); } + static Configurable getConfigurable(SimpleNode node) { + return node instanceof MyNode + ? ((MyNode)node).myConfigurable + : null; + } + @Nullable - SimpleNode findNode(Configurable toSelect) { - return myConfigurableToNodeMap.get(toSelect); + SimpleNode findNode(Configurable configurable) { + return myConfigurableToNodeMap.get(configurable); } @Nullable @@ -250,24 +248,6 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd : null; } - static boolean isFiltered(Set<Configurable> configurables, ConfigurableHit hits, SimpleNode value) { - if (value instanceof MyNode && !configurables.contains(((MyNode)value).myConfigurable)) { - if (hits != null) { - configurables = hits.getNameFullHits(); - while (value != null) { - if (value instanceof MyNode) { - if (configurables.contains(((MyNode)value).myConfigurable)) { - return true; - } - } - value = value.getParent(); - } - } - return false; - } - return true; - } - @Override public void doLayout() { myScroller.setBounds(0, 0, getWidth(), getHeight()); @@ -277,34 +257,34 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd public void paint(Graphics g) { super.paint(g); + if (0 == myTree.getY()) { + return; // separator is not needed without scrolling + } if (mySeparator == null) { mySeparator = new JLabel(); + mySeparator.setForeground(NORMAL_NODE); mySeparator.setFont(UIUtil.getLabelFont()); mySeparator.setFont(getFont().deriveFont(Font.BOLD)); } - ConfigurableGroup group = findConfigurableGroupAt(0, 5 + mySeparator.getFont().getSize()); - if (group != null && group == findConfigurableGroupAt(0, -5)) { - int offset = UIUtil.isUnderNativeMacLookAndFeel() ? 1 : 3; - mySeparator.setBorder(BorderFactory.createEmptyBorder(offset, 18, offset, 3)); + int height = mySeparator.getPreferredSize().height; + ConfigurableGroup group = findConfigurableGroupAt(0, height); + if (group != null && group == findConfigurableGroupAt(0, -myRenderer.getSeparatorHeight())) { + mySeparator.setBorder(BorderFactory.createEmptyBorder(0, 18, 0, 0)); mySeparator.setText(group.getDisplayName()); Rectangle bounds = myScroller.getViewport().getBounds(); - int height = mySeparator.getPreferredSize().height; if (bounds.height > height) { bounds.height = height; } g.setColor(myTree.getBackground()); + g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); if (g instanceof Graphics2D) { - int h = bounds.height / 4; - int y = bounds.y + bounds.height - h; - g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height - h); + int h = 4; // gradient height + int y = bounds.y + bounds.height; ((Graphics2D)g).setPaint(UIUtil.getGradientPaint( 0, y, g.getColor(), 0, y + h, ColorUtil.toAlpha(g.getColor(), 0))); - g.fillRect(bounds.x, y, bounds.width, h + h); - } - else { - g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); + g.fillRect(bounds.x, y, bounds.width, h); } mySeparator.setSize(bounds.width - 1, bounds.height); mySeparator.paint(g.create(bounds.x + 1, bounds.y, bounds.width - 1, bounds.height)); @@ -373,7 +353,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } private void fireSelected(Configurable configurable, ActionCallback callback) { - myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); + myFilter.myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); } @Override @@ -435,20 +415,21 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myConfigurable = configurable; String name = configurable.getDisplayName(); myDisplayName = name != null ? name.replace("\n", " ") : "{ " + configurable.getClass().getSimpleName() + " }"; - - myConfigurableToNodeMap.put(configurable, this); } private MyNode(CachingSimpleNode parent, ConfigurableGroup group) { super(parent); myComposite = group; - myConfigurable = null; + myConfigurable = group instanceof Configurable ? (Configurable)group : null; String name = group.getDisplayName(); myDisplayName = name != null ? name.replace("\n", " ") : "{ " + group.getClass().getSimpleName() + " }"; } @Override protected SimpleNode[] buildChildren() { + if (myConfigurable != null) { + myConfigurableToNodeMap.put(myConfigurable, this); + } if (myComposite == null) { return NO_CHILDREN; } @@ -460,7 +441,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd for (int i = 0; i < configurables.length; i++) { result[i] = new MyNode(this, configurables[i]); if (myConfigurable != null) { - myContext.registerKid(myConfigurable, configurables[i]); + myFilter.myContext.registerKid(myConfigurable, configurables[i]); } } return result; @@ -470,11 +451,6 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd public boolean isAlwaysLeaf() { return myComposite == null; } - - @Override - public int getWeight() { - return WeightBasedComparator.UNDEFINED_WEIGHT; - } } private final class MyRenderer extends GroupedElementsRenderer.Tree { @@ -490,8 +466,9 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd protected void layout() { myNodeIcon = new JLabel(" ", SwingConstants.RIGHT); myProjectIcon = new JLabel(" ", SwingConstants.LEFT); - myProjectIcon.setOpaque(true); - myRendererComponent.add(BorderLayout.NORTH, mySeparatorComponent); + myNodeIcon.setOpaque(false); + myTextLabel.setOpaque(false); + myProjectIcon.setOpaque(false); myRendererComponent.add(BorderLayout.CENTER, myComponent); myRendererComponent.add(BorderLayout.WEST, myNodeIcon); myRendererComponent.add(BorderLayout.EAST, myProjectIcon); @@ -504,22 +481,17 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd boolean leaf, int row, boolean focused) { - myTextLabel.setOpaque(selected); myTextLabel.setFont(UIUtil.getLabelFont()); - - String text; - boolean hasSeparatorAbove = false; - int preferredForcedWidth = -1; + myRendererComponent.setBackground(selected ? UIUtil.getTreeSelectionBackground() : myTree.getBackground()); MyNode node = extractNode(value); if (node == null) { - text = value.toString(); + myTextLabel.setText(value.toString()); } else { - text = node.myDisplayName; + myTextLabel.setText(node.myDisplayName); // show groups in bold if (myRoot == node.getParent()) { - hasSeparatorAbove = node != myRoot.getChildAt(0); myTextLabel.setFont(myTextLabel.getFont().deriveFont(Font.BOLD)); } TreePath path = tree.getPathForRow(row); @@ -543,18 +515,18 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd forcedWidth = visibleRect.width > 0 ? visibleRect.width - indent : forcedWidth; } - preferredForcedWidth = forcedWidth - 4; + myRendererComponent.setPrefereedWidth(forcedWidth - 4); } - Component result = configureComponent(text, null, null, null, selected, hasSeparatorAbove, null, preferredForcedWidth); // update font color for modified configurables + myTextLabel.setForeground(selected ? UIUtil.getTreeSelectionForeground() : NORMAL_NODE); if (!selected && node != null) { Configurable configurable = node.myConfigurable; if (configurable != null) { - if (myContext.getErrors().containsKey(configurable)) { - myTextLabel.setForeground(JBColor.RED); + if (myFilter.myContext.getErrors().containsKey(configurable)) { + myTextLabel.setForeground(WRONG_CONTENT); } - else if (myContext.getModified().contains(configurable)) { - myTextLabel.setForeground(JBColor.BLUE); + else if (myFilter.myContext.getModified().contains(configurable)) { + myTextLabel.setForeground(MODIFIED_CONTENT); } } } @@ -586,7 +558,6 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myProjectIcon.setToolTipText(OptionsBundle.message(project.isDefault() ? "configurable.default.project.tooltip" : "configurable.current.project.tooltip")); - myProjectIcon.setBackground(myTextLabel.getBackground()); myProjectIcon.setVisible(true); } else { @@ -601,9 +572,12 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd else { myNodeIcon.setIcon(null); } - return result; + return myRendererComponent; } + int getSeparatorHeight() { + return mySeparatorComponent.getParent() == null ? 0 : mySeparatorComponent.getPreferredSize().height; + } public boolean isUnderHandle(Point point) { Point handlePoint = SwingUtilities.convertPoint(myRendererComponent, point, myNodeIcon); @@ -720,7 +694,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd super.processMouseEvent(e); } - private final class MyTreeUi extends BasicTreeUI { + private final class MyTreeUi extends WideSelectionTreeUI { @Override public void toggleExpandState(TreePath path) { @@ -773,7 +747,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd boolean myWasHoldingFilter; public MyBuilder(SimpleTreeStructure structure) { - super(myTree, myContext.getFilter(), structure, new WeightBasedComparator(false)); + super(myTree, myFilter, structure, null); myTree.addTreeExpansionListener(new TreeExpansionListener() { public void treeExpanded(TreeExpansionEvent event) { invalidateExpansions(); @@ -798,7 +772,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd @Override public boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) { - return myContext.isHoldingFilter(); + return myFilter.myContext.isHoldingFilter(); } @Override @@ -809,22 +783,22 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd @Override protected ActionCallback refilterNow(Object preferredSelection, boolean adjustSelection) { final List<Object> toRestore = new ArrayList<Object>(); - if (myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { + if (myFilter.myContext.isHoldingFilter() && !myWasHoldingFilter && myToExpandOnResetFilter == null) { myToExpandOnResetFilter = myBuilder.getUi().getExpandedElements(); } - else if (!myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { + else if (!myFilter.myContext.isHoldingFilter() && myWasHoldingFilter && myToExpandOnResetFilter != null) { toRestore.addAll(myToExpandOnResetFilter); myToExpandOnResetFilter = null; } - myWasHoldingFilter = myContext.isHoldingFilter(); + myWasHoldingFilter = myFilter.myContext.isHoldingFilter(); ActionCallback result = super.refilterNow(preferredSelection, adjustSelection); myRefilteringNow = true; return result.doWhenDone(new Runnable() { public void run() { myRefilteringNow = false; - if (!myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { + if (!myFilter.myContext.isHoldingFilter() && getSelectedElements().isEmpty()) { restoreExpandedState(toRestore); } } |