|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2010 - 2017 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the Move Method or Module dialog. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 import os |
|
13 |
|
14 from PyQt5.QtCore import pyqtSlot |
|
15 from PyQt5.QtWidgets import QDialogButtonBox, QAbstractButton |
|
16 |
|
17 from E5Gui import E5FileDialog, E5MessageBox |
|
18 from E5Gui.E5Application import e5App |
|
19 from E5Gui.E5Completers import E5FileCompleter |
|
20 |
|
21 from Ui_MoveDialog import Ui_MoveDialog |
|
22 from RefactoringDialogBase import RefactoringDialogBase |
|
23 |
|
24 import Utilities |
|
25 |
|
26 |
|
27 class MoveDialog(RefactoringDialogBase, Ui_MoveDialog): |
|
28 """ |
|
29 Class implementing the Move Method or Module dialog. |
|
30 """ |
|
31 def __init__(self, refactoring, title, filename, offset, parent=None): |
|
32 """ |
|
33 Constructor |
|
34 |
|
35 @param refactoring reference to the main refactoring object |
|
36 @type Refactoring |
|
37 @param title title of the dialog |
|
38 @type str |
|
39 @param filename file name to be worked on |
|
40 @type str |
|
41 @param offset offset within file |
|
42 @type int or None |
|
43 @param parent reference to the parent widget |
|
44 @type QWidget |
|
45 """ |
|
46 RefactoringDialogBase.__init__(self, refactoring, title, parent) |
|
47 self.setupUi(self) |
|
48 |
|
49 self._changeGroupName = "Move" |
|
50 |
|
51 self.__destinationCompleter = E5FileCompleter(self.destinationEdit) |
|
52 |
|
53 self.__filename = filename |
|
54 self.__offset = offset |
|
55 |
|
56 self.__project = e5App().getObject("Project") |
|
57 |
|
58 self.__okButton = self.buttonBox.button(QDialogButtonBox.Ok) |
|
59 self.__okButton.setEnabled(False) |
|
60 self.__previewButton = self.buttonBox.addButton( |
|
61 self.tr("Preview"), QDialogButtonBox.ActionRole) |
|
62 self.__previewButton.setDefault(True) |
|
63 |
|
64 self.moveStackWidget.setCurrentIndex(0) |
|
65 |
|
66 self.__moveType = "" |
|
67 |
|
68 if offset is None: |
|
69 # it is a 'move module' refactoring, no need to determine |
|
70 # the move type via the client |
|
71 self.__processMoveType({"Kind": "move_module"}) |
|
72 else: |
|
73 self._refactoring.sendJson("RequestMoveType", { |
|
74 "ChangeGroup": self._changeGroupName, |
|
75 "Title": self._title, |
|
76 "FileName": self.__filename, |
|
77 "Offset": self.__offset, |
|
78 }) |
|
79 |
|
80 def __processMoveType(self, data): |
|
81 """ |
|
82 Private method to process the move type data sent by the refactoring |
|
83 client in order to polish the dialog. |
|
84 |
|
85 @param data dictionary containing the move type data |
|
86 @type dict |
|
87 """ |
|
88 self.__moveType = data["Kind"] |
|
89 |
|
90 if self.__moveType == "move_method": |
|
91 self.setWindowTitle(self.tr("Move Method")) |
|
92 self.moveStackWidget.setCurrentIndex(1) |
|
93 self.methodEdit.setText(data["Method"]) |
|
94 self.methodEdit.selectAll() |
|
95 elif self.__moveType == "move_global_method": |
|
96 self.setWindowTitle(self.tr("Move Global Method")) |
|
97 self.moveStackWidget.setCurrentIndex(2) |
|
98 self.destinationLabel.setText(self.tr("Destination Module:")) |
|
99 self.destinationEdit.setToolTip(self.tr( |
|
100 "Enter the destination module for the method")) |
|
101 self.selectButton.setToolTip(self.tr( |
|
102 "Select the destination module via a file selection dialog")) |
|
103 elif self.__moveType == "move_module": |
|
104 self.setWindowTitle(self.tr("Move Module")) |
|
105 self.moveStackWidget.setCurrentIndex(2) |
|
106 self.destinationLabel.setText(self.tr("Destination Package:")) |
|
107 self.destinationEdit.setToolTip(self.tr( |
|
108 "Enter the destination package for the module")) |
|
109 self.selectButton.setToolTip(self.tr( |
|
110 "Select the destination package via a directory selection" |
|
111 " dialog")) |
|
112 else: |
|
113 self.setWindowTitle(self.tr("Move")) |
|
114 self.moveStackWidget.setCurrentIndex(0) |
|
115 |
|
116 self.__updateUI() |
|
117 |
|
118 msh = self.minimumSizeHint() |
|
119 self.resize(max(self.width(), msh.width()), msh.height()) |
|
120 |
|
121 @pyqtSlot(str) |
|
122 def on_attributeEdit_textChanged(self, text): |
|
123 """ |
|
124 Private slot to react to changes of the attribute. |
|
125 |
|
126 @param text text entered into the edit |
|
127 @type str |
|
128 """ |
|
129 self.__updateUI() |
|
130 |
|
131 @pyqtSlot(str) |
|
132 def on_methodEdit_textChanged(self, text): |
|
133 """ |
|
134 Private slot to react to changes of the method. |
|
135 |
|
136 @param text text entered into the edit |
|
137 @type str |
|
138 """ |
|
139 self.__updateUI() |
|
140 |
|
141 @pyqtSlot(str) |
|
142 def on_destinationEdit_textChanged(self, text): |
|
143 """ |
|
144 Private slot to react to changes of the destination module. |
|
145 |
|
146 @param text text entered into the edit |
|
147 @type str |
|
148 """ |
|
149 self.__updateUI() |
|
150 |
|
151 def __updateUI(self): |
|
152 """ |
|
153 Private method to perform various UI updates. |
|
154 """ |
|
155 if self.__moveType == "move_method": |
|
156 enable = self.attributeEdit.text() != "" and \ |
|
157 self.methodEdit.text() != "" |
|
158 elif self.__moveType in ["move_global_method", "move_module"]: |
|
159 enable = self.destinationEdit.text() != "" |
|
160 else: |
|
161 enable = False |
|
162 |
|
163 self.__okButton.setEnabled(enable) |
|
164 self.__previewButton.setEnabled(enable) |
|
165 |
|
166 @pyqtSlot() |
|
167 def on_selectButton_clicked(self): |
|
168 """ |
|
169 Private slot called to select the destination module via a file |
|
170 selection dialog. |
|
171 """ |
|
172 dest = self.destinationEdit.text() |
|
173 if not dest: |
|
174 dest = self.__project.getProjectPath() |
|
175 elif not os.path.isabs(dest): |
|
176 dest = os.path.join(self.__project.getProjectPath(), dest) |
|
177 if self.__moveType == "move_global_method": |
|
178 destination = E5FileDialog.getOpenFileName( |
|
179 self, |
|
180 self.windowTitle(), |
|
181 dest, |
|
182 self.tr("Python Files (*.py *.py2 *.py3);;All Files (*)")) |
|
183 else: |
|
184 # move_module |
|
185 destination = E5FileDialog.getExistingDirectory( |
|
186 self, |
|
187 self.windowTitle(), |
|
188 dest) |
|
189 |
|
190 if destination: |
|
191 destination = Utilities.toNativeSeparators(destination) |
|
192 if not self.__project.startswithProjectPath(destination): |
|
193 if self.__moveType == "move_global_method": |
|
194 errorMessage = self.tr("""The selected module must be """ |
|
195 """inside the project.""") |
|
196 else: |
|
197 # move_module |
|
198 errorMessage = self.tr("""The selected directory must""" |
|
199 """ be inside the project.""") |
|
200 E5MessageBox.critical( |
|
201 self, |
|
202 self.windowTitle(), |
|
203 errorMessage) |
|
204 return |
|
205 |
|
206 if self.__moveType == "move_global_method": |
|
207 if not os.path.exists(destination): |
|
208 E5MessageBox.critical( |
|
209 self, |
|
210 self.windowTitle(), |
|
211 self.tr("""The selected module <b>{0}</b> does""" |
|
212 """ not exist.""").format(destination)) |
|
213 return |
|
214 else: |
|
215 # move_module |
|
216 if not os.path.exists( |
|
217 os.path.join(destination, "__init__.py")): |
|
218 E5MessageBox.critical( |
|
219 self, |
|
220 self.windowTitle(), |
|
221 self.tr("""The selected directory <b>{0}</b> is""" |
|
222 """ not a package.""").format(destination)) |
|
223 return |
|
224 |
|
225 destination = self.__project.getRelativePath(destination) |
|
226 self.destinationEdit.setText(destination) |
|
227 |
|
228 def __checkDestination(self): |
|
229 """ |
|
230 Private method to check the destination entered. |
|
231 |
|
232 @return flag indicating a valid entry (boolean) |
|
233 """ |
|
234 destination = os.path.join( |
|
235 self.__project.getProjectPath(), |
|
236 self.destinationEdit.text()) |
|
237 if self.__moveType == "move_global_method": |
|
238 if not os.path.exists(destination): |
|
239 E5MessageBox.critical( |
|
240 self, |
|
241 self.windowTitle(), |
|
242 self.tr("""The selected module <b>{0}</b> does""" |
|
243 """ not exist.""").format(destination)) |
|
244 return False |
|
245 else: |
|
246 # move_module |
|
247 if not os.path.exists(os.path.join(destination, "__init__.py")): |
|
248 E5MessageBox.critical( |
|
249 self, |
|
250 self.windowTitle(), |
|
251 self.tr("""The selected directory <b>{0}</b> is""" |
|
252 """ not a package.""").format(destination)) |
|
253 return False |
|
254 |
|
255 return True |
|
256 |
|
257 @pyqtSlot(QAbstractButton) |
|
258 def on_buttonBox_clicked(self, button): |
|
259 """ |
|
260 Private slot to act on the button pressed. |
|
261 |
|
262 @param button reference to the button pressed |
|
263 @type QAbstractButton |
|
264 """ |
|
265 if self.__moveType == "move_method" or ( |
|
266 self.__moveType in ["move_global_method", "move_module"] and |
|
267 self.__checkDestination()): |
|
268 if button == self.__previewButton: |
|
269 self.requestPreview() |
|
270 elif button == self.__okButton: |
|
271 self.applyChanges() |
|
272 |
|
273 def _calculateChanges(self): |
|
274 """ |
|
275 Protected method to initiate the calculation of the changes. |
|
276 """ |
|
277 newName = self.methodEdit.text() |
|
278 if not newName: |
|
279 newName = None |
|
280 |
|
281 self._refactoring.sendJson("CalculateMoveChanges", { |
|
282 "ChangeGroup": self._changeGroupName, |
|
283 "Title": self.windowTitle(), |
|
284 "FileName": self.__filename, |
|
285 "Offset": self.__offset, |
|
286 "Kind": self.__moveType, |
|
287 "NewName": newName, |
|
288 "Attribute": self.attributeEdit.text(), |
|
289 "DestinationModule": self.destinationEdit.text(), |
|
290 }) |
|
291 |
|
292 def processChangeData(self, data): |
|
293 """ |
|
294 Public method to process the change data sent by the refactoring |
|
295 client. |
|
296 |
|
297 @param data dictionary containing the change data |
|
298 @type dict |
|
299 """ |
|
300 subcommand = data["Subcommand"] |
|
301 if subcommand == "MoveType": |
|
302 self.__processMoveType(data) |
|
303 else: |
|
304 # pass on to base class |
|
305 RefactoringDialogBase.processChangeData(self, data) |