IDEADEV-41372: Artifacts: exception on right0clicking on just deleted library
[fedora-idea.git] / platform / platform-api / src / com / intellij / openapi / ui / MasterDetailsComponent.java
blobb013d99037d3f3ff8235b69aad1ce2cda6a3f159
1 /*
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;
53 import javax.swing.*;
54 import javax.swing.tree.DefaultMutableTreeNode;
55 import javax.swing.tree.DefaultTreeModel;
56 import javax.swing.tree.TreeNode;
57 import javax.swing.tree.TreePath;
58 import java.awt.*;
59 import java.util.*;
60 import java.util.List;
63 /**
64 * User: anna
65 * Date: 29-May-2006
67 public abstract class MasterDetailsComponent implements Configurable, PersistentStateComponent<MasterDetailsComponent.UIState>, DetailsComponent.Facade,
68 MasterDetails {
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) {
80 myHistory = history;
83 public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
84 return null;
87 public void queryPlace(@NotNull final Place place) {
89 });
90 private JScrollPane myMaster;
92 public void setHistory(final History history) {
93 myHistory = history;
96 public static class UIState {
97 @Tag("splitter-proportions")
98 public SplitterProportionsDataImpl proportions = new SplitterProportionsDataImpl();
99 @Tag("last-edited")
100 public String lastEditedConfigurable;
101 @Tag("settings")
102 public Element mySettingsElement;
105 protected UIState myState = new UIState();
107 protected Runnable TREE_UPDATER;
110 TREE_UPDATER = new Runnable() {
111 public void run() {
112 MyNode node = (MyNode)myTree.getSelectionPath().getLastPathComponent();
113 if (node != null) {
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() {
140 installAutoScroll();
142 reinintWholePanelIfNeeded();
145 private void reinintWholePanelIfNeeded() {
146 if (!myToReinitWholePanel) return;
148 myWholePanel = new JPanel(new BorderLayout()) {
149 public void addNotify() {
150 super.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) {
186 //do nothing
189 protected void scrollToSource(Component tree) {
190 updateSelectionFromTree();
193 protected boolean needToCheckFocus() {
194 return false;
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)) {
215 return;
219 final TreePath path = myTree.getSelectionPath();
220 if (path != null) {
221 final Object lastPathComp = path.getLastPathComponent();
222 if (!(lastPathComp instanceof MyNode)) return;
223 final MyNode node = (MyNode)lastPathComp;
224 setSelectedNode(node);
225 } else {
226 setSelectedNode(null);
230 protected boolean updateMultiSelection(final List<NamedConfigurable> selectedConfigurables) {
231 return false;
234 public DetailsComponent getDetailsComponent() {
235 return myDetails;
238 public Splitter getSplitter() {
239 return mySplitter;
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));
254 else {
255 group.add(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) {
264 myListners.add(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);
283 return panel;
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()) {
294 modified[0] = true;
295 return false;
298 return true;
301 return modified[0];
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) {
314 try {
315 final NamedConfigurable configurable = ((MyNode)node).getConfigurable();
316 if (isInitialized(configurable) && configurable.isModified()) {
317 configurable.apply();
320 catch (ConfigurationException e) {
321 ex[0] = e;
322 return false;
325 return true;
328 if (ex[0] != null) {
329 throw ex[0];
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);
352 selected = true;
355 if (!selected) {
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) {
370 path.append('|');
372 path.append(displayName);
374 final TreeNode parent = current.getParent();
375 if (!(parent instanceof MyNode)) break;
376 current = (MyNode)parent;
378 return path.toString();
381 @Nullable
382 protected PersistentStateComponent<?> getAdditionalSettings() {
383 return null;
386 public UIState getState() {
387 myState.mySettingsElement = null;
388 PersistentStateComponent<?> additionalSettings = getAdditionalSettings();
389 if (additionalSettings != null) {
390 final Object state = additionalSettings.getState();
391 if (state != null) {
392 myState.mySettingsElement = XmlSerializer.serialize(state, new SkipDefaultValuesSerializationFilters());
395 return myState;
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);
426 return true;
429 myRoot.removeAllChildren();
430 myCurrentConfigurable = null;
433 @Nullable
434 protected ArrayList<AnAction> createActions(final boolean fromPopup) {
435 return null;
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,
447 Object value,
448 boolean selected,
449 boolean expanded,
450 boolean leaf,
451 int row,
452 boolean hasFocus) {
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));
460 else {
461 setFont(font.deriveFont(Font.PLAIN));
463 append(node.getDisplayName(),
464 node.isDisplayInBold() ? SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES);
468 initToolbar();
469 ArrayList<AnAction> actions = createActions(true);
470 if (actions != null) {
471 final DefaultActionGroup group = new DefaultActionGroup();
472 for (AnAction action : actions) {
473 group.add(action);
475 actions = getAdditionalActions();
476 if (actions != null) {
477 group.addSeparator();
478 for (AnAction action : actions) {
479 group.add(action);
482 PopupHandler
483 .installPopupHandler(myTree, group, ActionPlaces.UNKNOWN, ActionManager.getInstance()); //popup should follow the selection
487 @Nullable
488 protected ArrayList<AnAction> getAdditionalActions() {
489 return null;
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);
509 return size;
512 @SuppressWarnings({"NonStaticInitializer"})
513 public JToolTip createToolTip() {
514 final JToolTip toolTip = new JToolTip() {
516 setUI(new MultiLineTooltipUI());
519 toolTip.setComponent(this);
520 return toolTip;
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) {
546 if (requestFocus) {
547 myTree.requestFocus();
549 if (nodeToSelect != null) {
550 return TreeUtil.selectInTree(nodeToSelect, requestFocus, myTree, center);
552 else {
553 return TreeUtil.selectFirstNode(myTree);
557 @Nullable
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();
566 return null;
569 @Nullable
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");
576 return configurable;
578 return null;
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);
590 @Nullable
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());
600 @Nullable
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;
616 return false;
618 return true;
621 return nodeToSelect[0];
624 protected void setSelectedNode(@Nullable MyNode node) {
625 if (node != null) {
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();
638 if (comp == null) {
639 setEmpty();
640 LOG.error("createComponent() returned null. configurable=" + configurable);
641 } else {
642 myDetails.setContent(comp);
643 if (!isInitialized(configurable)) {
644 configurable.reset();
645 initializeConfigurable(configurable);
647 myHistory.pushPlaceForElement(TREE_OBJECT, configurable.getEditableObject());
649 } else {
650 setEmpty();
654 private void setEmpty() {
655 myDetails.setContent(null);
656 myDetails.setEmptyContentText(getEmptySelectionString());
659 public String getHelpTopic() {
660 if (myCurrentConfigurable != null) {
661 return myCurrentConfigurable.getHelpTopic();
663 return null;
666 protected @Nullable String getEmptySelectionString() {
667 return null;
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);
692 names.add(name);
697 public Tree getTree() {
698 return myTree;
701 protected void removePaths(final TreePath... paths) {
702 MyNode parentNode = null;
703 int idx = -1;
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) {
718 TreeUtil
719 .selectInTree((DefaultMutableTreeNode)(idx < parentNode.getChildCount() ? parentNode.getChildAt(idx) : parentNode), true, myTree);
721 else {
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) {
759 super(userObject);
762 public MyNode(NamedConfigurable userObject, boolean displayInBold) {
763 super(userObject);
764 myDisplayInBold = displayInBold;
767 @NotNull
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() {
791 return null;
794 public String getBannerSlogan() {
795 return null;
798 public String getDisplayName() {
799 return "";
802 public Icon getIcon() {
803 return IconLoader.getIcon("/general/applicationSettings.png");
806 @Nullable
807 @NonNls
808 public String getHelpTopic() {
809 return null;
812 public JComponent createOptionsPanel() {
813 return null;
816 public boolean isModified() {
817 return false;
820 public void apply() throws ConfigurationException {
823 public void reset() {
826 public void disposeUIResources() {
829 }, false);
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;
874 return myNorthPanel;
877 public JComponent getMaster() {
878 myToReinitWholePanel = true;
879 return myMaster;
882 public DetailsComponent getDetails() {
883 myToReinitWholePanel = true;
884 return myDetails;
887 public void initUi() {
888 createComponent();