/* * Copyright 2013-2016 consulo.io * * 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 consulo.roots.ui.configuration; import com.intellij.openapi.Disposable; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ModuleExtensionWithSdkOrderEntry; import com.intellij.openapi.roots.ui.configuration.*; import consulo.roots.ui.configuration.extension.ExtensionCheckedTreeNode; import consulo.roots.ui.configuration.extension.ExtensionTreeCellRenderer; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.Comparing; import com.intellij.ui.CheckboxTreeNoPolicy; import com.intellij.ui.CheckedTreeNode; import com.intellij.ui.OnePixelSplitter; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.tree.TreeUtil; import consulo.module.extension.MutableModuleExtension; import consulo.psi.PsiPackageManager; import consulo.roots.ModifiableModuleRootLayer; import consulo.module.extension.ModuleExtension; import consulo.module.extension.ModuleExtensionWithSdk; import consulo.psi.PsiPackageSupportProvider; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import consulo.annotations.RequiredDispatchThread; import javax.swing.*; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreeSelectionModel; import java.awt.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author VISTALL * @since 10:33/19.05.13 */ public class ExtensionEditor extends ModuleElementsEditor { private final ModuleConfigurationState myState; private final OutputEditor myOutputEditor; private final ClasspathEditor myClasspathEditor; private final ContentEntriesEditor myContentEntriesEditor; private CheckboxTreeNoPolicy myTree; private Splitter mySplitter; private ModuleExtension<?> myConfigurablePanelExtension; public ExtensionEditor(ModuleConfigurationState state, OutputEditor outputEditor, ClasspathEditor classpathEditor, ContentEntriesEditor contentEntriesEditor) { super(state); myState = state; myOutputEditor = outputEditor; myClasspathEditor = classpathEditor; myContentEntriesEditor = contentEntriesEditor; } @Override public void moduleStateChanged() { mySplitter.setSecondComponent(null); myTree.setModel(new DefaultTreeModel(new ExtensionCheckedTreeNode(null, myState, this))); TreeUtil.expandAll(myTree); } @NotNull @Override protected JComponent createComponentImpl() { JPanel rootPane = new JPanel(new BorderLayout()); mySplitter = new OnePixelSplitter(); myTree = new CheckboxTreeNoPolicy(new ExtensionTreeCellRenderer(), new ExtensionCheckedTreeNode(null, myState, this)) { @Override protected void adjustParentsAndChildren(CheckedTreeNode node, boolean checked) { if (!checked) { changeNodeState(node, false); checkOrUncheckChildren(node, false); } else { // we need collect parents, and enable it in right order // A // - B // -- C // when we enable C, ill be calls like A -> B -> C List<CheckedTreeNode> parents = new ArrayList<CheckedTreeNode>(); TreeNode parent = node.getParent(); while (parent != null) { if (parent instanceof CheckedTreeNode) { parents.add((CheckedTreeNode)parent); } parent = parent.getParent(); } Collections.reverse(parents); for (CheckedTreeNode checkedTreeNode : parents) { checkNode(checkedTreeNode, true); } changeNodeState(node, true); } repaint(); } }; myTree.setRootVisible(false); myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); myTree.addTreeSelectionListener(new TreeSelectionListener() { @Override @RequiredDispatchThread public void valueChanged(final TreeSelectionEvent e) { final List<MutableModuleExtension> selected = TreeUtil.collectSelectedObjectsOfType(myTree, MutableModuleExtension.class); updateSecondComponent(ContainerUtil.<MutableModuleExtension>getFirstItem(selected)); } }); TreeUtil.expandAll(myTree); mySplitter.setFirstComponent(myTree); rootPane.add(new JBScrollPane(mySplitter), BorderLayout.CENTER); return rootPane; } @Nullable @RequiredDispatchThread private JComponent createConfigurationPanel(final @NotNull MutableModuleExtension extension) { myConfigurablePanelExtension = extension; final Runnable updateOnCheck = new Runnable() { @Override @RequiredDispatchThread public void run() { extensionChanged(extension); } }; JComponent configurablePanel = null; final consulo.ui.Component component = extension.createConfigurationComponent(updateOnCheck); if (component != null) { // we can call UIAccess.get() due we inside ui thread // we need this ugly cast for now configurablePanel = (JComponent)component; } else { configurablePanel = extension.createConfigurablePanel(updateOnCheck); } if (configurablePanel instanceof Disposable) { registerDisposable((Disposable)configurablePanel); } return configurablePanel; } @RequiredDispatchThread private void updateSecondComponent(@Nullable MutableModuleExtension extension) { if (extension == null || !extension.isEnabled()) { mySplitter.setSecondComponent(null); } else { mySplitter.setSecondComponent(createConfigurationPanel(extension)); } } @RequiredDispatchThread public void extensionChanged(MutableModuleExtension extension) { final JComponent secondComponent = myConfigurablePanelExtension != extension ? null : mySplitter.getSecondComponent(); if (secondComponent == null && extension.isEnabled() || secondComponent != null && !extension.isEnabled()) { updateSecondComponent(!extension.isEnabled() ? null : extension); } if (extension instanceof ModuleExtensionWithSdk) { // we using module layer, dont use modifiable model - it ill proxy, and methods 'addModuleExtensionSdkEntry' && 'removeOrderEntry' // ill call this method again ModifiableModuleRootLayer moduleRootLayer = extension.getModuleRootLayer(); final ModuleExtensionWithSdkOrderEntry sdkOrderEntry = moduleRootLayer.findModuleExtensionSdkEntry(extension); if (!extension.isEnabled() && sdkOrderEntry != null) { moduleRootLayer.removeOrderEntry(sdkOrderEntry); } if (extension.isEnabled()) { final ModuleExtensionWithSdk sdkExtension = (ModuleExtensionWithSdk)extension; if (!sdkExtension.getInheritableSdk().isNull()) { if (sdkOrderEntry == null) { moduleRootLayer.addModuleExtensionSdkEntry(sdkExtension); } else { final ModuleExtensionWithSdk<?> moduleExtension = sdkOrderEntry.getModuleExtension(); if (moduleExtension != null && !Comparing.equal(sdkExtension.getInheritableSdk(), moduleExtension.getInheritableSdk())) { moduleRootLayer.addModuleExtensionSdkEntry(sdkExtension); } } } else if (sdkOrderEntry != null) { moduleRootLayer.removeOrderEntry(sdkOrderEntry); } } } for (PsiPackageSupportProvider supportProvider : PsiPackageSupportProvider.EP_NAME.getExtensions()) { final Module module = extension.getModule(); if (supportProvider.isSupported(extension)) { PsiPackageManager.getInstance(module.getProject()).dropCache(extension.getClass()); } } myClasspathEditor.moduleStateChanged(); myContentEntriesEditor.moduleStateChanged(); myOutputEditor.moduleStateChanged(); } @Override public void saveData() { } @Nls @Override public String getDisplayName() { return "Extensions"; } @Nullable @Override public String getHelpTopic() { return null; } }