PY-17265 Cleanup related to transition to Java 8
[idea/community.git] / python / src / com / jetbrains / python / refactoring / move / moduleMembers / PyMoveModuleMembersDialog.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.jetbrains.python.refactoring.move.moduleMembers;
17
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.MemberInfoBase;
23 import com.intellij.refactoring.classMembers.MemberInfoChange;
24 import com.intellij.refactoring.classMembers.MemberInfoModel;
25 import com.intellij.refactoring.ui.AbstractMemberSelectionTable;
26 import com.intellij.ui.HideableDecorator;
27 import com.intellij.ui.RowIcon;
28 import com.intellij.ui.components.JBScrollPane;
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;
41
42 import javax.swing.*;
43 import javax.swing.event.TableModelEvent;
44 import javax.swing.event.TableModelListener;
45 import java.awt.*;
46 import java.util.Collection;
47 import java.util.List;
48
49 /**
50  * @author Mikhail Golubev
51  */
52 public class PyMoveModuleMembersDialog extends PyBaseMoveDialog {
53   @NonNls private final static String BULK_MOVE_TABLE_VISIBLE = "python.move.module.members.dialog.show.table";
54
55   private final TopLevelSymbolsSelectionTable myMemberSelectionTable;
56   private final PyModuleMemberInfoModel myModuleMemberModel;
57   private final boolean mySeveralElementsSelected;
58
59   /**
60    * @param project dialog project
61    * @param elements elements to move
62    * @param source
63    * @param destination destination where elements have to be moved
64    */
65   public PyMoveModuleMembersDialog(@NotNull Project project,
66                                    @NotNull List<PsiNamedElement> elements,
67                                    @NotNull String source,
68                                    @NotNull String destination) {
69     super(project, source, destination);
70
71     assert !elements.isEmpty();
72     final PsiNamedElement firstElement = elements.get(0);
73     setTitle(PyBundle.message("refactoring.move.module.members.dialog.title"));
74
75     final PyFile pyFile = (PyFile)firstElement.getContainingFile();
76     myModuleMemberModel = new PyModuleMemberInfoModel(pyFile);
77
78     final List<PyModuleMemberInfo> symbolsInfos = collectModuleMemberInfos(myModuleMemberModel.myPyFile);
79     for (PyModuleMemberInfo info : symbolsInfos) {
80       //noinspection SuspiciousMethodCalls
81       info.setChecked(elements.contains(info.getMember()));
82     }
83     myModuleMemberModel.memberInfoChanged(new MemberInfoChange<>(symbolsInfos));
84     myMemberSelectionTable = new TopLevelSymbolsSelectionTable(symbolsInfos, myModuleMemberModel);
85     myMemberSelectionTable.addMemberInfoChangeListener(myModuleMemberModel);
86     myMemberSelectionTable.getModel().addTableModelListener(new TableModelListener() {
87       @Override
88       public void tableChanged(TableModelEvent e) {
89         validateButtons();
90       }
91     });
92     mySeveralElementsSelected = elements.size() > 1;
93     final boolean tableIsVisible = mySeveralElementsSelected || PropertiesComponent.getInstance().getBoolean(BULK_MOVE_TABLE_VISIBLE);
94     final String description;
95     if (!tableIsVisible && elements.size() == 1) {
96       final String name = PyMoveRefactoringUtil.getCompactPresentableName(firstElement);
97       if (firstElement instanceof PyFunction) {
98         description =  PyBundle.message("refactoring.move.module.members.dialog.description.function.$0", name);
99       }
100       else if (firstElement instanceof PyClass) {
101         description = PyBundle.message("refactoring.move.module.members.dialog.description.class.$0", name);
102       }
103       else {
104         description = PyBundle.message("refactoring.move.module.members.dialog.description.variable.$0", name);
105       }
106     }
107     else {
108       description = PyBundle.message("refactoring.move.module.members.dialog.description.selection");
109     }
110     myDescription.setText(description);
111     final HideableDecorator decorator = new HideableDecorator(myExtraPanel, PyBundle.message("refactoring.move.module.members.dialog.table.title"), true) {
112       @Override
113       protected void on() {
114         super.on();
115         myDescription.setText(PyBundle.message("refactoring.move.module.members.dialog.description.selection"));
116         PropertiesComponent.getInstance().setValue(BULK_MOVE_TABLE_VISIBLE, true);
117       }
118
119       @Override
120       protected void off() {
121         super.off();
122         PropertiesComponent.getInstance().setValue(BULK_MOVE_TABLE_VISIBLE, false);
123       }
124     };
125     decorator.setOn(tableIsVisible);
126     decorator.setContentComponent(new JBScrollPane(myMemberSelectionTable) {
127       @Override
128       public Dimension getMinimumSize() {
129         // Prevent growth of the dialog after several expand/collapse actions
130         return new Dimension((int)super.getMinimumSize().getWidth(), 0);
131       }
132     });
133
134     init();
135   }
136
137   @Override
138   protected void doWhenFirstShown() {
139     super.doWhenFirstShown();
140     enlargeDialogHeightIfNecessary();
141   }
142   
143   private void enlargeDialogHeightIfNecessary() {
144     if (mySeveralElementsSelected && !PropertiesComponent.getInstance(getProject()).getBoolean(BULK_MOVE_TABLE_VISIBLE)) {
145       final DialogWrapperPeer peer = getPeer();
146       final Dimension realSize = peer.getSize();
147       final double preferredHeight = peer.getPreferredSize().getHeight();
148       if (realSize.getHeight() < preferredHeight) {
149         peer.setSize((int)realSize.getWidth(), (int)preferredHeight);
150       }
151     }
152   }
153
154   @Nullable
155   @Override
156   protected String getDimensionServiceKey() {
157     return "#com.jetbrains.python.refactoring.move.PyMoveModuleMembersDialog";
158   }
159
160   @Override
161   protected String getHelpId() {
162     return "python.reference.moveModuleMembers";
163   }
164
165   @Override
166   protected boolean areButtonsValid() {
167     return !myMemberSelectionTable.getSelectedMemberInfos().isEmpty();
168   }
169
170   /**
171    * @return selected elements in the same order as they are declared in the original file
172    */
173   @NotNull
174   public List<PyElement> getSelectedTopLevelSymbols() {
175     final Collection<PyModuleMemberInfo> selectedMembers = myMemberSelectionTable.getSelectedMemberInfos();
176     final List<PyElement> selectedElements = ContainerUtil.map(selectedMembers, MemberInfoBase::getMember);
177     return ContainerUtil.sorted(selectedElements, (e1, e2) -> PyPsiUtils.isBefore(e1, e2) ? -1 : 1);
178   }
179
180   @NotNull
181   private static List<PyModuleMemberInfo> collectModuleMemberInfos(@NotNull PyFile pyFile) {
182     final List<PyElement> moduleMembers = PyMoveModuleMembersHelper.getTopLevelModuleMembers(pyFile);
183     return ContainerUtil.mapNotNull(moduleMembers, PyModuleMemberInfo::new);
184   }
185
186   static class TopLevelSymbolsSelectionTable extends AbstractMemberSelectionTable<PyElement, PyModuleMemberInfo> {
187     public TopLevelSymbolsSelectionTable(Collection<PyModuleMemberInfo> memberInfos,
188                                          @Nullable MemberInfoModel<PyElement, PyModuleMemberInfo> memberInfoModel) {
189       super(memberInfos, memberInfoModel, null);
190     }
191
192     @Nullable
193     @Override
194     protected Object getAbstractColumnValue(PyModuleMemberInfo memberInfo) {
195       return null;
196     }
197
198     @Override
199     protected boolean isAbstractColumnEditable(int rowIndex) {
200       return false;
201     }
202
203     @Override
204     protected void setVisibilityIcon(PyModuleMemberInfo memberInfo, RowIcon icon) {
205
206     }
207
208     @Override
209     protected Icon getOverrideIcon(PyModuleMemberInfo memberInfo) {
210       return null;
211     }
212   }
213 }