find usages & validation in project structure reworked
[fedora-idea.git] / platform / platform-api / src / com / intellij / openapi / ui / MasterDetailsComponent.java
blob9c5d72b5cdb6029d7eac6bdfa8cbd356dbffb1b0
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.containers.HashSet;
42 import com.intellij.util.ui.UIUtil;
43 import com.intellij.util.ui.tree.TreeUtil;
44 import org.jetbrains.annotations.NonNls;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
48 import javax.swing.*;
49 import javax.swing.tree.DefaultMutableTreeNode;
50 import javax.swing.tree.DefaultTreeModel;
51 import javax.swing.tree.TreeNode;
52 import javax.swing.tree.TreePath;
53 import java.awt.*;
54 import java.util.*;
55 import java.util.List;
58 /**
59 * User: anna
60 * Date: 29-May-2006
62 public abstract class MasterDetailsComponent implements Configurable, PersistentStateComponent<MasterDetailsComponent.UIState>, DetailsComponent.Facade,
63 MasterDetails {
64 protected static final Logger LOG = Logger.getInstance("#com.intellij.openapi.ui.MasterDetailsComponent");
65 protected static final Icon COPY_ICON = IconLoader.getIcon("/actions/copy.png");
66 protected NamedConfigurable myCurrentConfigurable;
67 private final Splitter mySplitter = new Splitter(false, .2f);
70 @NonNls public static final String TREE_OBJECT = "treeObject";
71 @NonNls public static final String TREE_NAME = "treeName";
73 protected History myHistory = new History(new Place.Navigator() {
74 public void setHistory(final History history) {
75 myHistory = history;
78 public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
79 return null;
82 public void queryPlace(@NotNull final Place place) {
84 });
85 private JScrollPane myMaster;
87 public void setHistory(final History history) {
88 myHistory = history;
91 public static class UIState {
92 public SplitterProportionsDataImpl proportions = new SplitterProportionsDataImpl();
93 public String lastEditedConfigurable;
94 public List<String> order = new ArrayList<String>();
97 protected UIState myState = new UIState();
99 protected Runnable TREE_UPDATER;
102 TREE_UPDATER = new Runnable() {
103 public void run() {
104 MyNode node = (MyNode)myTree.getSelectionPath().getLastPathComponent();
105 if (node != null) {
106 myState.lastEditedConfigurable = getNodePathString(node); //survive after rename;
107 myDetails.setText(node.getConfigurable().getBannerSlogan());
108 ((DefaultTreeModel)myTree.getModel()).reload(node);
109 fireItemsChangedExternally();
115 protected MyNode myRoot = new MyRootNode();
116 protected Tree myTree = new Tree();
118 private final DetailsComponent myDetails = new DetailsComponent();
119 protected JPanel myWholePanel;
120 public JPanel myNorthPanel = new JPanel(new BorderLayout());
122 private final ArrayList<ItemsChangeListener> myListners = new ArrayList<ItemsChangeListener>();
124 private final Set<NamedConfigurable> myInitializedConfigurables = new HashSet<NamedConfigurable>();
126 private boolean myHasDeletedItems;
127 protected AutoScrollToSourceHandler myAutoScrollHandler;
129 private boolean myToReinitWholePanel = true;
131 protected MasterDetailsComponent() {
132 installAutoScroll();
134 reinintWholePanelIfNeeded();
137 private void reinintWholePanelIfNeeded() {
138 if (!myToReinitWholePanel) return;
140 myWholePanel = new JPanel(new BorderLayout()) {
141 public void addNotify() {
142 super.addNotify();
143 MasterDetailsComponent.this.addNotify();
146 mySplitter.setHonorComponentsMinimumSize(true);
147 myWholePanel.add(mySplitter, BorderLayout.CENTER);
149 JPanel left = new JPanel(new BorderLayout()) {
150 public Dimension getMinimumSize() {
151 final Dimension original = super.getMinimumSize();
152 return new Dimension(Math.max(original.width, 100), original.height);
156 left.add(myNorthPanel, BorderLayout.NORTH);
157 myMaster = new JScrollPane(myTree);
158 left.add(myMaster, BorderLayout.CENTER);
159 mySplitter.setFirstComponent(left);
161 final JPanel right = new JPanel(new BorderLayout());
162 right.add(myDetails.getComponent(), BorderLayout.CENTER);
164 mySplitter.setSecondComponent(right);
166 GuiUtils.replaceJSplitPaneWithIDEASplitter(myWholePanel);
168 myToReinitWholePanel = false;
171 private void installAutoScroll() {
172 myAutoScrollHandler = new AutoScrollToSourceHandler() {
173 protected boolean isAutoScrollMode() {
174 return isAutoScrollEnabled();
177 protected void setAutoScrollMode(boolean state) {
178 //do nothing
181 protected void scrollToSource(Component tree) {
182 updateSelectionFromTree();
185 protected boolean needToCheckFocus() {
186 return false;
189 myAutoScrollHandler.install(myTree);
192 protected void addNotify() {
193 updateSelectionFromTree();
196 private void updateSelectionFromTree() {
197 TreePath[] treePaths = myTree.getSelectionPaths();
198 if (treePaths != null) {
199 List<NamedConfigurable> selectedConfigurables = new ArrayList<NamedConfigurable>();
200 for (TreePath path : treePaths) {
201 Object lastPathComponent = path.getLastPathComponent();
202 if (lastPathComponent instanceof MyNode) {
203 selectedConfigurables.add(((MyNode)lastPathComponent).getConfigurable());
206 if (selectedConfigurables.size() > 1 && updateMultiSelection(selectedConfigurables)) {
207 return;
211 final TreePath path = myTree.getSelectionPath();
212 if (path != null) {
213 final Object lastPathComp = path.getLastPathComponent();
214 if (!(lastPathComp instanceof MyNode)) return;
215 final MyNode node = (MyNode)lastPathComp;
216 setSelectedNode(node);
217 } else {
218 setSelectedNode(null);
222 protected boolean updateMultiSelection(final List<NamedConfigurable> selectedConfigurables) {
223 return false;
226 public DetailsComponent getDetailsComponent() {
227 return myDetails;
230 public Splitter getSplitter() {
231 return mySplitter;
234 protected boolean isAutoScrollEnabled() {
235 return myHistory != null ? !myHistory.isNavigatingNow() : true;
238 private void initToolbar() {
239 final ArrayList<AnAction> actions = createActions(false);
240 if (actions != null) {
241 final DefaultActionGroup group = new DefaultActionGroup();
242 for (AnAction action : actions) {
243 if (action instanceof ActionGroupWithPreselection) {
244 group.add(new MyActionGroupWrapper((ActionGroupWithPreselection)action));
246 else {
247 group.add(action);
250 final JComponent component = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent();
251 myNorthPanel.add(component, BorderLayout.NORTH);
255 protected void addItemsChangeListener(ItemsChangeListener l) {
256 myListners.add(l);
259 protected Dimension getPanelPrefferedSize() {
260 return new Dimension(800, 600);
263 public JComponent createComponent() {
264 reinintWholePanelIfNeeded();
266 updateSelectionFromTree();
268 SwingUtilities.updateComponentTreeUI(myWholePanel);
269 final JPanel panel = new JPanel(new BorderLayout()) {
270 public Dimension getPreferredSize() {
271 return getPanelPrefferedSize();
274 panel.add(myWholePanel, BorderLayout.CENTER);
275 return panel;
278 public boolean isModified() {
279 if (myHasDeletedItems) return true;
280 final boolean[] modified = new boolean[1];
281 TreeUtil.traverseDepth(myRoot, new TreeUtil.Traverse() {
282 public boolean accept(Object node) {
283 if (node instanceof MyNode) {
284 final NamedConfigurable configurable = ((MyNode)node).getConfigurable();
285 if (isInitialized(configurable) && configurable.isModified()) {
286 modified[0] = true;
287 return false;
290 return true;
293 return modified[0];
296 protected boolean isInitialized(final NamedConfigurable configurable) {
297 return myInitializedConfigurables.contains(configurable);
300 public void apply() throws ConfigurationException {
301 processRemovedItems();
302 final ConfigurationException[] ex = new ConfigurationException[1];
303 TreeUtil.traverse(myRoot, new TreeUtil.Traverse() {
304 public boolean accept(Object node) {
305 if (node instanceof MyNode) {
306 try {
307 final NamedConfigurable configurable = ((MyNode)node).getConfigurable();
308 if (isInitialized(configurable) && configurable.isModified()) {
309 configurable.apply();
312 catch (ConfigurationException e) {
313 ex[0] = e;
314 return false;
317 return true;
320 if (ex[0] != null) {
321 throw ex[0];
323 myHasDeletedItems = false;
326 protected abstract void processRemovedItems();
328 protected abstract boolean wasObjectStored(Object editableObject);
330 public void reset() {
331 myHasDeletedItems = false;
332 ((DefaultTreeModel)myTree.getModel()).reload();
333 //myTree.requestFocus();
334 myState.proportions.restoreSplitterProportions(myWholePanel);
336 final Enumeration enumeration = myRoot.breadthFirstEnumeration();
337 boolean selected = false;
338 while (enumeration.hasMoreElements()) {
339 final MyNode node = (MyNode)enumeration.nextElement();
340 if (node instanceof MyRootNode) continue;
341 final String path = getNodePathString(node);
342 if (!selected && Comparing.strEqual(path, myState.lastEditedConfigurable)) {
343 TreeUtil.selectInTree(node, false, myTree);
344 selected = true;
347 if (!selected) {
348 TreeUtil.selectFirstNode(myTree);
350 updateSelectionFromTree();
353 private static String getNodePathString(final MyNode node) {
354 StringBuilder path = new StringBuilder();
355 MyNode current = node;
356 while (current != null) {
357 final Object userObject = current.getUserObject();
358 if (!(userObject instanceof NamedConfigurable)) break;
359 final String displayName = current.getDisplayName();
360 if (StringUtil.isEmptyOrSpaces(displayName)) break;
361 if (path.length() > 0) {
362 path.append('|');
364 path.append(displayName);
366 final TreeNode parent = current.getParent();
367 if (!(parent instanceof MyNode)) break;
368 current = (MyNode)parent;
370 return path.toString();
373 public UIState getState() {
374 return myState;
377 public void loadState(final UIState object) {
378 myState.lastEditedConfigurable = object.lastEditedConfigurable;
379 myState.proportions = object.proportions;
380 myState.order = object.order;
383 public void disposeUIResources() {
384 myState.proportions.saveSplitterProportions(myWholePanel);
385 myAutoScrollHandler.cancelAllRequests();
386 myDetails.disposeUIResources();
387 myInitializedConfigurables.clear();
388 TreeUtil.traverseDepth(myRoot, new TreeUtil.Traverse() {
389 public boolean accept(Object node) {
390 if (node instanceof MyNode) {
391 final MyNode treeNode = ((MyNode)node);
392 treeNode.getConfigurable().disposeUIResources();
393 if (!(treeNode instanceof MyRootNode)) {
394 treeNode.setUserObject(null);
397 return true;
400 myRoot.removeAllChildren();
401 myCurrentConfigurable = null;
404 @Nullable
405 protected ArrayList<AnAction> createActions(final boolean fromPopup) {
406 return null;
410 protected void initTree() {
411 ((DefaultTreeModel)myTree.getModel()).setRoot(myRoot);
412 myTree.setRootVisible(false);
413 myTree.setShowsRootHandles(true);
414 UIUtil.setLineStyleAngled(myTree);
415 TreeUtil.installActions(myTree);
416 myTree.setCellRenderer(new ColoredTreeCellRenderer() {
417 public void customizeCellRenderer(JTree tree,
418 Object value,
419 boolean selected,
420 boolean expanded,
421 boolean leaf,
422 int row,
423 boolean hasFocus) {
424 if (value instanceof MyNode) {
425 final MyNode node = ((MyNode)value);
426 setIcon(node.getConfigurable().getIcon(expanded));
427 final Font font = UIUtil.getTreeFont();
428 if (node.isDisplayInBold()) {
429 setFont(font.deriveFont(Font.BOLD));
431 else {
432 setFont(font.deriveFont(Font.PLAIN));
434 append(node.getDisplayName(),
435 node.isDisplayInBold() ? SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES);
439 initToolbar();
440 ArrayList<AnAction> actions = createActions(true);
441 if (actions != null) {
442 final DefaultActionGroup group = new DefaultActionGroup();
443 for (AnAction action : actions) {
444 group.add(action);
446 actions = getAdditionalActions();
447 if (actions != null) {
448 group.addSeparator();
449 for (AnAction action : actions) {
450 group.add(action);
453 PopupHandler
454 .installPopupHandler(myTree, group, ActionPlaces.UNKNOWN, ActionManager.getInstance()); //popup should follow the selection
458 @Nullable
459 protected ArrayList<AnAction> getAdditionalActions() {
460 return null;
463 public void fireItemsChangeListener(final Object editableObject) {
464 for (ItemsChangeListener listener : myListners) {
465 listener.itemChanged(editableObject);
469 private void fireItemsChangedExternally() {
470 for (ItemsChangeListener listener : myListners) {
471 listener.itemsExternallyChanged();
475 private void createUIComponents() {
476 myTree = new Tree() {
477 public Dimension getPreferredScrollableViewportSize() {
478 Dimension size = super.getPreferredScrollableViewportSize();
479 size = new Dimension(size.width + 20, size.height);
480 return size;
483 @SuppressWarnings({"NonStaticInitializer"})
484 public JToolTip createToolTip() {
485 final JToolTip toolTip = new JToolTip() {
487 setUI(new MultiLineTooltipUI());
490 toolTip.setComponent(this);
491 return toolTip;
496 protected void addNode(MyNode nodeToAdd, MyNode parent) {
497 parent.add(nodeToAdd);
498 TreeUtil.sort(parent, new Comparator() {
499 public int compare(final Object o1, final Object o2) {
500 MyNode node1 = (MyNode)o1;
501 MyNode node2 = (MyNode)o2;
502 return node1.getDisplayName().compareToIgnoreCase(node2.getDisplayName());
505 ((DefaultTreeModel)myTree.getModel()).reload(parent);
508 public ActionCallback selectNodeInTree(final DefaultMutableTreeNode nodeToSelect) {
509 return selectNodeInTree(nodeToSelect, true, false);
512 public ActionCallback selectNodeInTree(final DefaultMutableTreeNode nodeToSelect, boolean requestFocus) {
513 return selectNodeInTree(nodeToSelect, true, requestFocus);
516 public ActionCallback selectNodeInTree(final DefaultMutableTreeNode nodeToSelect, boolean center, final boolean requestFocus) {
517 if (requestFocus) {
518 myTree.requestFocus();
520 if (nodeToSelect != null) {
521 return TreeUtil.selectInTree(nodeToSelect, requestFocus, myTree, center);
523 else {
524 return TreeUtil.selectFirstNode(myTree);
528 @Nullable
529 public Object getSelectedObject() {
530 final TreePath selectionPath = myTree.getSelectionPath();
531 if (selectionPath != null && selectionPath.getLastPathComponent() instanceof MyNode) {
532 MyNode node = (MyNode)selectionPath.getLastPathComponent();
533 final NamedConfigurable configurable = node.getConfigurable();
534 LOG.assertTrue(configurable != null, "already disposed");
535 return configurable.getEditableObject();
537 return null;
540 @Nullable
541 public NamedConfigurable getSelectedConfugurable() {
542 final TreePath selectionPath = myTree.getSelectionPath();
543 if (selectionPath != null) {
544 MyNode node = (MyNode)selectionPath.getLastPathComponent();
545 final NamedConfigurable configurable = node.getConfigurable();
546 LOG.assertTrue(configurable != null, "already disposed");
547 return configurable;
549 return null;
552 public void selectNodeInTree(String displayName) {
553 final MyNode nodeByName = findNodeByName(myRoot, displayName);
554 selectNodeInTree(nodeByName, true);
557 public void selectNodeInTree(final Object object) {
558 selectNodeInTree(findNodeByObject(myRoot, object), true);
561 @Nullable
562 protected static MyNode findNodeByName(final TreeNode root, final String profileName) {
563 if (profileName == null) return null; //do not suggest root node
564 return findNodeByCondition(root, new Condition<NamedConfigurable>() {
565 public boolean value(final NamedConfigurable configurable) {
566 return Comparing.strEqual(profileName, configurable.getDisplayName());
571 @Nullable
572 public static MyNode findNodeByObject(final TreeNode root, final Object editableObject) {
573 if (editableObject == null) return null; //do not suggest root node
574 return findNodeByCondition(root, new Condition<NamedConfigurable>() {
575 public boolean value(final NamedConfigurable configurable) {
576 return Comparing.equal(editableObject, configurable.getEditableObject());
581 protected static MyNode findNodeByCondition(final TreeNode root, final Condition<NamedConfigurable> condition) {
582 final MyNode[] nodeToSelect = new MyNode[1];
583 TreeUtil.traverseDepth(root, new TreeUtil.Traverse() {
584 public boolean accept(Object node) {
585 if (condition.value(((MyNode)node).getConfigurable())) {
586 nodeToSelect[0] = (MyNode)node;
587 return false;
589 return true;
592 return nodeToSelect[0];
595 protected void setSelectedNode(@Nullable MyNode node) {
596 if (node != null) {
597 myState.lastEditedConfigurable = getNodePathString(node);
599 updateSelection(node != null ? node.getConfigurable() : null);
602 protected void updateSelection(@Nullable NamedConfigurable configurable) {
603 myDetails.setText(configurable != null ? configurable.getBannerSlogan() : null);
605 myCurrentConfigurable = configurable;
607 if (configurable != null) {
608 final JComponent comp = configurable.createComponent();
609 if (comp == null) {
610 setEmpty();
611 LOG.error("createComponent() returned null. configurable=" + configurable);
612 } else {
613 myDetails.setContent(comp);
614 if (!isInitialized(configurable)) {
615 configurable.reset();
616 initializeConfigurable(configurable);
618 myHistory.pushPlaceForElement(TREE_OBJECT, configurable.getEditableObject());
620 } else {
621 setEmpty();
625 private void setEmpty() {
626 myDetails.setContent(null);
627 myDetails.setEmptyContentText(getEmptySelectionString());
630 public String getHelpTopic() {
631 if (myCurrentConfigurable != null) {
632 return myCurrentConfigurable.getHelpTopic();
634 return null;
637 protected @Nullable String getEmptySelectionString() {
638 return null;
641 protected void initializeConfigurable(final NamedConfigurable configurable) {
642 myInitializedConfigurables.add(configurable);
645 protected void checkApply(Set<MyNode> rootNodes, String prefix, String title) throws ConfigurationException {
646 for (MyNode rootNode : rootNodes) {
647 final Set<String> names = new HashSet<String>();
648 for (int i = 0; i < rootNode.getChildCount(); i++) {
649 final MyNode node = (MyNode)rootNode.getChildAt(i);
650 final NamedConfigurable scopeConfigurable = node.getConfigurable();
651 final String name = scopeConfigurable.getDisplayName();
652 if (name.trim().length() == 0) {
653 selectNodeInTree(node);
654 throw new ConfigurationException("Name should contain non-space characters");
656 if (names.contains(name)) {
657 final NamedConfigurable selectedConfugurable = getSelectedConfugurable();
658 if (selectedConfugurable == null || !Comparing.strEqual(selectedConfugurable.getDisplayName(), name)) {
659 selectNodeInTree(node);
661 throw new ConfigurationException(CommonBundle.message("smth.already.exist.error.message", prefix, name), title);
663 names.add(name);
668 public Tree getTree() {
669 return myTree;
672 protected void removePaths(final TreePath... paths) {
673 MyNode parentNode = null;
674 int idx = -1;
675 for (TreePath path : paths) {
676 final MyNode node = (MyNode)path.getLastPathComponent();
677 final NamedConfigurable namedConfigurable = node.getConfigurable();
678 final Object editableObject = namedConfigurable.getEditableObject();
679 parentNode = (MyNode)node.getParent();
680 idx = parentNode.getIndex(node);
681 parentNode.remove(node);
682 myHasDeletedItems |= wasObjectStored(editableObject);
683 fireItemsChangeListener(editableObject);
684 onItemDeleted(editableObject);
685 namedConfigurable.disposeUIResources();
687 ((DefaultTreeModel)myTree.getModel()).reload();
688 if (parentNode != null && idx != -1) {
689 TreeUtil
690 .selectInTree((DefaultMutableTreeNode)(idx < parentNode.getChildCount() ? parentNode.getChildAt(idx) : parentNode), true, myTree);
692 else {
693 TreeUtil.selectFirstNode(myTree);
697 protected void onItemDeleted(Object item) {
700 protected class MyDeleteAction extends AnAction implements DumbAware {
701 private final Condition<Object> myCondition;
703 public MyDeleteAction(Condition<Object> availableCondition) {
704 super(CommonBundle.message("button.delete"), CommonBundle.message("button.delete"), Icons.DELETE_ICON);
705 registerCustomShortcutSet(CommonShortcuts.DELETE, myTree);
706 myCondition = availableCondition;
709 public void update(AnActionEvent e) {
710 final Presentation presentation = e.getPresentation();
711 presentation.setEnabled(false);
712 final TreePath[] selectionPath = myTree.getSelectionPaths();
713 if (selectionPath != null) {
714 for (TreePath path : selectionPath) {
715 if (!myCondition.value(path.getLastPathComponent())) return;
717 presentation.setEnabled(true);
721 public void actionPerformed(AnActionEvent e) {
722 removePaths(myTree.getSelectionPaths());
726 public static class MyNode extends DefaultMutableTreeNode {
727 private boolean myDisplayInBold;
729 public MyNode(NamedConfigurable userObject) {
730 super(userObject);
733 public MyNode(NamedConfigurable userObject, boolean displayInBold) {
734 super(userObject);
735 myDisplayInBold = displayInBold;
738 @NotNull
739 public String getDisplayName() {
740 final NamedConfigurable configurable = ((NamedConfigurable)getUserObject());
741 LOG.assertTrue(configurable != null, "Tree was already disposed");
742 return configurable.getDisplayName();
745 public NamedConfigurable getConfigurable() {
746 return (NamedConfigurable)getUserObject();
749 public boolean isDisplayInBold() {
750 return myDisplayInBold;
754 @SuppressWarnings({"ConstantConditions"})
755 protected static class MyRootNode extends MyNode {
756 public MyRootNode() {
757 super(new NamedConfigurable(false, null) {
758 public void setDisplayName(String name) {
761 public Object getEditableObject() {
762 return null;
765 public String getBannerSlogan() {
766 return null;
769 public String getDisplayName() {
770 return "";
773 public Icon getIcon() {
774 return IconLoader.getIcon("/general/applicationSettings.png");
777 @Nullable
778 @NonNls
779 public String getHelpTopic() {
780 return null;
783 public JComponent createOptionsPanel() {
784 return null;
787 public boolean isModified() {
788 return false;
791 public void apply() throws ConfigurationException {
794 public void reset() {
797 public void disposeUIResources() {
800 }, false);
804 protected interface ItemsChangeListener {
805 void itemChanged(@Nullable Object deletedItem);
807 void itemsExternallyChanged();
810 public static interface ActionGroupWithPreselection {
811 ActionGroup getActionGroup();
813 int getDefaultIndex();
816 protected class MyActionGroupWrapper extends AnAction implements DumbAware {
817 private ActionGroup myActionGroup;
818 private ActionGroupWithPreselection myPreselection;
820 public MyActionGroupWrapper(final ActionGroupWithPreselection actionGroup) {
821 this(actionGroup.getActionGroup());
822 myPreselection = actionGroup;
825 public MyActionGroupWrapper(final ActionGroup actionGroup) {
826 super(actionGroup.getTemplatePresentation().getText(), actionGroup.getTemplatePresentation().getDescription(),
827 actionGroup.getTemplatePresentation().getIcon());
828 myActionGroup = actionGroup;
829 registerCustomShortcutSet(actionGroup.getShortcutSet(), myTree);
832 public void actionPerformed(AnActionEvent e) {
833 final JBPopupFactory popupFactory = JBPopupFactory.getInstance();
834 final ListPopupStep step = popupFactory.createActionsStep(myActionGroup, e.getDataContext(), false, false,
835 myActionGroup.getTemplatePresentation().getText(), myTree, true,
836 myPreselection != null ? myPreselection.getDefaultIndex() : 0, true);
837 final ListPopup listPopup = popupFactory.createListPopup(step);
838 listPopup.setHandleAutoSelectionBeforeShow(true);
839 listPopup.showUnderneathOf(myNorthPanel);
843 public JComponent getToolbar() {
844 myToReinitWholePanel = true;
845 return myNorthPanel;
848 public JComponent getMaster() {
849 myToReinitWholePanel = true;
850 return myMaster;
853 public DetailsComponent getDetails() {
854 myToReinitWholePanel = true;
855 return myDetails;
858 public void initUi() {
859 createComponent();