diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinMemberInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinMemberInfo.kt new file mode 100644 index 00000000000..848cf7fabbd --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinMemberInfo.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.refactoring + +import com.intellij.refactoring.classMembers.MemberInfoBase +import com.intellij.refactoring.classMembers.MemberInfoModel +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetNamedDeclaration + +public class KotlinMemberInfo(member: JetNamedDeclaration) : MemberInfoBase(member) { + init { + isStatic = member.getParent() is JetFile + displayName = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(member.resolveToDescriptor()) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt index 5537e0cad08..a149951831a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt @@ -31,7 +31,6 @@ import com.intellij.psi.PsiPackage import com.intellij.psi.PsiDirectory import org.jetbrains.kotlin.psi.psiUtil.getPackage import org.jetbrains.kotlin.idea.refactoring.move.getFileNameAfterMove -import java.util.HashSet import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesImpl @@ -41,6 +40,7 @@ import org.jetbrains.kotlin.psi.JetNamedFunction import org.jetbrains.kotlin.psi.JetProperty import org.jetbrains.kotlin.psi.JetNamedDeclaration import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog +import java.util.* public class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() { private fun doMoveWithCheck( @@ -68,7 +68,14 @@ public class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() { if (!CommonRefactoringUtil.checkReadOnlyStatusRecursively(project, elements.toList(), true)) return false [suppress("UNCHECKED_CAST")] - val elementsToSearch = elements.toList() as List + val elementsToSearch = elements.toSet() as Set + + val sourceFile = elementsToSearch.mapTo(LinkedHashSet()) { it.getContainingJetFile() }.singleOrNull() + if (sourceFile == null) { + CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), "All declarations must belong to the same file", null, project) + return false + } + val targetPackageName = MoveClassesOrPackagesImpl.getInitialTargetPackageName(targetContainer, elements) val targetDirectory = MoveClassesOrPackagesImpl.getInitialTargetDirectory(targetContainer, elements) val searchInComments = JavaRefactoringSettings.getInstance()!!.MOVE_SEARCH_IN_COMMENTS @@ -83,7 +90,7 @@ public class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() { val moveToPackage = targetContainer !is JetFile MoveKotlinTopLevelDeclarationsDialog( - project, elementsToSearch, targetPackageName, targetDirectory, targetFile, moveToPackage, searchInComments, searchInText, callback + project, sourceFile, elementsToSearch, targetPackageName, targetDirectory, targetFile, moveToPackage, searchInComments, searchInText, callback ).show() return true diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form index 597b9a8fad3..12aa6c7ce98 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form @@ -10,16 +10,6 @@ - - - - - - - - - - @@ -106,6 +96,14 @@ + + + + + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java index 0dba4dfce06..ee740ad7ef5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java @@ -34,6 +34,9 @@ import com.intellij.refactoring.JavaRefactoringSettings; import com.intellij.refactoring.MoveDestination; import com.intellij.refactoring.PackageWrapper; import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.classMembers.DependencyMemberInfoModel; +import com.intellij.refactoring.classMembers.MemberInfoChange; +import com.intellij.refactoring.classMembers.UsesMemberDependencyGraph; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandler; import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox; @@ -43,29 +46,51 @@ import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.ui.ComboboxWithBrowseButton; import com.intellij.ui.RecentsManager; import com.intellij.ui.ReferenceEditorComboWithBrowseButton; -import com.intellij.usageView.UsageViewUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.ui.UIUtil; import kotlin.Function0; +import kotlin.Function1; +import kotlin.KotlinPackage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.idea.core.refactoring.RefactoringPackage; import org.jetbrains.kotlin.idea.refactoring.JetRefactoringBundle; +import org.jetbrains.kotlin.idea.refactoring.KotlinMemberInfo; import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.*; +import org.jetbrains.kotlin.idea.refactoring.ui.KotlinMemberSelectionPanel; +import org.jetbrains.kotlin.idea.refactoring.ui.KotlinMemberSelectionTable; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.psi.JetNamedDeclaration; -import org.jetbrains.kotlin.idea.core.refactoring.RefactoringPackage; import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.util.List; +import java.util.Set; public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { private static final String RECENTS_KEY = "MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY"; - private JLabel elementDescription; + private class MemberInfoModelImpl extends DependencyMemberInfoModel { + public MemberInfoModelImpl() { + super(new UsesMemberDependencyGraph(sourceFile, null, false), WARNING); + } + + @Override + @Nullable + public Boolean isFixedAbstract(KotlinMemberInfo member) { + return null; + } + + @Override + public boolean isCheckedWhenDisabled(KotlinMemberInfo member) { + return false; + } + } + private JCheckBox cbSearchInComments; private JCheckBox cbSearchTextOccurences; private JPanel mainPanel; @@ -76,13 +101,16 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { private JRadioButton rbMoveToPackage; private JRadioButton rbMoveToFile; private TextFieldWithBrowseButton fileChooser; + private JPanel memberInfoPanel; + private KotlinMemberSelectionTable memberTable; - private final List elementsToMove; + private final JetFile sourceFile; private final MoveCallback moveCallback; public MoveKotlinTopLevelDeclarationsDialog( @NotNull Project project, - @NotNull List elementsToMove, + @NotNull JetFile sourceFile, + @NotNull Set elementsToMove, @Nullable String targetPackageName, @Nullable PsiDirectory targetDirectory, @Nullable JetFile targetFile, @@ -93,15 +121,13 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { ) { super(project, true); - this.elementsToMove = elementsToMove; + this.sourceFile = sourceFile; this.moveCallback = moveCallback; init(); setTitle(MoveHandler.REFACTORING_NAME); - initElementDescription(elementsToMove); - initSearchOptions(searchInComments, searchForTextOccurences); initPackageChooser(targetPackageName, targetDirectory); @@ -110,7 +136,32 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { initMoveToButtons(moveToPackage); + initMemberInfo(elementsToMove); + updateControls(); + + pack(); + } + + private void initMemberInfo(@NotNull final Set elementsToMove) { + List memberInfos = KotlinPackage.map( + KotlinPackage.filterIsInstance(sourceFile.getDeclarations(), JetNamedDeclaration.class), + new Function1() { + @Override + public KotlinMemberInfo invoke(JetNamedDeclaration declaration) { + KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration); + memberInfo.setChecked(elementsToMove.contains(declaration)); + return memberInfo; + } + } + ); + KotlinMemberSelectionPanel selectionPanel = new KotlinMemberSelectionPanel(getTitle(), memberInfos, null); + memberTable = selectionPanel.getTable(); + MemberInfoModelImpl memberInfoModel = new MemberInfoModelImpl(); + memberInfoModel.memberInfoChanged(new MemberInfoChange(memberInfos)); + selectionPanel.getTable().setMemberInfoModel(memberInfoModel); + selectionPanel.getTable().addMemberInfoChangeListener(memberInfoModel); + memberInfoPanel.add(selectionPanel, BorderLayout.CENTER); } private void initPackageChooser(String targetPackageName, PsiDirectory targetDirectory) { @@ -177,22 +228,6 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { } } - private void initElementDescription(List elementsToMove) { - if (elementsToMove.size() == 1) { - PsiElement element = elementsToMove.get(0); - elementDescription.setText( - JetRefactoringBundle.message( - "refactoring.move.specifc.element", - UsageViewUtil.getType(element), - UsageViewUtil.getLongName(element) - ) - ); - } - else if (elementsToMove.size() > 1) { - elementDescription.setText(JetRefactoringBundle.message("refactoring.move.selected.elements")); - } - } - private void createUIComponents() { classPackageChooser = createPackageChooser(); @@ -298,6 +333,8 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { @Nullable private String verifyBeforeRun() { + if (memberTable.getSelectedMemberInfos().isEmpty()) return "At least one member must be selected"; + if (isMoveToPackage()) { String name = getTargetPackage().trim(); if (name.length() != 0 && !PsiNameHelper.getInstance(myProject).isQualifiedName(name)) { @@ -314,6 +351,18 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { return null; } + private List getSelectedElementsToMove() { + return KotlinPackage.map( + memberTable.getSelectedMemberInfos(), + new Function1() { + @Override + public JetNamedDeclaration invoke(KotlinMemberInfo info) { + return info.getMember(); + } + } + ); + } + @Override protected JComponent createCenterPanel() { boolean isDestinationVisible = hasAnySourceRoots(); @@ -354,6 +403,9 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { if (target == null) return; saveRefactoringSettings(); + + List elementsToMove = getSelectedElementsToMove(); + for (PsiElement element : elementsToMove) { String message = target.verify(element.getContainingFile()); if (message != null) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/ui/KotlinMemberSelectionPanel.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/ui/KotlinMemberSelectionPanel.kt new file mode 100644 index 00000000000..829219b13af --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/ui/KotlinMemberSelectionPanel.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.refactoring.ui + +import com.intellij.refactoring.ui.AbstractMemberSelectionPanel +import com.intellij.ui.ScrollPaneFactory +import com.intellij.ui.SeparatorFactory +import org.jetbrains.kotlin.idea.refactoring.KotlinMemberInfo +import org.jetbrains.kotlin.psi.JetNamedDeclaration +import java.awt.BorderLayout + +public class KotlinMemberSelectionPanel(title: String, + memberInfo: List, + abstractColumnHeader: String? +) : AbstractMemberSelectionPanel() { + private val table = createMemberSelectionTable(memberInfo, abstractColumnHeader) + + init { + setLayout(BorderLayout()) + + val scrollPane = ScrollPaneFactory.createScrollPane(table) + add(SeparatorFactory.createSeparator(title, table), BorderLayout.NORTH) + add(scrollPane, BorderLayout.CENTER) + } + + protected fun createMemberSelectionTable( + memberInfo: List, + abstractColumnHeader: String? + ): KotlinMemberSelectionTable { + return KotlinMemberSelectionTable(memberInfo, null, abstractColumnHeader) + } + + override fun getTable() = table +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/ui/KotlinMemberSelectionTable.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/ui/KotlinMemberSelectionTable.kt new file mode 100644 index 00000000000..5284d400c20 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/ui/KotlinMemberSelectionTable.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2015 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 org.jetbrains.kotlin.idea.refactoring.ui + +import com.intellij.refactoring.classMembers.MemberInfoModel +import com.intellij.refactoring.ui.AbstractMemberSelectionTable +import com.intellij.ui.RowIcon +import com.intellij.util.IconUtil +import org.jetbrains.kotlin.idea.JetIconProvider +import org.jetbrains.kotlin.idea.refactoring.KotlinMemberInfo +import org.jetbrains.kotlin.psi.JetNamedDeclaration + +public class KotlinMemberSelectionTable( + memberInfos: List, + memberInfoModel: MemberInfoModel?, + abstractColumnHeader: String? +) : AbstractMemberSelectionTable(memberInfos, memberInfoModel, abstractColumnHeader) { + override fun getAbstractColumnValue(memberInfo: KotlinMemberInfo) = null + + override fun isAbstractColumnEditable(rowIndex: Int) = false + + override fun setVisibilityIcon(memberInfo: KotlinMemberInfo, icon: RowIcon) { + icon.setIcon(JetIconProvider.getVisibilityIcon(memberInfo.getMember().getModifierList()), 1); + } + + override fun getOverrideIcon(memberInfo: KotlinMemberInfo) = AbstractMemberSelectionTable.EMPTY_OVERRIDE_ICON +} \ No newline at end of file