diff -r 963fc1b0ba6e -r 70b4fb448811 RefactoringRope/MoveDialog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RefactoringRope/MoveDialog.py Fri Sep 22 19:47:36 2017 +0200 @@ -0,0 +1,305 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2010 - 2017 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the Move Method or Module dialog. +""" + +from __future__ import unicode_literals + +import os + +from PyQt5.QtCore import pyqtSlot +from PyQt5.QtWidgets import QDialogButtonBox, QAbstractButton + +from E5Gui import E5FileDialog, E5MessageBox +from E5Gui.E5Application import e5App +from E5Gui.E5Completers import E5FileCompleter + +from Ui_MoveDialog import Ui_MoveDialog +from RefactoringDialogBase import RefactoringDialogBase + +import Utilities + + +class MoveDialog(RefactoringDialogBase, Ui_MoveDialog): + """ + Class implementing the Move Method or Module dialog. + """ + def __init__(self, refactoring, title, filename, offset, parent=None): + """ + Constructor + + @param refactoring reference to the main refactoring object + @type Refactoring + @param title title of the dialog + @type str + @param filename file name to be worked on + @type str + @param offset offset within file + @type int or None + @param parent reference to the parent widget + @type QWidget + """ + RefactoringDialogBase.__init__(self, refactoring, title, parent) + self.setupUi(self) + + self._changeGroupName = "Move" + + self.__destinationCompleter = E5FileCompleter(self.destinationEdit) + + self.__filename = filename + self.__offset = offset + + self.__project = e5App().getObject("Project") + + self.__okButton = self.buttonBox.button(QDialogButtonBox.Ok) + self.__okButton.setEnabled(False) + self.__previewButton = self.buttonBox.addButton( + self.tr("Preview"), QDialogButtonBox.ActionRole) + self.__previewButton.setDefault(True) + + self.moveStackWidget.setCurrentIndex(0) + + self.__moveType = "" + + if offset is None: + # it is a 'move module' refactoring, no need to determine + # the move type via the client + self.__processMoveType({"Kind": "move_module"}) + else: + self._refactoring.sendJson("RequestMoveType", { + "ChangeGroup": self._changeGroupName, + "Title": self._title, + "FileName": self.__filename, + "Offset": self.__offset, + }) + + def __processMoveType(self, data): + """ + Private method to process the move type data sent by the refactoring + client in order to polish the dialog. + + @param data dictionary containing the move type data + @type dict + """ + self.__moveType = data["Kind"] + + if self.__moveType == "move_method": + self.setWindowTitle(self.tr("Move Method")) + self.moveStackWidget.setCurrentIndex(1) + self.methodEdit.setText(data["Method"]) + self.methodEdit.selectAll() + elif self.__moveType == "move_global_method": + self.setWindowTitle(self.tr("Move Global Method")) + self.moveStackWidget.setCurrentIndex(2) + self.destinationLabel.setText(self.tr("Destination Module:")) + self.destinationEdit.setToolTip(self.tr( + "Enter the destination module for the method")) + self.selectButton.setToolTip(self.tr( + "Select the destination module via a file selection dialog")) + elif self.__moveType == "move_module": + self.setWindowTitle(self.tr("Move Module")) + self.moveStackWidget.setCurrentIndex(2) + self.destinationLabel.setText(self.tr("Destination Package:")) + self.destinationEdit.setToolTip(self.tr( + "Enter the destination package for the module")) + self.selectButton.setToolTip(self.tr( + "Select the destination package via a directory selection" + " dialog")) + else: + self.setWindowTitle(self.tr("Move")) + self.moveStackWidget.setCurrentIndex(0) + + self.__updateUI() + + msh = self.minimumSizeHint() + self.resize(max(self.width(), msh.width()), msh.height()) + + @pyqtSlot(str) + def on_attributeEdit_textChanged(self, text): + """ + Private slot to react to changes of the attribute. + + @param text text entered into the edit + @type str + """ + self.__updateUI() + + @pyqtSlot(str) + def on_methodEdit_textChanged(self, text): + """ + Private slot to react to changes of the method. + + @param text text entered into the edit + @type str + """ + self.__updateUI() + + @pyqtSlot(str) + def on_destinationEdit_textChanged(self, text): + """ + Private slot to react to changes of the destination module. + + @param text text entered into the edit + @type str + """ + self.__updateUI() + + def __updateUI(self): + """ + Private method to perform various UI updates. + """ + if self.__moveType == "move_method": + enable = self.attributeEdit.text() != "" and \ + self.methodEdit.text() != "" + elif self.__moveType in ["move_global_method", "move_module"]: + enable = self.destinationEdit.text() != "" + else: + enable = False + + self.__okButton.setEnabled(enable) + self.__previewButton.setEnabled(enable) + + @pyqtSlot() + def on_selectButton_clicked(self): + """ + Private slot called to select the destination module via a file + selection dialog. + """ + dest = self.destinationEdit.text() + if not dest: + dest = self.__project.getProjectPath() + elif not os.path.isabs(dest): + dest = os.path.join(self.__project.getProjectPath(), dest) + if self.__moveType == "move_global_method": + destination = E5FileDialog.getOpenFileName( + self, + self.windowTitle(), + dest, + self.tr("Python Files (*.py *.py2 *.py3);;All Files (*)")) + else: + # move_module + destination = E5FileDialog.getExistingDirectory( + self, + self.windowTitle(), + dest) + + if destination: + destination = Utilities.toNativeSeparators(destination) + if not self.__project.startswithProjectPath(destination): + if self.__moveType == "move_global_method": + errorMessage = self.tr("""The selected module must be """ + """inside the project.""") + else: + # move_module + errorMessage = self.tr("""The selected directory must""" + """ be inside the project.""") + E5MessageBox.critical( + self, + self.windowTitle(), + errorMessage) + return + + if self.__moveType == "move_global_method": + if not os.path.exists(destination): + E5MessageBox.critical( + self, + self.windowTitle(), + self.tr("""The selected module <b>{0}</b> does""" + """ not exist.""").format(destination)) + return + else: + # move_module + if not os.path.exists( + os.path.join(destination, "__init__.py")): + E5MessageBox.critical( + self, + self.windowTitle(), + self.tr("""The selected directory <b>{0}</b> is""" + """ not a package.""").format(destination)) + return + + destination = self.__project.getRelativePath(destination) + self.destinationEdit.setText(destination) + + def __checkDestination(self): + """ + Private method to check the destination entered. + + @return flag indicating a valid entry (boolean) + """ + destination = os.path.join( + self.__project.getProjectPath(), + self.destinationEdit.text()) + if self.__moveType == "move_global_method": + if not os.path.exists(destination): + E5MessageBox.critical( + self, + self.windowTitle(), + self.tr("""The selected module <b>{0}</b> does""" + """ not exist.""").format(destination)) + return False + else: + # move_module + if not os.path.exists(os.path.join(destination, "__init__.py")): + E5MessageBox.critical( + self, + self.windowTitle(), + self.tr("""The selected directory <b>{0}</b> is""" + """ not a package.""").format(destination)) + return False + + return True + + @pyqtSlot(QAbstractButton) + def on_buttonBox_clicked(self, button): + """ + Private slot to act on the button pressed. + + @param button reference to the button pressed + @type QAbstractButton + """ + if self.__moveType == "move_method" or ( + self.__moveType in ["move_global_method", "move_module"] and + self.__checkDestination()): + if button == self.__previewButton: + self.requestPreview() + elif button == self.__okButton: + self.applyChanges() + + def _calculateChanges(self): + """ + Protected method to initiate the calculation of the changes. + """ + newName = self.methodEdit.text() + if not newName: + newName = None + + self._refactoring.sendJson("CalculateMoveChanges", { + "ChangeGroup": self._changeGroupName, + "Title": self.windowTitle(), + "FileName": self.__filename, + "Offset": self.__offset, + "Kind": self.__moveType, + "NewName": newName, + "Attribute": self.attributeEdit.text(), + "DestinationModule": self.destinationEdit.text(), + }) + + def processChangeData(self, data): + """ + Public method to process the change data sent by the refactoring + client. + + @param data dictionary containing the change data + @type dict + """ + subcommand = data["Subcommand"] + if subcommand == "MoveType": + self.__processMoveType(data) + else: + # pass on to base class + RefactoringDialogBase.processChangeData(self, data)