2 * Copyright 2000-2015 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.
16 package com.jetbrains.python.refactoring.move.moduleMembers;
18 import com.intellij.ide.util.PropertiesComponent;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.ui.DialogWrapperPeer;
21 import com.intellij.psi.PsiNamedElement;
22 import com.intellij.refactoring.classMembers.MemberInfoChange;
23 import com.intellij.refactoring.classMembers.MemberInfoModel;
24 import com.intellij.refactoring.ui.AbstractMemberSelectionTable;
25 import com.intellij.ui.HideableDecorator;
26 import com.intellij.ui.RowIcon;
27 import com.intellij.ui.components.JBScrollPane;
28 import com.intellij.util.Function;
29 import com.intellij.util.containers.ContainerUtil;
30 import com.jetbrains.python.PyBundle;
31 import com.jetbrains.python.psi.PyClass;
32 import com.jetbrains.python.psi.PyElement;
33 import com.jetbrains.python.psi.PyFile;
34 import com.jetbrains.python.psi.PyFunction;
35 import com.jetbrains.python.psi.impl.PyPsiUtils;
36 import com.jetbrains.python.refactoring.move.PyBaseMoveDialog;
37 import com.jetbrains.python.refactoring.move.PyMoveRefactoringUtil;
38 import org.jetbrains.annotations.NonNls;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
43 import javax.swing.event.TableModelEvent;
44 import javax.swing.event.TableModelListener;
46 import java.util.Collection;
47 import java.util.Comparator;
48 import java.util.List;
51 * @author Mikhail Golubev
53 public class PyMoveModuleMembersDialog extends PyBaseMoveDialog {
54 @NonNls private final static String BULK_MOVE_TABLE_VISIBLE = "python.move.module.members.dialog.show.table";
56 private final TopLevelSymbolsSelectionTable myMemberSelectionTable;
57 private final PyModuleMemberInfoModel myModuleMemberModel;
58 private final boolean mySeveralElementsSelected;
61 * @param project dialog project
62 * @param elements elements to move
64 * @param destination destination where elements have to be moved
66 public PyMoveModuleMembersDialog(@NotNull Project project,
67 @NotNull List<PsiNamedElement> elements,
68 @NotNull String source,
69 @NotNull String destination) {
70 super(project, source, destination);
72 assert !elements.isEmpty();
73 final PsiNamedElement firstElement = elements.get(0);
74 setTitle(PyBundle.message("refactoring.move.module.members.dialog.title"));
76 final PyFile pyFile = (PyFile)firstElement.getContainingFile();
77 myModuleMemberModel = new PyModuleMemberInfoModel(pyFile);
79 final List<PyModuleMemberInfo> symbolsInfos = collectModuleMemberInfos(myModuleMemberModel.myPyFile);
80 for (PyModuleMemberInfo info : symbolsInfos) {
81 //noinspection SuspiciousMethodCalls
82 info.setChecked(elements.contains(info.getMember()));
84 myModuleMemberModel.memberInfoChanged(new MemberInfoChange<>(symbolsInfos));
85 myMemberSelectionTable = new TopLevelSymbolsSelectionTable(symbolsInfos, myModuleMemberModel);
86 myMemberSelectionTable.addMemberInfoChangeListener(myModuleMemberModel);
87 myMemberSelectionTable.getModel().addTableModelListener(new TableModelListener() {
89 public void tableChanged(TableModelEvent e) {
93 mySeveralElementsSelected = elements.size() > 1;
94 final boolean tableIsVisible = mySeveralElementsSelected || PropertiesComponent.getInstance().getBoolean(BULK_MOVE_TABLE_VISIBLE);
95 final String description;
96 if (!tableIsVisible && elements.size() == 1) {
97 final String name = PyMoveRefactoringUtil.getCompactPresentableName(firstElement);
98 if (firstElement instanceof PyFunction) {
99 description = PyBundle.message("refactoring.move.module.members.dialog.description.function.$0", name);
101 else if (firstElement instanceof PyClass) {
102 description = PyBundle.message("refactoring.move.module.members.dialog.description.class.$0", name);
105 description = PyBundle.message("refactoring.move.module.members.dialog.description.variable.$0", name);
109 description = PyBundle.message("refactoring.move.module.members.dialog.description.selection");
111 myDescription.setText(description);
112 final HideableDecorator decorator = new HideableDecorator(myExtraPanel, PyBundle.message("refactoring.move.module.members.dialog.table.title"), true) {
114 protected void on() {
116 myDescription.setText(PyBundle.message("refactoring.move.module.members.dialog.description.selection"));
117 PropertiesComponent.getInstance().setValue(BULK_MOVE_TABLE_VISIBLE, true);
121 protected void off() {
123 PropertiesComponent.getInstance().setValue(BULK_MOVE_TABLE_VISIBLE, false);
126 decorator.setOn(tableIsVisible);
127 decorator.setContentComponent(new JBScrollPane(myMemberSelectionTable) {
129 public Dimension getMinimumSize() {
130 // Prevent growth of the dialog after several expand/collapse actions
131 return new Dimension((int)super.getMinimumSize().getWidth(), 0);
139 protected void doWhenFirstShown() {
140 super.doWhenFirstShown();
141 enlargeDialogHeightIfNecessary();
144 private void enlargeDialogHeightIfNecessary() {
145 if (mySeveralElementsSelected && !PropertiesComponent.getInstance(getProject()).getBoolean(BULK_MOVE_TABLE_VISIBLE)) {
146 final DialogWrapperPeer peer = getPeer();
147 final Dimension realSize = peer.getSize();
148 final double preferredHeight = peer.getPreferredSize().getHeight();
149 if (realSize.getHeight() < preferredHeight) {
150 peer.setSize((int)realSize.getWidth(), (int)preferredHeight);
157 protected String getDimensionServiceKey() {
158 return "#com.jetbrains.python.refactoring.move.PyMoveModuleMembersDialog";
162 protected String getHelpId() {
163 return "python.reference.moveModuleMembers";
167 protected boolean areButtonsValid() {
168 return !myMemberSelectionTable.getSelectedMemberInfos().isEmpty();
172 * @return selected elements in the same order as they are declared in the original file
175 public List<PyElement> getSelectedTopLevelSymbols() {
176 final Collection<PyModuleMemberInfo> selectedMembers = myMemberSelectionTable.getSelectedMemberInfos();
177 final List<PyElement> selectedElements = ContainerUtil.map(selectedMembers, info -> info.getMember());
178 return ContainerUtil.sorted(selectedElements, (e1, e2) -> PyPsiUtils.isBefore(e1, e2) ? -1 : 1);
182 private static List<PyModuleMemberInfo> collectModuleMemberInfos(@NotNull PyFile pyFile) {
183 final List<PyElement> moduleMembers = PyMoveModuleMembersHelper.getTopLevelModuleMembers(pyFile);
184 return ContainerUtil.mapNotNull(moduleMembers, element -> new PyModuleMemberInfo(element));
187 static class TopLevelSymbolsSelectionTable extends AbstractMemberSelectionTable<PyElement, PyModuleMemberInfo> {
188 public TopLevelSymbolsSelectionTable(Collection<PyModuleMemberInfo> memberInfos,
189 @Nullable MemberInfoModel<PyElement, PyModuleMemberInfo> memberInfoModel) {
190 super(memberInfos, memberInfoModel, null);
195 protected Object getAbstractColumnValue(PyModuleMemberInfo memberInfo) {
200 protected boolean isAbstractColumnEditable(int rowIndex) {
205 protected void setVisibilityIcon(PyModuleMemberInfo memberInfo, RowIcon icon) {
210 protected Icon getOverrideIcon(PyModuleMemberInfo memberInfo) {