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
;
49 import javax
.swing
.tree
.DefaultMutableTreeNode
;
50 import javax
.swing
.tree
.DefaultTreeModel
;
51 import javax
.swing
.tree
.TreeNode
;
52 import javax
.swing
.tree
.TreePath
;
55 import java
.util
.List
;
62 public abstract class MasterDetailsComponent
implements Configurable
, PersistentStateComponent
<MasterDetailsComponent
.UIState
>, DetailsComponent
.Facade
,
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
) {
78 public ActionCallback
navigateTo(@Nullable final Place place
, final boolean requestFocus
) {
82 public void queryPlace(@NotNull final Place place
) {
85 private JScrollPane myMaster
;
87 public void setHistory(final History 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() {
104 MyNode node
= (MyNode
)myTree
.getSelectionPath().getLastPathComponent();
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() {
134 reinintWholePanelIfNeeded();
137 private void reinintWholePanelIfNeeded() {
138 if (!myToReinitWholePanel
) return;
140 myWholePanel
= new JPanel(new BorderLayout()) {
141 public void 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
) {
181 protected void scrollToSource(Component tree
) {
182 updateSelectionFromTree();
185 protected boolean needToCheckFocus() {
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
)) {
211 final TreePath path
= myTree
.getSelectionPath();
213 final Object lastPathComp
= path
.getLastPathComponent();
214 if (!(lastPathComp
instanceof MyNode
)) return;
215 final MyNode node
= (MyNode
)lastPathComp
;
216 setSelectedNode(node
);
218 setSelectedNode(null);
222 protected boolean updateMultiSelection(final List
<NamedConfigurable
> selectedConfigurables
) {
226 public DetailsComponent
getDetailsComponent() {
230 public Splitter
getSplitter() {
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
));
250 final JComponent component
= ActionManager
.getInstance().createActionToolbar(ActionPlaces
.UNKNOWN
, group
, true).getComponent();
251 myNorthPanel
.add(component
, BorderLayout
.NORTH
);
255 protected void addItemsChangeListener(ItemsChangeListener 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
);
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()) {
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
) {
307 final NamedConfigurable configurable
= ((MyNode
)node
).getConfigurable();
308 if (isInitialized(configurable
) && configurable
.isModified()) {
309 configurable
.apply();
312 catch (ConfigurationException e
) {
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
);
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) {
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() {
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);
400 myRoot
.removeAllChildren();
401 myCurrentConfigurable
= null;
405 protected ArrayList
<AnAction
> createActions(final boolean fromPopup
) {
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
,
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
));
432 setFont(font
.deriveFont(Font
.PLAIN
));
434 append(node
.getDisplayName(),
435 node
.isDisplayInBold() ? SimpleTextAttributes
.REGULAR_BOLD_ATTRIBUTES
: SimpleTextAttributes
.REGULAR_ATTRIBUTES
);
440 ArrayList
<AnAction
> actions
= createActions(true);
441 if (actions
!= null) {
442 final DefaultActionGroup group
= new DefaultActionGroup();
443 for (AnAction action
: actions
) {
446 actions
= getAdditionalActions();
447 if (actions
!= null) {
448 group
.addSeparator();
449 for (AnAction action
: actions
) {
454 .installPopupHandler(myTree
, group
, ActionPlaces
.UNKNOWN
, ActionManager
.getInstance()); //popup should follow the selection
459 protected ArrayList
<AnAction
> getAdditionalActions() {
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
);
483 @SuppressWarnings({"NonStaticInitializer"})
484 public JToolTip
createToolTip() {
485 final JToolTip toolTip
= new JToolTip() {
487 setUI(new MultiLineTooltipUI());
490 toolTip
.setComponent(this);
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
) {
518 myTree
.requestFocus();
520 if (nodeToSelect
!= null) {
521 return TreeUtil
.selectInTree(nodeToSelect
, requestFocus
, myTree
, center
);
524 return TreeUtil
.selectFirstNode(myTree
);
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();
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");
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);
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());
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
;
592 return nodeToSelect
[0];
595 protected void setSelectedNode(@Nullable MyNode node
) {
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();
611 LOG
.error("createComponent() returned null. configurable=" + configurable
);
613 myDetails
.setContent(comp
);
614 if (!isInitialized(configurable
)) {
615 configurable
.reset();
616 initializeConfigurable(configurable
);
618 myHistory
.pushPlaceForElement(TREE_OBJECT
, configurable
.getEditableObject());
625 private void setEmpty() {
626 myDetails
.setContent(null);
627 myDetails
.setEmptyContentText(getEmptySelectionString());
630 public String
getHelpTopic() {
631 if (myCurrentConfigurable
!= null) {
632 return myCurrentConfigurable
.getHelpTopic();
637 protected @Nullable String
getEmptySelectionString() {
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
);
668 public Tree
getTree() {
672 protected void removePaths(final TreePath
... paths
) {
673 MyNode parentNode
= null;
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) {
690 .selectInTree((DefaultMutableTreeNode
)(idx
< parentNode
.getChildCount() ? parentNode
.getChildAt(idx
) : parentNode
), true, myTree
);
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
) {
733 public MyNode(NamedConfigurable userObject
, boolean displayInBold
) {
735 myDisplayInBold
= displayInBold
;
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() {
765 public String
getBannerSlogan() {
769 public String
getDisplayName() {
773 public Icon
getIcon() {
774 return IconLoader
.getIcon("/general/applicationSettings.png");
779 public String
getHelpTopic() {
783 public JComponent
createOptionsPanel() {
787 public boolean isModified() {
791 public void apply() throws ConfigurationException
{
794 public void reset() {
797 public void disposeUIResources() {
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;
848 public JComponent
getMaster() {
849 myToReinitWholePanel
= true;
853 public DetailsComponent
getDetails() {
854 myToReinitWholePanel
= true;
858 public void initUi() {