diff options
Diffstat (limited to 'platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java')
-rw-r--r-- | platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java | 182 |
1 files changed, 78 insertions, 104 deletions
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); } } |