RefactoringRope/MoveDialog.py

Tue, 10 Dec 2024 15:49:01 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Tue, 10 Dec 2024 15:49:01 +0100
branch
eric7
changeset 426
7592a1c052e8
parent 420
fa31c3a0df1d
permissions
-rw-r--r--

Updated copyright for 2025.

# -*- coding: utf-8 -*-

# Copyright (c) 2010 - 2025 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the Move Method or Module dialog.
"""

import os

from PyQt6.QtCore import pyqtSlot
from PyQt6.QtWidgets import QAbstractButton, QDialogButtonBox

from eric7.EricWidgets import EricFileDialog, EricMessageBox
from eric7.EricWidgets.EricApplication import ericApp
from eric7.EricWidgets.EricCompleters import EricFileCompleter

try:
    from eric7.SystemUtilities.FileSystemUtilities import toNativeSeparators
except ImportError:
    # backward compatibility for eric < 23.1
    from eric7.Utilities import toNativeSeparators

from .RefactoringDialogBase import RefactoringDialogBase
from .Ui_MoveDialog import Ui_MoveDialog


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 RefactoringServer
        @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 = EricFileCompleter(self.destinationEdit)

        self.__filename = filename
        self.__offset = offset

        self.__project = ericApp().getObject("Project")

        self.__okButton = self.buttonBox.button(QDialogButtonBox.StandardButton.Ok)
        self.__okButton.setEnabled(False)
        self.__previewButton = self.buttonBox.addButton(
            self.tr("Preview"), QDialogButtonBox.ButtonRole.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):  # noqa: U100
        """
        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):  # noqa: U100
        """
        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):  # noqa: U100
        """
        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)
        destination = (
            EricFileDialog.getOpenFileName(
                self,
                self.windowTitle(),
                dest,
                self.tr("Python Files (*.py *.py3);;All Files (*)"),
            )
            if self.__moveType == "move_global_method"
            else
            # move_module
            EricFileDialog.getExistingDirectory(self, self.windowTitle(), dest)
        )

        if destination:
            destination = 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."""
                    )
                EricMessageBox.critical(self, self.windowTitle(), errorMessage)
                return

            if self.__moveType == "move_global_method":
                if not os.path.exists(destination):
                    EricMessageBox.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")):
                    EricMessageBox.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
        @rtype bool
        """
        destination = os.path.join(
            self.__project.getProjectPath(), self.destinationEdit.text()
        )
        if self.__moveType == "move_global_method":
            if not os.path.exists(destination):
                EricMessageBox.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")):
                EricMessageBox.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)

eric ide

mercurial