2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com
.intellij
.openapi
.ui
;
19 import com
.intellij
.CommonBundle
;
20 import com
.intellij
.ide
.ui
.SplitterProportionsDataImpl
;
21 import com
.intellij
.openapi
.actionSystem
.*;
22 import com
.intellij
.openapi
.components
.PersistentStateComponent
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.options
.Configurable
;
25 import com
.intellij
.openapi
.options
.ConfigurationException
;
26 import com
.intellij
.openapi
.options
.MasterDetails
;
27 import com
.intellij
.openapi
.project
.DumbAware
;
28 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
29 import com
.intellij
.openapi
.ui
.popup
.ListPopup
;
30 import com
.intellij
.openapi
.ui
.popup
.ListPopupStep
;
31 import com
.intellij
.openapi
.util
.ActionCallback
;
32 import com
.intellij
.openapi
.util
.Comparing
;
33 import com
.intellij
.openapi
.util
.Condition
;
34 import com
.intellij
.openapi
.util
.IconLoader
;
35 import com
.intellij
.openapi
.util
.text
.StringUtil
;
36 import com
.intellij
.ui
.*;
37 import com
.intellij
.ui
.navigation
.History
;
38 import com
.intellij
.ui
.navigation
.Place
;
39 import com
.intellij
.ui
.treeStructure
.Tree
;
40 import com
.intellij
.util
.Icons
;
41 import com
.intellij
.util
.ReflectionUtil
;
42 import com
.intellij
.util
.containers
.HashSet
;
43 import com
.intellij
.util
.ui
.UIUtil
;
44 import com
.intellij
.util
.ui
.tree
.TreeUtil
;
45 import com
.intellij
.util
.xmlb
.SkipDefaultValuesSerializationFilters
;
46 import com
.intellij
.util
.xmlb
.XmlSerializer
;
47 import com
.intellij
.util
.xmlb
.annotations
.Tag
;
48 import org
.jdom
.Element
;
49 import org
.jetbrains
.annotations
.NonNls
;
50 import org
.jetbrains
.annotations
.NotNull
;
51 import org
.jetbrains
.annotations
.Nullable
;
54 import javax
.swing
.tree
.DefaultMutableTreeNode
;
55 import javax
.swing
.tree
.DefaultTreeModel
;
56 import javax
.swing
.tree
.TreeNode
;
57 import javax
.swing
.tree
.TreePath
;
60 import java
.util
.List
;
67 public abstract class MasterDetailsComponent
implements Configurable
, PersistentStateComponent
<MasterDetailsComponent
.UIState
>, DetailsComponent
.Facade
,
69 protected static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.ui.MasterDetailsComponent");
70 protected static final Icon COPY_ICON
= IconLoader
.getIcon("/actions/copy.png");
71 protected NamedConfigurable myCurrentConfigurable
;
72 private final Splitter mySplitter
= new Splitter(false, .2f
);
75 @NonNls public static final String TREE_OBJECT
= "treeObject";
76 @NonNls public static final String TREE_NAME
= "treeName";
78 protected History myHistory
= new History(new Place
.Navigator() {
79 public void setHistory(final History history
) {
83 public ActionCallback
navigateTo(@Nullable final Place place
, final boolean requestFocus
) {
87 public void queryPlace(@NotNull final Place place
) {
90 private JScrollPane myMaster
;
92 public void setHistory(final History history
) {
96 public static class UIState
{
97 @Tag("splitter-proportions")
98 public SplitterProportionsDataImpl proportions
= new SplitterProportionsDataImpl();
100 public String lastEditedConfigurable
;
102 public Element mySettingsElement
;
105 protected UIState myState
= new UIState();
107 protected Runnable TREE_UPDATER
;
110 TREE_UPDATER
= new Runnable() {
112 MyNode node
= (MyNode
)myTree
.getSelectionPath().getLastPathComponent();
114 myState
.lastEditedConfigurable
= getNodePathString(node
); //survive after rename;
115 myDetails
.setText(node
.getConfigurable().getBannerSlogan());
116 ((DefaultTreeModel
)myTree
.getModel()).reload(node
);
117 fireItemsChangedExternally();
123 protected MyNode myRoot
= new MyRootNode();
124 protected Tree myTree
= new Tree();
126 private final DetailsComponent myDetails
= new DetailsComponent();
127 protected JPanel myWholePanel
;
128 public JPanel myNorthPanel
= new JPanel(new BorderLayout());
130 private final ArrayList
<ItemsChangeListener
> myListners
= new ArrayList
<ItemsChangeListener
>();
132 private final Set
<NamedConfigurable
> myInitializedConfigurables
= new HashSet
<NamedConfigurable
>();
134 private boolean myHasDeletedItems
;
135 protected AutoScrollToSourceHandler myAutoScrollHandler
;
137 private boolean myToReinitWholePanel
= true;
139 protected MasterDetailsComponent() {
142 reinintWholePanelIfNeeded();
145 private void reinintWholePanelIfNeeded() {
146 if (!myToReinitWholePanel
) return;
148 myWholePanel
= new JPanel(new BorderLayout()) {
149 public void addNotify() {
151 MasterDetailsComponent
.this.addNotify();
154 mySplitter
.setHonorComponentsMinimumSize(true);
155 myWholePanel
.add(mySplitter
, BorderLayout
.CENTER
);
157 JPanel left
= new JPanel(new BorderLayout()) {
158 public Dimension
getMinimumSize() {
159 final Dimension original
= super.getMinimumSize();
160 return new Dimension(Math
.max(original
.width
, 100), original
.height
);
164 left
.add(myNorthPanel
, BorderLayout
.NORTH
);
165 myMaster
= new JScrollPane(myTree
);
166 left
.add(myMaster
, BorderLayout
.CENTER
);
167 mySplitter
.setFirstComponent(left
);
169 final JPanel right
= new JPanel(new BorderLayout());
170 right
.add(myDetails
.getComponent(), BorderLayout
.CENTER
);
172 mySplitter
.setSecondComponent(right
);
174 GuiUtils
.replaceJSplitPaneWithIDEASplitter(myWholePanel
);
176 myToReinitWholePanel
= false;
179 private void installAutoScroll() {
180 myAutoScrollHandler
= new AutoScrollToSourceHandler() {
181 protected boolean isAutoScrollMode() {
182 return isAutoScrollEnabled();
185 protected void setAutoScrollMode(boolean state
) {
189 protected void scrollToSource(Component tree
) {
190 updateSelectionFromTree();
193 protected boolean needToCheckFocus() {
197 myAutoScrollHandler
.install(myTree
);
200 protected void addNotify() {
201 updateSelectionFromTree();
204 private void updateSelectionFromTree() {
205 TreePath
[] treePaths
= myTree
.getSelectionPaths();
206 if (treePaths
!= null) {
207 List
<NamedConfigurable
> selectedConfigurables
= new ArrayList
<NamedConfigurable
>();
208 for (TreePath path
: treePaths
) {
209 Object lastPathComponent
= path
.getLastPathComponent();
210 if (lastPathComponent
instanceof MyNode
) {
211 selectedConfigurables
.add(((MyNode
)lastPathComponent
).getConfigurable());
214 if (selectedConfigurables
.size() > 1 && updateMultiSelection(selectedConfigurables
)) {
219 final TreePath path
= myTree
.getSelectionPath();
221 final Object lastPathComp
= path
.getLastPathComponent();
222 if (!(lastPathComp
instanceof MyNode
)) return;
223 final MyNode node
= (MyNode
)lastPathComp
;
224 setSelectedNode(node
);
226 setSelectedNode(null);
230 protected boolean updateMultiSelection(final List
<NamedConfigurable
> selectedConfigurables
) {
234 public DetailsComponent
getDetailsComponent() {
238 public Splitter
getSplitter() {
242 protected boolean isAutoScrollEnabled() {
243 return myHistory
!= null ?
!myHistory
.isNavigatingNow() : true;
246 private void initToolbar() {
247 final ArrayList
<AnAction
> actions
= createActions(false);
248 if (actions
!= null) {
249 final DefaultActionGroup group
= new DefaultActionGroup();
250 for (AnAction action
: actions
) {
251 if (action
instanceof ActionGroupWithPreselection
) {
252 group
.add(new MyActionGroupWrapper((ActionGroupWithPreselection
)action
));
258 final JComponent component
= ActionManager
.getInstance().createActionToolbar(ActionPlaces
.UNKNOWN
, group
, true).getComponent();
259 myNorthPanel
.add(component
, BorderLayout
.NORTH
);
263 public void addItemsChangeListener(ItemsChangeListener l
) {
267 protected Dimension
getPanelPrefferedSize() {
268 return new Dimension(800, 600);
271 public JComponent
createComponent() {
272 reinintWholePanelIfNeeded();
274 updateSelectionFromTree();
276 SwingUtilities
.updateComponentTreeUI(myWholePanel
);
277 final JPanel panel
= new JPanel(new BorderLayout()) {
278 public Dimension
getPreferredSize() {
279 return getPanelPrefferedSize();
282 panel
.add(myWholePanel
, BorderLayout
.CENTER
);
286 public boolean isModified() {
287 if (myHasDeletedItems
) return true;
288 final boolean[] modified
= new boolean[1];
289 TreeUtil
.traverseDepth(myRoot
, new TreeUtil
.Traverse() {
290 public boolean accept(Object node
) {
291 if (node
instanceof MyNode
) {
292 final NamedConfigurable configurable
= ((MyNode
)node
).getConfigurable();
293 if (isInitialized(configurable
) && configurable
.isModified()) {
304 protected boolean isInitialized(final NamedConfigurable configurable
) {
305 return myInitializedConfigurables
.contains(configurable
);
308 public void apply() throws ConfigurationException
{
309 processRemovedItems();
310 final ConfigurationException
[] ex
= new ConfigurationException
[1];
311 TreeUtil
.traverse(myRoot
, new TreeUtil
.Traverse() {
312 public boolean accept(Object node
) {
313 if (node
instanceof MyNode
) {
315 final NamedConfigurable configurable
= ((MyNode
)node
).getConfigurable();
316 if (isInitialized(configurable
) && configurable
.isModified()) {
317 configurable
.apply();
320 catch (ConfigurationException e
) {
331 myHasDeletedItems
= false;
334 protected abstract void processRemovedItems();
336 protected abstract boolean wasObjectStored(Object editableObject
);
338 public void reset() {
339 myHasDeletedItems
= false;
340 ((DefaultTreeModel
)myTree
.getModel()).reload();
341 //myTree.requestFocus();
342 myState
.proportions
.restoreSplitterProportions(myWholePanel
);
344 final Enumeration enumeration
= myRoot
.breadthFirstEnumeration();
345 boolean selected
= false;
346 while (enumeration
.hasMoreElements()) {
347 final MyNode node
= (MyNode
)enumeration
.nextElement();
348 if (node
instanceof MyRootNode
) continue;
349 final String path
= getNodePathString(node
);
350 if (!selected
&& Comparing
.strEqual(path
, myState
.lastEditedConfigurable
)) {
351 TreeUtil
.selectInTree(node
, false, myTree
);
356 TreeUtil
.selectFirstNode(myTree
);
358 updateSelectionFromTree();
361 private static String
getNodePathString(final MyNode node
) {
362 StringBuilder path
= new StringBuilder();
363 MyNode current
= node
;
364 while (current
!= null) {
365 final Object userObject
= current
.getUserObject();
366 if (!(userObject
instanceof NamedConfigurable
)) break;
367 final String displayName
= current
.getDisplayName();
368 if (StringUtil
.isEmptyOrSpaces(displayName
)) break;
369 if (path
.length() > 0) {
372 path
.append(displayName
);
374 final TreeNode parent
= current
.getParent();
375 if (!(parent
instanceof MyNode
)) break;
376 current
= (MyNode
)parent
;
378 return path
.toString();
382 protected PersistentStateComponent
<?
> getAdditionalSettings() {
386 public UIState
getState() {
387 myState
.mySettingsElement
= null;
388 PersistentStateComponent
<?
> additionalSettings
= getAdditionalSettings();
389 if (additionalSettings
!= null) {
390 final Object state
= additionalSettings
.getState();
392 myState
.mySettingsElement
= XmlSerializer
.serialize(state
, new SkipDefaultValuesSerializationFilters());
398 public void loadState(final UIState object
) {
399 myState
.lastEditedConfigurable
= object
.lastEditedConfigurable
;
400 myState
.proportions
= object
.proportions
;
401 final PersistentStateComponent
<?
> additionalSettings
= getAdditionalSettings();
402 if (additionalSettings
!= null) {
403 final Element settingsElement
= object
.mySettingsElement
;
404 if (settingsElement
!= null) {
405 final Class
<?
> stateType
= ReflectionUtil
.getRawType(ReflectionUtil
.resolveVariableInHierarchy(PersistentStateComponent
.class.getTypeParameters()[0], additionalSettings
.getClass()));
406 //noinspection unchecked
407 ((PersistentStateComponent
)additionalSettings
).loadState(XmlSerializer
.deserialize(settingsElement
, stateType
));
412 public void disposeUIResources() {
413 myState
.proportions
.saveSplitterProportions(myWholePanel
);
414 myAutoScrollHandler
.cancelAllRequests();
415 myDetails
.disposeUIResources();
416 myInitializedConfigurables
.clear();
417 TreeUtil
.traverseDepth(myRoot
, new TreeUtil
.Traverse() {
418 public boolean accept(Object node
) {
419 if (node
instanceof MyNode
) {
420 final MyNode treeNode
= ((MyNode
)node
);
421 treeNode
.getConfigurable().disposeUIResources();
422 if (!(treeNode
instanceof MyRootNode
)) {
423 treeNode
.setUserObject(null);
429 myRoot
.removeAllChildren();
430 myCurrentConfigurable
= null;
434 protected ArrayList
<AnAction
> createActions(final boolean fromPopup
) {
439 protected void initTree() {
440 ((DefaultTreeModel
)myTree
.getModel()).setRoot(myRoot
);
441 myTree
.setRootVisible(false);
442 myTree
.setShowsRootHandles(true);
443 UIUtil
.setLineStyleAngled(myTree
);
444 TreeUtil
.installActions(myTree
);
445 myTree
.setCellRenderer(new ColoredTreeCellRenderer() {
446 public void customizeCellRenderer(JTree tree
,
453 if (value
instanceof MyNode
) {
454 final MyNode node
= ((MyNode
)value
);
455 setIcon(node
.getConfigurable().getIcon(expanded
));
456 final Font font
= UIUtil
.getTreeFont();
457 if (node
.isDisplayInBold()) {
458 setFont(font
.deriveFont(Font
.BOLD
));
461 setFont(font
.deriveFont(Font
.PLAIN
));
463 append(node
.getDisplayName(),
464 node
.isDisplayInBold() ? SimpleTextAttributes
.REGULAR_BOLD_ATTRIBUTES
: SimpleTextAttributes
.REGULAR_ATTRIBUTES
);
469 ArrayList
<AnAction
> actions
= createActions(true);
470 if (actions
!= null) {
471 final DefaultActionGroup group
= new DefaultActionGroup();
472 for (AnAction action
: actions
) {
475 actions
= getAdditionalActions();
476 if (actions
!= null) {
477 group
.addSeparator();
478 for (AnAction action
: actions
) {
483 .installPopupHandler(myTree
, group
, ActionPlaces
.UNKNOWN
, ActionManager
.getInstance()); //popup should follow the selection
488 protected ArrayList
<AnAction
> getAdditionalActions() {
492 public void fireItemsChangeListener(final Object editableObject
) {
493 for (ItemsChangeListener listener
: myListners
) {
494 listener
.itemChanged(editableObject
);
498 private void fireItemsChangedExternally() {
499 for (ItemsChangeListener listener
: myListners
) {
500 listener
.itemsExternallyChanged();
504 private void createUIComponents() {
505 myTree
= new Tree() {
506 public Dimension
getPreferredScrollableViewportSize() {
507 Dimension size
= super.getPreferredScrollableViewportSize();
508 size
= new Dimension(size
.width
+ 20, size
.height
);
512 @SuppressWarnings({"NonStaticInitializer"})
513 public JToolTip
createToolTip() {
514 final JToolTip toolTip
= new JToolTip() {
516 setUI(new MultiLineTooltipUI());
519 toolTip
.setComponent(this);
525 protected void addNode(MyNode nodeToAdd
, MyNode parent
) {
526 parent
.add(nodeToAdd
);
527 TreeUtil
.sort(parent
, new Comparator() {
528 public int compare(final Object o1
, final Object o2
) {
529 MyNode node1
= (MyNode
)o1
;
530 MyNode node2
= (MyNode
)o2
;
531 return node1
.getDisplayName().compareToIgnoreCase(node2
.getDisplayName());
534 ((DefaultTreeModel
)myTree
.getModel()).reload(parent
);
537 public ActionCallback
selectNodeInTree(final DefaultMutableTreeNode nodeToSelect
) {
538 return selectNodeInTree(nodeToSelect
, true, false);
541 public ActionCallback
selectNodeInTree(final DefaultMutableTreeNode nodeToSelect
, boolean requestFocus
) {
542 return selectNodeInTree(nodeToSelect
, true, requestFocus
);
545 public ActionCallback
selectNodeInTree(final DefaultMutableTreeNode nodeToSelect
, boolean center
, final boolean requestFocus
) {
547 myTree
.requestFocus();
549 if (nodeToSelect
!= null) {
550 return TreeUtil
.selectInTree(nodeToSelect
, requestFocus
, myTree
, center
);
553 return TreeUtil
.selectFirstNode(myTree
);
558 public Object
getSelectedObject() {
559 final TreePath selectionPath
= myTree
.getSelectionPath();
560 if (selectionPath
!= null && selectionPath
.getLastPathComponent() instanceof MyNode
) {
561 MyNode node
= (MyNode
)selectionPath
.getLastPathComponent();
562 final NamedConfigurable configurable
= node
.getConfigurable();
563 LOG
.assertTrue(configurable
!= null, "already disposed");
564 return configurable
.getEditableObject();
570 public NamedConfigurable
getSelectedConfugurable() {
571 final TreePath selectionPath
= myTree
.getSelectionPath();
572 if (selectionPath
!= null) {
573 MyNode node
= (MyNode
)selectionPath
.getLastPathComponent();
574 final NamedConfigurable configurable
= node
.getConfigurable();
575 LOG
.assertTrue(configurable
!= null, "already disposed");
581 public void selectNodeInTree(String displayName
) {
582 final MyNode nodeByName
= findNodeByName(myRoot
, displayName
);
583 selectNodeInTree(nodeByName
, true);
586 public void selectNodeInTree(final Object object
) {
587 selectNodeInTree(findNodeByObject(myRoot
, object
), true);
591 protected static MyNode
findNodeByName(final TreeNode root
, final String profileName
) {
592 if (profileName
== null) return null; //do not suggest root node
593 return findNodeByCondition(root
, new Condition
<NamedConfigurable
>() {
594 public boolean value(final NamedConfigurable configurable
) {
595 return Comparing
.strEqual(profileName
, configurable
.getDisplayName());
601 public static MyNode
findNodeByObject(final TreeNode root
, final Object editableObject
) {
602 if (editableObject
== null) return null; //do not suggest root node
603 return findNodeByCondition(root
, new Condition
<NamedConfigurable
>() {
604 public boolean value(final NamedConfigurable configurable
) {
605 return Comparing
.equal(editableObject
, configurable
.getEditableObject());
610 protected static MyNode
findNodeByCondition(final TreeNode root
, final Condition
<NamedConfigurable
> condition
) {
611 final MyNode
[] nodeToSelect
= new MyNode
[1];
612 TreeUtil
.traverseDepth(root
, new TreeUtil
.Traverse() {
613 public boolean accept(Object node
) {
614 if (condition
.value(((MyNode
)node
).getConfigurable())) {
615 nodeToSelect
[0] = (MyNode
)node
;
621 return nodeToSelect
[0];
624 protected void setSelectedNode(@Nullable MyNode node
) {
626 myState
.lastEditedConfigurable
= getNodePathString(node
);
628 updateSelection(node
!= null ? node
.getConfigurable() : null);
631 protected void updateSelection(@Nullable NamedConfigurable configurable
) {
632 myDetails
.setText(configurable
!= null ? configurable
.getBannerSlogan() : null);
634 myCurrentConfigurable
= configurable
;
636 if (configurable
!= null) {
637 final JComponent comp
= configurable
.createComponent();
640 LOG
.error("createComponent() returned null. configurable=" + configurable
);
642 myDetails
.setContent(comp
);
643 if (!isInitialized(configurable
)) {
644 configurable
.reset();
645 initializeConfigurable(configurable
);
647 myHistory
.pushPlaceForElement(TREE_OBJECT
, configurable
.getEditableObject());
654 private void setEmpty() {
655 myDetails
.setContent(null);
656 myDetails
.setEmptyContentText(getEmptySelectionString());
659 public String
getHelpTopic() {
660 if (myCurrentConfigurable
!= null) {
661 return myCurrentConfigurable
.getHelpTopic();
666 protected @Nullable String
getEmptySelectionString() {
670 protected void initializeConfigurable(final NamedConfigurable configurable
) {
671 myInitializedConfigurables
.add(configurable
);
674 protected void checkApply(Set
<MyNode
> rootNodes
, String prefix
, String title
) throws ConfigurationException
{
675 for (MyNode rootNode
: rootNodes
) {
676 final Set
<String
> names
= new HashSet
<String
>();
677 for (int i
= 0; i
< rootNode
.getChildCount(); i
++) {
678 final MyNode node
= (MyNode
)rootNode
.getChildAt(i
);
679 final NamedConfigurable scopeConfigurable
= node
.getConfigurable();
680 final String name
= scopeConfigurable
.getDisplayName();
681 if (name
.trim().length() == 0) {
682 selectNodeInTree(node
);
683 throw new ConfigurationException("Name should contain non-space characters");
685 if (names
.contains(name
)) {
686 final NamedConfigurable selectedConfugurable
= getSelectedConfugurable();
687 if (selectedConfugurable
== null || !Comparing
.strEqual(selectedConfugurable
.getDisplayName(), name
)) {
688 selectNodeInTree(node
);
690 throw new ConfigurationException(CommonBundle
.message("smth.already.exist.error.message", prefix
, name
), title
);
697 public Tree
getTree() {
701 protected void removePaths(final TreePath
... paths
) {
702 MyNode parentNode
= null;
704 for (TreePath path
: paths
) {
705 final MyNode node
= (MyNode
)path
.getLastPathComponent();
706 final NamedConfigurable namedConfigurable
= node
.getConfigurable();
707 final Object editableObject
= namedConfigurable
.getEditableObject();
708 parentNode
= (MyNode
)node
.getParent();
709 idx
= parentNode
.getIndex(node
);
710 parentNode
.remove(node
);
711 myHasDeletedItems
|= wasObjectStored(editableObject
);
712 fireItemsChangeListener(editableObject
);
713 onItemDeleted(editableObject
);
714 namedConfigurable
.disposeUIResources();
716 ((DefaultTreeModel
)myTree
.getModel()).reload();
717 if (parentNode
!= null && idx
!= -1) {
719 .selectInTree((DefaultMutableTreeNode
)(idx
< parentNode
.getChildCount() ? parentNode
.getChildAt(idx
) : parentNode
), true, myTree
);
722 TreeUtil
.selectFirstNode(myTree
);
726 protected void onItemDeleted(Object item
) {
729 protected class MyDeleteAction
extends AnAction
implements DumbAware
{
730 private final Condition
<Object
> myCondition
;
732 public MyDeleteAction(Condition
<Object
> availableCondition
) {
733 super(CommonBundle
.message("button.delete"), CommonBundle
.message("button.delete"), Icons
.DELETE_ICON
);
734 registerCustomShortcutSet(CommonShortcuts
.DELETE
, myTree
);
735 myCondition
= availableCondition
;
738 public void update(AnActionEvent e
) {
739 final Presentation presentation
= e
.getPresentation();
740 presentation
.setEnabled(false);
741 final TreePath
[] selectionPath
= myTree
.getSelectionPaths();
742 if (selectionPath
!= null) {
743 for (TreePath path
: selectionPath
) {
744 if (!myCondition
.value(path
.getLastPathComponent())) return;
746 presentation
.setEnabled(true);
750 public void actionPerformed(AnActionEvent e
) {
751 removePaths(myTree
.getSelectionPaths());
755 public static class MyNode
extends DefaultMutableTreeNode
{
756 private boolean myDisplayInBold
;
758 public MyNode(NamedConfigurable userObject
) {
762 public MyNode(NamedConfigurable userObject
, boolean displayInBold
) {
764 myDisplayInBold
= displayInBold
;
768 public String
getDisplayName() {
769 final NamedConfigurable configurable
= ((NamedConfigurable
)getUserObject());
770 LOG
.assertTrue(configurable
!= null, "Tree was already disposed");
771 return configurable
.getDisplayName();
774 public NamedConfigurable
getConfigurable() {
775 return (NamedConfigurable
)getUserObject();
778 public boolean isDisplayInBold() {
779 return myDisplayInBold
;
783 @SuppressWarnings({"ConstantConditions"})
784 protected static class MyRootNode
extends MyNode
{
785 public MyRootNode() {
786 super(new NamedConfigurable(false, null) {
787 public void setDisplayName(String name
) {
790 public Object
getEditableObject() {
794 public String
getBannerSlogan() {
798 public String
getDisplayName() {
802 public Icon
getIcon() {
803 return IconLoader
.getIcon("/general/applicationSettings.png");
808 public String
getHelpTopic() {
812 public JComponent
createOptionsPanel() {
816 public boolean isModified() {
820 public void apply() throws ConfigurationException
{
823 public void reset() {
826 public void disposeUIResources() {
833 protected interface ItemsChangeListener
{
834 void itemChanged(@Nullable Object deletedItem
);
836 void itemsExternallyChanged();
839 public static interface ActionGroupWithPreselection
{
840 ActionGroup
getActionGroup();
842 int getDefaultIndex();
845 protected class MyActionGroupWrapper
extends AnAction
implements DumbAware
{
846 private ActionGroup myActionGroup
;
847 private ActionGroupWithPreselection myPreselection
;
849 public MyActionGroupWrapper(final ActionGroupWithPreselection actionGroup
) {
850 this(actionGroup
.getActionGroup());
851 myPreselection
= actionGroup
;
854 public MyActionGroupWrapper(final ActionGroup actionGroup
) {
855 super(actionGroup
.getTemplatePresentation().getText(), actionGroup
.getTemplatePresentation().getDescription(),
856 actionGroup
.getTemplatePresentation().getIcon());
857 myActionGroup
= actionGroup
;
858 registerCustomShortcutSet(actionGroup
.getShortcutSet(), myTree
);
861 public void actionPerformed(AnActionEvent e
) {
862 final JBPopupFactory popupFactory
= JBPopupFactory
.getInstance();
863 final ListPopupStep step
= popupFactory
.createActionsStep(myActionGroup
, e
.getDataContext(), false, false,
864 myActionGroup
.getTemplatePresentation().getText(), myTree
, true,
865 myPreselection
!= null ? myPreselection
.getDefaultIndex() : 0, true);
866 final ListPopup listPopup
= popupFactory
.createListPopup(step
);
867 listPopup
.setHandleAutoSelectionBeforeShow(true);
868 listPopup
.showUnderneathOf(myNorthPanel
);
872 public JComponent
getToolbar() {
873 myToReinitWholePanel
= true;
877 public JComponent
getMaster() {
878 myToReinitWholePanel
= true;
882 public DetailsComponent
getDetails() {
883 myToReinitWholePanel
= true;
887 public void initUi() {