| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.intellij.ide.util; |
| |
| import com.intellij.codeInsight.generation.*; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.IdeBundle; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.keymap.KeymapManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.ui.VerticalFlowLayout; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.psi.codeStyle.CodeStyleSettings; |
| import com.intellij.psi.codeStyle.CodeStyleSettingsManager; |
| import com.intellij.ui.*; |
| import com.intellij.ui.treeStructure.Tree; |
| import com.intellij.util.PlatformIcons; |
| import com.intellij.util.containers.Convertor; |
| import com.intellij.util.containers.FactoryMap; |
| import com.intellij.util.containers.HashMap; |
| import com.intellij.util.ui.UIUtil; |
| import com.intellij.util.ui.tree.TreeUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.tree.*; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.util.*; |
| import java.util.List; |
| |
| public class MemberChooser<T extends ClassMember> extends DialogWrapper implements TypeSafeDataProvider { |
| protected Tree myTree; |
| private DefaultTreeModel myTreeModel; |
| protected JComponent[] myOptionControls; |
| private JCheckBox myCopyJavadocCheckbox; |
| private JCheckBox myInsertOverrideAnnotationCheckbox; |
| private final ArrayList<MemberNode> mySelectedNodes = new ArrayList<MemberNode>(); |
| |
| private final SortEmAction mySortAction; |
| |
| private boolean myAlphabeticallySorted = false; |
| private boolean myShowClasses = true; |
| protected boolean myAllowEmptySelection = false; |
| private final boolean myAllowMultiSelection; |
| private final Project myProject; |
| private final boolean myIsInsertOverrideVisible; |
| private final JComponent myHeaderPanel; |
| |
| protected T[] myElements; |
| protected Comparator<ElementNode> myComparator = new OrderComparator(); |
| |
| protected final HashMap<MemberNode,ParentNode> myNodeToParentMap = new HashMap<MemberNode, ParentNode>(); |
| protected final HashMap<ClassMember, MemberNode> myElementToNodeMap = new HashMap<ClassMember, MemberNode>(); |
| protected final ArrayList<ContainerNode> myContainerNodes = new ArrayList<ContainerNode>(); |
| |
| protected LinkedHashSet<T> mySelectedElements; |
| |
| @NonNls private static final String PROP_SORTED = "MemberChooser.sorted"; |
| @NonNls private static final String PROP_SHOWCLASSES = "MemberChooser.showClasses"; |
| @NonNls private static final String PROP_COPYJAVADOC = "MemberChooser.copyJavadoc"; |
| |
| public MemberChooser(T[] elements, |
| boolean allowEmptySelection, |
| boolean allowMultiSelection, |
| @NotNull Project project, |
| @Nullable JComponent headerPanel, |
| JComponent[] optionControls) { |
| this(allowEmptySelection, allowMultiSelection, project, false, headerPanel, optionControls); |
| resetElements(elements); |
| init(); |
| } |
| |
| public MemberChooser(T[] elements, boolean allowEmptySelection, boolean allowMultiSelection, @NotNull Project project) { |
| this(elements, allowEmptySelection, allowMultiSelection, project, false); |
| } |
| |
| public MemberChooser(T[] elements, |
| boolean allowEmptySelection, |
| boolean allowMultiSelection, |
| @NotNull Project project, |
| boolean isInsertOverrideVisible) { |
| this(elements, allowEmptySelection, allowMultiSelection, project, isInsertOverrideVisible, null); |
| } |
| |
| public MemberChooser(T[] elements, |
| boolean allowEmptySelection, |
| boolean allowMultiSelection, |
| @NotNull Project project, |
| boolean isInsertOverrideVisible, |
| @Nullable JComponent headerPanel |
| ) { |
| this(allowEmptySelection, allowMultiSelection, project, isInsertOverrideVisible, headerPanel, null); |
| resetElements(elements); |
| init(); |
| } |
| |
| protected MemberChooser(boolean allowEmptySelection, |
| boolean allowMultiSelection, |
| @NotNull Project project, |
| boolean isInsertOverrideVisible, |
| @Nullable JComponent headerPanel, |
| @Nullable JComponent[] optionControls) { |
| super(project, true); |
| myAllowEmptySelection = allowEmptySelection; |
| myAllowMultiSelection = allowMultiSelection; |
| myProject = project; |
| myIsInsertOverrideVisible = isInsertOverrideVisible; |
| myHeaderPanel = headerPanel; |
| myTree = createTree(); |
| myOptionControls = optionControls; |
| mySortAction = new SortEmAction(); |
| mySortAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.ALT_MASK)), myTree); |
| } |
| |
| protected void resetElementsWithDefaultComparator(T[] elements, final boolean restoreSelectedElements) { |
| myComparator = myAlphabeticallySorted ? new AlphaComparator() : new OrderComparator(); |
| resetElements(elements, null, restoreSelectedElements); |
| } |
| |
| public void resetElements(T[] elements) { |
| resetElements(elements, null, false); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public void resetElements(T[] elements, final @Nullable Comparator<T> sortComparator, final boolean restoreSelectedElements) { |
| final List<T> selectedElements = restoreSelectedElements && mySelectedElements != null ? new ArrayList<T>(mySelectedElements) : null; |
| myElements = elements; |
| if (sortComparator != null) { |
| myComparator = new ElementNodeComparatorWrapper(sortComparator); |
| } |
| mySelectedNodes.clear(); |
| myNodeToParentMap.clear(); |
| myElementToNodeMap.clear(); |
| myContainerNodes.clear(); |
| |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| @Override |
| public void run() { |
| myTreeModel = buildModel(); |
| } |
| }); |
| |
| myTree.setModel(myTreeModel); |
| myTree.setRootVisible(false); |
| |
| |
| doSort(); |
| |
| defaultExpandTree(); |
| |
| //restoreTree(); |
| |
| if (myOptionControls == null) { |
| myCopyJavadocCheckbox = new NonFocusableCheckBox(IdeBundle.message("checkbox.copy.javadoc")); |
| if (myIsInsertOverrideVisible) { |
| myInsertOverrideAnnotationCheckbox = new NonFocusableCheckBox(IdeBundle.message("checkbox.insert.at.override")); |
| myOptionControls = new JCheckBox[] {myCopyJavadocCheckbox, myInsertOverrideAnnotationCheckbox}; |
| } |
| else { |
| myOptionControls = new JCheckBox[] {myCopyJavadocCheckbox}; |
| } |
| } |
| |
| myTree.doLayout(); |
| setOKActionEnabled(myAllowEmptySelection || myElements != null && myElements.length > 0); |
| |
| if (selectedElements != null) { |
| selectElements(selectedElements.toArray(new ClassMember[selectedElements.size()])); |
| } |
| if (mySelectedElements == null || mySelectedElements.isEmpty()) { |
| expandFirst(); |
| } |
| } |
| |
| /** |
| * should be invoked in read action |
| */ |
| private DefaultTreeModel buildModel() { |
| final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); |
| final Ref<Integer> count = new Ref<Integer>(0); |
| final FactoryMap<MemberChooserObject, ParentNode> map = new FactoryMap<MemberChooserObject, ParentNode>() { |
| @Override |
| protected ParentNode create(final MemberChooserObject key) { |
| ParentNode node = null; |
| DefaultMutableTreeNode parentNode = rootNode; |
| |
| if (supportsNestedContainers() && key instanceof ClassMember) { |
| MemberChooserObject parentNodeDelegate = ((ClassMember)key).getParentNodeDelegate(); |
| |
| if (parentNodeDelegate != null) { |
| parentNode = get(parentNodeDelegate); |
| } |
| } |
| if (isContainerNode(key)) { |
| final ContainerNode containerNode = new ContainerNode(parentNode, key, count); |
| node = containerNode; |
| myContainerNodes.add(containerNode); |
| } |
| if (node == null) { |
| node = new ParentNode(parentNode, key, count); |
| } |
| return node; |
| } |
| }; |
| |
| for (T object : myElements) { |
| final ParentNode parentNode = map.get(object.getParentNodeDelegate()); |
| final MemberNode elementNode = createMemberNode(count, object, parentNode); |
| myNodeToParentMap.put(elementNode, parentNode); |
| myElementToNodeMap.put(object, elementNode); |
| } |
| return new DefaultTreeModel(rootNode); |
| } |
| |
| protected MemberNode createMemberNode(Ref<Integer> count, T object, ParentNode parentNode) { |
| return new MemberNodeImpl(parentNode, object, count); |
| } |
| |
| protected boolean supportsNestedContainers() { |
| return false; |
| } |
| |
| protected void defaultExpandTree() { |
| TreeUtil.expandAll(myTree); |
| } |
| |
| protected boolean isContainerNode(MemberChooserObject key) { |
| return key instanceof PsiElementMemberChooserObject; |
| } |
| |
| public void selectElements(ClassMember[] elements) { |
| ArrayList<TreePath> selectionPaths = new ArrayList<TreePath>(); |
| for (ClassMember element : elements) { |
| MemberNode treeNode = myElementToNodeMap.get(element); |
| if (treeNode != null) { |
| selectionPaths.add(new TreePath(((DefaultMutableTreeNode)treeNode).getPath())); |
| } |
| } |
| myTree.setSelectionPaths(selectionPaths.toArray(new TreePath[selectionPaths.size()])); |
| } |
| |
| |
| @Override |
| @NotNull |
| protected Action[] createActions() { |
| if (myAllowEmptySelection) { |
| return new Action[]{getOKAction(), new SelectNoneAction(), getCancelAction()}; |
| } |
| else { |
| return new Action[]{getOKAction(), getCancelAction()}; |
| } |
| } |
| |
| @Override |
| protected void doHelpAction() { |
| } |
| |
| protected void customizeOptionsPanel() { |
| if (myInsertOverrideAnnotationCheckbox != null && myIsInsertOverrideVisible) { |
| CodeStyleSettings styleSettings = CodeStyleSettingsManager.getInstance(myProject).getCurrentSettings(); |
| myInsertOverrideAnnotationCheckbox.setSelected(styleSettings.INSERT_OVERRIDE_ANNOTATION); |
| } |
| if (myCopyJavadocCheckbox != null) { |
| myCopyJavadocCheckbox.setSelected(PropertiesComponent.getInstance().isTrueValue(PROP_COPYJAVADOC)); |
| } |
| } |
| |
| @Override |
| protected JComponent createSouthPanel() { |
| JPanel panel = new JPanel(new GridBagLayout()); |
| |
| customizeOptionsPanel(); |
| JPanel optionsPanel = new JPanel(new VerticalFlowLayout()); |
| for (final JComponent component : myOptionControls) { |
| optionsPanel.add(component); |
| } |
| |
| panel.add( |
| optionsPanel, |
| new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, |
| new Insets(0, 0, 0, 5), 0, 0) |
| ); |
| |
| if (!myAllowEmptySelection && (myElements == null || myElements.length == 0)) { |
| setOKActionEnabled(false); |
| } |
| panel.add( |
| super.createSouthPanel(), |
| new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.SOUTH, GridBagConstraints.NONE, |
| new Insets(0, 0, 0, 0), 0, 0) |
| ); |
| return panel; |
| } |
| |
| @Override |
| protected JComponent createNorthPanel() { |
| return myHeaderPanel; |
| } |
| |
| @Override |
| protected JComponent createCenterPanel() { |
| JPanel panel = new JPanel(new BorderLayout()); |
| |
| // Toolbar |
| |
| DefaultActionGroup group = new DefaultActionGroup(); |
| |
| fillToolbarActions(group); |
| |
| group.addSeparator(); |
| |
| ExpandAllAction expandAllAction = new ExpandAllAction(); |
| expandAllAction.registerCustomShortcutSet( |
| new CustomShortcutSet(KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_EXPAND_ALL)), |
| myTree); |
| group.add(expandAllAction); |
| |
| CollapseAllAction collapseAllAction = new CollapseAllAction(); |
| collapseAllAction.registerCustomShortcutSet( |
| new CustomShortcutSet(KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_COLLAPSE_ALL)), |
| myTree); |
| group.add(collapseAllAction); |
| |
| panel.add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent(), |
| BorderLayout.NORTH); |
| |
| // Tree |
| expandFirst(); |
| defaultExpandTree(); |
| installSpeedSearch(); |
| |
| JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTree); |
| scrollPane.setPreferredSize(new Dimension(350, 450)); |
| panel.add(scrollPane, BorderLayout.CENTER); |
| |
| return panel; |
| } |
| |
| private void expandFirst() { |
| if (getRootNode().getChildCount() > 0) { |
| myTree.expandRow(0); |
| myTree.setSelectionRow(1); |
| } |
| } |
| |
| protected Tree createTree() { |
| final Tree tree = new Tree(new DefaultTreeModel(new DefaultMutableTreeNode())); |
| |
| tree.setCellRenderer(getTreeCellRenderer()); |
| UIUtil.setLineStyleAngled(tree); |
| tree.setRootVisible(false); |
| tree.setShowsRootHandles(true); |
| tree.addKeyListener(new TreeKeyListener()); |
| tree.addTreeSelectionListener(new MyTreeSelectionListener()); |
| |
| if (!myAllowMultiSelection) { |
| tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); |
| } |
| |
| new DoubleClickListener() { |
| @Override |
| protected boolean onDoubleClick(MouseEvent e) { |
| if (tree.getPathForLocation(e.getX(), e.getY()) != null) { |
| doOKAction(); |
| return true; |
| } |
| return false; |
| } |
| }.installOn(tree); |
| |
| TreeUtil.installActions(tree); |
| return tree; |
| } |
| |
| protected TreeCellRenderer getTreeCellRenderer() { |
| return new ColoredTreeCellRenderer() { |
| @Override |
| public void customizeCellRenderer(JTree tree, Object value, boolean selected, boolean expanded, |
| boolean leaf, int row, boolean hasFocus) { |
| if (value instanceof ElementNode) { |
| ((ElementNode) value).getDelegate().renderTreeNode(this, tree); |
| } |
| } |
| }; |
| } |
| |
| @NotNull |
| protected String convertElementText(@NotNull String originalElementText) { |
| String res = originalElementText; |
| |
| int i = res.indexOf(':'); |
| if (i >= 0) { |
| res = res.substring(0, i); |
| } |
| i = res.indexOf('('); |
| if (i >= 0) { |
| res = res.substring(0, i); |
| } |
| |
| return res; |
| } |
| |
| protected void installSpeedSearch() { |
| final TreeSpeedSearch treeSpeedSearch = new TreeSpeedSearch(myTree, new Convertor<TreePath, String>() { |
| @Override |
| @Nullable |
| public String convert(TreePath path) { |
| final ElementNode lastPathComponent = (ElementNode)path.getLastPathComponent(); |
| if (lastPathComponent == null) return null; |
| String text = lastPathComponent.getDelegate().getText(); |
| if (text != null) { |
| text = convertElementText(text); |
| } |
| return text; |
| } |
| }); |
| treeSpeedSearch.setComparator(getSpeedSearchComparator()); |
| } |
| |
| protected SpeedSearchComparator getSpeedSearchComparator() { |
| return new SpeedSearchComparator(false); |
| } |
| |
| protected void disableAlphabeticalSorting(final AnActionEvent event) { |
| mySortAction.setSelected(event, false); |
| } |
| |
| protected void onAlphabeticalSortingEnabled(final AnActionEvent event) { |
| //do nothing by default |
| } |
| |
| protected void fillToolbarActions(DefaultActionGroup group) { |
| final boolean alphabeticallySorted = PropertiesComponent.getInstance().isTrueValue(PROP_SORTED); |
| if (alphabeticallySorted) { |
| setSortComparator(new OrderComparator()); |
| } |
| myAlphabeticallySorted = alphabeticallySorted; |
| group.add(mySortAction); |
| |
| if (!supportsNestedContainers()) { |
| ShowContainersAction showContainersAction = getShowContainersAction(); |
| showContainersAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_MASK)), |
| myTree); |
| setShowClasses(PropertiesComponent.getInstance().isTrueValue(PROP_SHOWCLASSES)); |
| group.add(showContainersAction); |
| } |
| } |
| |
| @Override |
| protected String getDimensionServiceKey() { |
| return "#com.intellij.ide.util.MemberChooser"; |
| } |
| |
| @Override |
| public JComponent getPreferredFocusedComponent() { |
| return myTree; |
| } |
| |
| public JComponent[] getOptionControls() { |
| return myOptionControls; |
| } |
| |
| @Nullable |
| private LinkedHashSet<T> getSelectedElementsList() { |
| return getExitCode() == OK_EXIT_CODE ? mySelectedElements : null; |
| } |
| |
| @Nullable |
| public List<T> getSelectedElements() { |
| final LinkedHashSet<T> list = getSelectedElementsList(); |
| return list == null ? null : new ArrayList<T>(list); |
| } |
| |
| @Nullable |
| public T[] getSelectedElements(T[] a) { |
| LinkedHashSet<T> list = getSelectedElementsList(); |
| if (list == null) return null; |
| return list.toArray(a); |
| } |
| |
| protected final boolean areElementsSelected() { |
| return mySelectedElements != null && !mySelectedElements.isEmpty(); |
| } |
| |
| public void setCopyJavadocVisible(boolean state) { |
| myCopyJavadocCheckbox.setVisible(state); |
| } |
| |
| public boolean isCopyJavadoc() { |
| return myCopyJavadocCheckbox.isSelected(); |
| } |
| |
| public boolean isInsertOverrideAnnotation() { |
| return myIsInsertOverrideVisible && myInsertOverrideAnnotationCheckbox.isSelected(); |
| } |
| |
| private boolean isAlphabeticallySorted() { |
| return myAlphabeticallySorted; |
| } |
| |
| @SuppressWarnings("unchecked") |
| protected void changeSortComparator(final Comparator<T> comparator) { |
| setSortComparator(new ElementNodeComparatorWrapper(comparator)); |
| } |
| |
| private void setSortComparator(Comparator<ElementNode> sortComparator) { |
| if (myComparator.equals(sortComparator)) return; |
| myComparator = sortComparator; |
| doSort(); |
| } |
| |
| protected void doSort() { |
| Pair<ElementNode, List<ElementNode>> pair = storeSelection(); |
| |
| Enumeration<TreeNode> children = getRootNodeChildren(); |
| while (children.hasMoreElements()) { |
| ParentNode classNode = (ParentNode)children.nextElement(); |
| sortNode(classNode, myComparator); |
| myTreeModel.nodeStructureChanged(classNode); |
| } |
| |
| restoreSelection(pair); |
| } |
| |
| private static void sortNode(ParentNode node, final Comparator<ElementNode> sortComparator) { |
| ArrayList<ElementNode> arrayList = new ArrayList<ElementNode>(); |
| Enumeration<TreeNode> children = node.children(); |
| while (children.hasMoreElements()) { |
| arrayList.add((ElementNode)children.nextElement()); |
| } |
| |
| Collections.sort(arrayList, sortComparator); |
| |
| replaceChildren(node, arrayList); |
| } |
| |
| private static void replaceChildren(final DefaultMutableTreeNode node, final Collection<? extends ElementNode> arrayList) { |
| node.removeAllChildren(); |
| for (ElementNode child : arrayList) { |
| node.add(child); |
| } |
| } |
| |
| protected void restoreTree() { |
| Pair<ElementNode, List<ElementNode>> selection = storeSelection(); |
| |
| DefaultMutableTreeNode root = getRootNode(); |
| if (!myShowClasses || myContainerNodes.isEmpty()) { |
| List<ParentNode> otherObjects = new ArrayList<ParentNode>(); |
| Enumeration<TreeNode> children = getRootNodeChildren(); |
| ParentNode newRoot = new ParentNode(null, new MemberChooserObjectBase(getAllContainersNodeName()), new Ref<Integer>(0)); |
| while (children.hasMoreElements()) { |
| final ParentNode nextElement = (ParentNode)children.nextElement(); |
| if (nextElement instanceof ContainerNode) { |
| final ContainerNode containerNode = (ContainerNode)nextElement; |
| Enumeration<TreeNode> memberNodes = containerNode.children(); |
| List<MemberNode> memberNodesList = new ArrayList<MemberNode>(); |
| while (memberNodes.hasMoreElements()) { |
| memberNodesList.add((MemberNode)memberNodes.nextElement()); |
| } |
| for (MemberNode memberNode : memberNodesList) { |
| newRoot.add(memberNode); |
| } |
| } |
| else { |
| otherObjects.add(nextElement); |
| } |
| } |
| replaceChildren(root, otherObjects); |
| sortNode(newRoot, myComparator); |
| if (newRoot.children().hasMoreElements()) root.add(newRoot); |
| } |
| else { |
| Enumeration<TreeNode> children = getRootNodeChildren(); |
| while (children.hasMoreElements()) { |
| ParentNode allClassesNode = (ParentNode)children.nextElement(); |
| Enumeration<TreeNode> memberNodes = allClassesNode.children(); |
| ArrayList<MemberNode> arrayList = new ArrayList<MemberNode>(); |
| while (memberNodes.hasMoreElements()) { |
| arrayList.add((MemberNode)memberNodes.nextElement()); |
| } |
| Collections.sort(arrayList, myComparator); |
| for (MemberNode memberNode : arrayList) { |
| myNodeToParentMap.get(memberNode).add(memberNode); |
| } |
| } |
| replaceChildren(root, myContainerNodes); |
| } |
| myTreeModel.nodeStructureChanged(root); |
| |
| defaultExpandTree(); |
| |
| restoreSelection(selection); |
| } |
| |
| private void setShowClasses(boolean showClasses) { |
| myShowClasses = showClasses; |
| restoreTree(); |
| } |
| |
| protected String getAllContainersNodeName() { |
| return IdeBundle.message("node.memberchooser.all.classes"); |
| } |
| |
| private Enumeration<TreeNode> getRootNodeChildren() { |
| return getRootNode().children(); |
| } |
| |
| protected DefaultMutableTreeNode getRootNode() { |
| return (DefaultMutableTreeNode)myTreeModel.getRoot(); |
| } |
| |
| private Pair<ElementNode,List<ElementNode>> storeSelection() { |
| List<ElementNode> selectedNodes = new ArrayList<ElementNode>(); |
| TreePath[] paths = myTree.getSelectionPaths(); |
| if (paths != null) { |
| for (TreePath path : paths) { |
| selectedNodes.add((ElementNode)path.getLastPathComponent()); |
| } |
| } |
| TreePath leadSelectionPath = myTree.getLeadSelectionPath(); |
| return Pair.create(leadSelectionPath != null ? (ElementNode)leadSelectionPath.getLastPathComponent() : null, selectedNodes); |
| } |
| |
| |
| private void restoreSelection(Pair<ElementNode,List<ElementNode>> pair) { |
| List<ElementNode> selectedNodes = pair.second; |
| |
| DefaultMutableTreeNode root = getRootNode(); |
| |
| ArrayList<TreePath> toSelect = new ArrayList<TreePath>(); |
| for (ElementNode node : selectedNodes) { |
| DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)node; |
| if (root.isNodeDescendant(treeNode)) { |
| toSelect.add(new TreePath(treeNode.getPath())); |
| } |
| } |
| |
| if (!toSelect.isEmpty()) { |
| myTree.setSelectionPaths(toSelect.toArray(new TreePath[toSelect.size()])); |
| } |
| |
| ElementNode leadNode = pair.first; |
| if (leadNode != null) { |
| myTree.setLeadSelectionPath(new TreePath(((DefaultMutableTreeNode)leadNode).getPath())); |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| PropertiesComponent instance = PropertiesComponent.getInstance(); |
| instance.setValue(PROP_SORTED, Boolean.toString(isAlphabeticallySorted())); |
| instance.setValue(PROP_SHOWCLASSES, Boolean.toString(myShowClasses)); |
| |
| if (myCopyJavadocCheckbox != null) { |
| instance.setValue(PROP_COPYJAVADOC, Boolean.toString(myCopyJavadocCheckbox.isSelected())); |
| } |
| |
| final Container contentPane = getContentPane(); |
| if (contentPane != null) { |
| contentPane.removeAll(); |
| } |
| mySelectedNodes.clear(); |
| myElements = null; |
| super.dispose(); |
| } |
| |
| @Override |
| public void calcData(final DataKey key, final DataSink sink) { |
| if (key.equals(CommonDataKeys.PSI_ELEMENT)) { |
| if (mySelectedElements != null && !mySelectedElements.isEmpty()) { |
| T selectedElement = mySelectedElements.iterator().next(); |
| if (selectedElement instanceof ClassMemberWithElement) { |
| sink.put(CommonDataKeys.PSI_ELEMENT, ((ClassMemberWithElement)selectedElement).getElement()); |
| } |
| } |
| } |
| } |
| |
| private class MyTreeSelectionListener implements TreeSelectionListener { |
| @Override |
| public void valueChanged(TreeSelectionEvent e) { |
| TreePath[] paths = e.getPaths(); |
| if (paths == null) return; |
| for (int i = 0; i < paths.length; i++) { |
| Object node = paths[i].getLastPathComponent(); |
| if (node instanceof MemberNode) { |
| final MemberNode memberNode = (MemberNode)node; |
| if (e.isAddedPath(i)) { |
| if (!mySelectedNodes.contains(memberNode)) { |
| mySelectedNodes.add(memberNode); |
| } |
| } |
| else { |
| mySelectedNodes.remove(memberNode); |
| } |
| } |
| } |
| mySelectedElements = new LinkedHashSet<T>(); |
| for (MemberNode selectedNode : mySelectedNodes) { |
| mySelectedElements.add((T)selectedNode.getDelegate()); |
| } |
| } |
| } |
| |
| protected interface ElementNode extends MutableTreeNode { |
| MemberChooserObject getDelegate(); |
| int getOrder(); |
| } |
| |
| protected interface MemberNode extends ElementNode {} |
| |
| protected abstract static class ElementNodeImpl extends DefaultMutableTreeNode implements ElementNode { |
| private final int myOrder; |
| private final MemberChooserObject myDelegate; |
| |
| public ElementNodeImpl(@Nullable DefaultMutableTreeNode parent, MemberChooserObject delegate, Ref<Integer> order) { |
| myOrder = order.get(); |
| order.set(myOrder + 1); |
| myDelegate = delegate; |
| if (parent != null) { |
| parent.add(this); |
| } |
| } |
| |
| @Override |
| public MemberChooserObject getDelegate() { |
| return myDelegate; |
| } |
| |
| @Override |
| public int getOrder() { |
| return myOrder; |
| } |
| } |
| |
| protected static class MemberNodeImpl extends ElementNodeImpl implements MemberNode { |
| public MemberNodeImpl(ParentNode parent, ClassMember delegate, Ref<Integer> order) { |
| super(parent, delegate, order); |
| } |
| } |
| |
| protected static class ParentNode extends ElementNodeImpl { |
| public ParentNode(@Nullable DefaultMutableTreeNode parent, MemberChooserObject delegate, Ref<Integer> order) { |
| super(parent, delegate, order); |
| } |
| } |
| |
| protected static class ContainerNode extends ParentNode { |
| public ContainerNode(DefaultMutableTreeNode parent, MemberChooserObject delegate, Ref<Integer> order) { |
| super(parent, delegate, order); |
| } |
| } |
| |
| private class SelectNoneAction extends AbstractAction { |
| public SelectNoneAction() { |
| super(IdeBundle.message("action.select.none")); |
| } |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| myTree.clearSelection(); |
| doOKAction(); |
| } |
| } |
| |
| private class TreeKeyListener extends KeyAdapter { |
| @Override |
| public void keyPressed(KeyEvent e) { |
| TreePath path = myTree.getLeadSelectionPath(); |
| if (path == null) return; |
| final Object lastComponent = path.getLastPathComponent(); |
| if (e.getKeyCode() == KeyEvent.VK_ENTER) { |
| if (lastComponent instanceof ParentNode) return; |
| doOKAction(); |
| e.consume(); |
| } |
| else if (e.getKeyCode() == KeyEvent.VK_INSERT) { |
| if (lastComponent instanceof ElementNode) { |
| final DefaultMutableTreeNode node = (DefaultMutableTreeNode)lastComponent; |
| if (!mySelectedNodes.contains(node)) { |
| if (node.getNextNode() != null) { |
| myTree.setSelectionPath(new TreePath(node.getNextNode().getPath())); |
| } |
| } |
| else { |
| if (node.getNextNode() != null) { |
| myTree.removeSelectionPath(new TreePath(node.getPath())); |
| myTree.setSelectionPath(new TreePath(node.getNextNode().getPath())); |
| myTree.repaint(); |
| } |
| } |
| e.consume(); |
| } |
| } |
| } |
| } |
| |
| private class SortEmAction extends ToggleAction { |
| public SortEmAction() { |
| super(IdeBundle.message("action.sort.alphabetically"), |
| IdeBundle.message("action.sort.alphabetically"), AllIcons.ObjectBrowser.Sorted); |
| } |
| |
| @Override |
| public boolean isSelected(AnActionEvent event) { |
| return isAlphabeticallySorted(); |
| } |
| |
| @Override |
| public void setSelected(AnActionEvent event, boolean flag) { |
| myAlphabeticallySorted = flag; |
| setSortComparator(flag ? new AlphaComparator() : new OrderComparator()); |
| if (flag) { |
| MemberChooser.this.onAlphabeticalSortingEnabled(event); |
| } |
| } |
| } |
| |
| protected ShowContainersAction getShowContainersAction() { |
| return new ShowContainersAction(IdeBundle.message("action.show.classes"), PlatformIcons.CLASS_ICON); |
| } |
| |
| protected class ShowContainersAction extends ToggleAction { |
| public ShowContainersAction(final String text, final Icon icon) { |
| super(text, text, icon); |
| } |
| |
| @Override |
| public boolean isSelected(AnActionEvent event) { |
| return myShowClasses; |
| } |
| |
| @Override |
| public void setSelected(AnActionEvent event, boolean flag) { |
| setShowClasses(flag); |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| super.update(e); |
| Presentation presentation = e.getPresentation(); |
| presentation.setEnabled(myContainerNodes.size() > 1); |
| } |
| } |
| |
| private class ExpandAllAction extends AnAction { |
| public ExpandAllAction() { |
| super(IdeBundle.message("action.expand.all"), IdeBundle.message("action.expand.all"), |
| AllIcons.Actions.Expandall); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| TreeUtil.expandAll(myTree); |
| } |
| } |
| |
| private class CollapseAllAction extends AnAction { |
| public CollapseAllAction() { |
| super(IdeBundle.message("action.collapse.all"), IdeBundle.message("action.collapse.all"), |
| AllIcons.Actions.Collapseall); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| TreeUtil.collapseAll(myTree, 1); |
| } |
| } |
| |
| private static class AlphaComparator implements Comparator<ElementNode> { |
| @Override |
| public int compare(ElementNode n1, ElementNode n2) { |
| return n1.getDelegate().getText().compareToIgnoreCase(n2.getDelegate().getText()); |
| } |
| } |
| |
| protected static class OrderComparator implements Comparator<ElementNode> { |
| public OrderComparator() { |
| } // To make this class instanceable from the subclasses |
| |
| @Override |
| public int compare(ElementNode n1, ElementNode n2) { |
| if (n1.getDelegate() instanceof ClassMemberWithElement && n2.getDelegate() instanceof ClassMemberWithElement) { |
| return ((ClassMemberWithElement)n1.getDelegate()).getElement().getTextOffset() - |
| ((ClassMemberWithElement)n2.getDelegate()).getElement().getTextOffset(); |
| } |
| return n1.getOrder() - n2.getOrder(); |
| } |
| } |
| |
| private static class ElementNodeComparatorWrapper<T> implements Comparator<ElementNode> { |
| private final Comparator<T> myDelegate; |
| |
| public ElementNodeComparatorWrapper(final Comparator<T> delegate) { |
| myDelegate = delegate; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public int compare(final ElementNode o1, final ElementNode o2) { |
| return myDelegate.compare((T) o1.getDelegate(), (T) o2.getDelegate()); |
| } |
| } |
| } |