src/eric7/Plugins/VcsPlugins/vcsGit/GitRemoteRepositoriesDialog.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2014 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to show available remote repositories.
8 """
9
10 import os
11
12 from PyQt6.QtCore import pyqtSlot, Qt, QProcess, QTimer
13 from PyQt6.QtWidgets import (
14 QWidget, QHeaderView, QTreeWidgetItem, QDialogButtonBox, QLineEdit
15 )
16
17 from EricWidgets import EricMessageBox
18
19 from .Ui_GitRemoteRepositoriesDialog import Ui_GitRemoteRepositoriesDialog
20
21 import Preferences
22 from Globals import strToQByteArray
23
24
25 class GitRemoteRepositoriesDialog(QWidget, Ui_GitRemoteRepositoriesDialog):
26 """
27 Class implementing a dialog to show available remote repositories.
28 """
29 def __init__(self, vcs, parent=None):
30 """
31 Constructor
32
33 @param vcs reference to the vcs object
34 @param parent parent widget (QWidget)
35 """
36 super().__init__(parent)
37 self.setupUi(self)
38
39 self.vcs = vcs
40 self.process = QProcess()
41 self.process.finished.connect(self.__procFinished)
42 self.process.readyReadStandardOutput.connect(self.__readStdout)
43 self.process.readyReadStandardError.connect(self.__readStderr)
44
45 self.refreshButton = self.buttonBox.addButton(
46 self.tr("Refresh"), QDialogButtonBox.ButtonRole.ActionRole)
47 self.refreshButton.setToolTip(
48 self.tr("Press to refresh the repositories display"))
49 self.refreshButton.setEnabled(False)
50 self.buttonBox.button(
51 QDialogButtonBox.StandardButton.Close).setEnabled(False)
52 self.buttonBox.button(
53 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
54
55 self.__lastColumn = self.repolist.columnCount()
56
57 self.repolist.headerItem().setText(self.__lastColumn, "")
58 self.repolist.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder)
59
60 self.__ioEncoding = Preferences.getSystem("IOEncoding")
61
62 def __resort(self):
63 """
64 Private method to resort the list.
65 """
66 self.repolist.sortItems(
67 self.repolist.sortColumn(),
68 self.repolist.header().sortIndicatorOrder())
69
70 def __resizeColumns(self):
71 """
72 Private method to resize the list columns.
73 """
74 self.repolist.header().resizeSections(
75 QHeaderView.ResizeMode.ResizeToContents)
76 self.repolist.header().setStretchLastSection(True)
77
78 def __generateItem(self, name, url, oper):
79 """
80 Private method to generate a status item in the status list.
81
82 @param name name of the remote repository (string)
83 @param url URL of the remote repository (string)
84 @param oper operation the remote repository may be used for (string)
85 """
86 foundItems = self.repolist.findItems(
87 name, Qt.MatchFlag.MatchExactly, 0)
88 if foundItems:
89 # modify the operations column only
90 foundItems[0].setText(
91 2, "{0} + {1}".format(foundItems[0].text(2), oper))
92 else:
93 QTreeWidgetItem(self.repolist, [name, url, oper])
94
95 def closeEvent(self, e):
96 """
97 Protected slot implementing a close event handler.
98
99 @param e close event (QCloseEvent)
100 """
101 if (
102 self.process is not None and
103 self.process.state() != QProcess.ProcessState.NotRunning
104 ):
105 self.process.terminate()
106 QTimer.singleShot(2000, self.process.kill)
107 self.process.waitForFinished(3000)
108
109 e.accept()
110
111 def start(self, projectDir):
112 """
113 Public slot to start the git remote command.
114
115 @param projectDir name of the project directory (string)
116 """
117 self.repolist.clear()
118
119 self.errorGroup.hide()
120 self.intercept = False
121 self.projectDir = projectDir
122
123 self.__ioEncoding = Preferences.getSystem("IOEncoding")
124
125 self.removeButton.setEnabled(False)
126 self.renameButton.setEnabled(False)
127 self.pruneButton.setEnabled(False)
128 self.showInfoButton.setEnabled(False)
129
130 args = self.vcs.initCommand("remote")
131 args.append('--verbose')
132
133 # find the root of the repo
134 repodir = self.projectDir
135 while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
136 repodir = os.path.dirname(repodir)
137 if os.path.splitdrive(repodir)[1] == os.sep:
138 return
139
140 self.process.kill()
141 self.process.setWorkingDirectory(repodir)
142
143 self.process.start('git', args)
144 procStarted = self.process.waitForStarted(5000)
145 if not procStarted:
146 self.inputGroup.setEnabled(False)
147 self.inputGroup.hide()
148 EricMessageBox.critical(
149 self,
150 self.tr('Process Generation Error'),
151 self.tr(
152 'The process {0} could not be started. '
153 'Ensure, that it is in the search path.'
154 ).format('git'))
155 else:
156 self.buttonBox.button(
157 QDialogButtonBox.StandardButton.Close).setEnabled(False)
158 self.buttonBox.button(
159 QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
160 self.buttonBox.button(
161 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
162
163 self.inputGroup.setEnabled(True)
164 self.inputGroup.show()
165 self.refreshButton.setEnabled(False)
166
167 def __finish(self):
168 """
169 Private slot called when the process finished or the user pressed
170 the button.
171 """
172 if (
173 self.process is not None and
174 self.process.state() != QProcess.ProcessState.NotRunning
175 ):
176 self.process.terminate()
177 QTimer.singleShot(2000, self.process.kill)
178 self.process.waitForFinished(3000)
179
180 self.inputGroup.setEnabled(False)
181 self.inputGroup.hide()
182 self.refreshButton.setEnabled(True)
183
184 self.buttonBox.button(
185 QDialogButtonBox.StandardButton.Close).setEnabled(True)
186 self.buttonBox.button(
187 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
188 self.buttonBox.button(
189 QDialogButtonBox.StandardButton.Close).setDefault(True)
190 self.buttonBox.button(
191 QDialogButtonBox.StandardButton.Close).setFocus(
192 Qt.FocusReason.OtherFocusReason)
193
194 self.__resort()
195 self.__resizeColumns()
196
197 self.__updateButtons()
198
199 def on_buttonBox_clicked(self, button):
200 """
201 Private slot called by a button of the button box clicked.
202
203 @param button button that was clicked (QAbstractButton)
204 """
205 if button == self.buttonBox.button(
206 QDialogButtonBox.StandardButton.Close
207 ):
208 self.close()
209 elif button == self.buttonBox.button(
210 QDialogButtonBox.StandardButton.Cancel
211 ):
212 self.__finish()
213 elif button == self.refreshButton:
214 self.on_refreshButton_clicked()
215
216 def __procFinished(self, exitCode, exitStatus):
217 """
218 Private slot connected to the finished signal.
219
220 @param exitCode exit code of the process (integer)
221 @param exitStatus exit status of the process (QProcess.ExitStatus)
222 """
223 self.__finish()
224
225 def __readStdout(self):
226 """
227 Private slot to handle the readyReadStandardOutput signal.
228
229 It reads the output of the process, formats it and inserts it into
230 the contents pane.
231 """
232 if self.process is not None:
233 self.process.setReadChannel(QProcess.ProcessChannel.StandardOutput)
234
235 while self.process.canReadLine():
236 line = str(self.process.readLine(), self.__ioEncoding,
237 'replace').strip()
238
239 name, line = line.split(None, 1)
240 url, oper = line.rsplit(None, 1)
241 oper = oper[1:-1] # it is enclosed in ()
242 self.__generateItem(name, url, oper)
243
244 def __readStderr(self):
245 """
246 Private slot to handle the readyReadStandardError signal.
247
248 It reads the error output of the process and inserts it into the
249 error pane.
250 """
251 if self.process is not None:
252 s = str(self.process.readAllStandardError(),
253 self.__ioEncoding, 'replace')
254 self.errorGroup.show()
255 self.errors.insertPlainText(s)
256 self.errors.ensureCursorVisible()
257
258 def on_passwordCheckBox_toggled(self, isOn):
259 """
260 Private slot to handle the password checkbox toggled.
261
262 @param isOn flag indicating the status of the check box (boolean)
263 """
264 if isOn:
265 self.input.setEchoMode(QLineEdit.EchoMode.Password)
266 else:
267 self.input.setEchoMode(QLineEdit.EchoMode.Normal)
268
269 @pyqtSlot()
270 def on_sendButton_clicked(self):
271 """
272 Private slot to send the input to the git process.
273 """
274 inputTxt = self.input.text()
275 inputTxt += os.linesep
276
277 if self.passwordCheckBox.isChecked():
278 self.errors.insertPlainText(os.linesep)
279 self.errors.ensureCursorVisible()
280 else:
281 self.errors.insertPlainText(inputTxt)
282 self.errors.ensureCursorVisible()
283
284 self.process.write(strToQByteArray(inputTxt))
285
286 self.passwordCheckBox.setChecked(False)
287 self.input.clear()
288
289 def on_input_returnPressed(self):
290 """
291 Private slot to handle the press of the return key in the input field.
292 """
293 self.intercept = True
294 self.on_sendButton_clicked()
295
296 def keyPressEvent(self, evt):
297 """
298 Protected slot to handle a key press event.
299
300 @param evt the key press event (QKeyEvent)
301 """
302 if self.intercept:
303 self.intercept = False
304 evt.accept()
305 return
306 super().keyPressEvent(evt)
307
308 @pyqtSlot()
309 def on_refreshButton_clicked(self):
310 """
311 Private slot to refresh the status display.
312 """
313 self.start(self.projectDir)
314
315 def __updateButtons(self):
316 """
317 Private method to update the buttons status.
318 """
319 enable = len(self.repolist.selectedItems()) == 1
320
321 self.removeButton.setEnabled(enable)
322 self.pruneButton.setEnabled(enable)
323 self.showInfoButton.setEnabled(enable)
324 self.renameButton.setEnabled(enable)
325 self.changeUrlButton.setEnabled(enable)
326 self.credentialsButton.setEnabled(enable)
327
328 @pyqtSlot()
329 def on_repolist_itemSelectionChanged(self):
330 """
331 Private slot to act upon changes of selected items.
332 """
333 self.__updateButtons()
334
335 @pyqtSlot()
336 def on_addButton_clicked(self):
337 """
338 Private slot to add a remote repository.
339 """
340 self.vcs.gitAddRemote(self.projectDir)
341 self.on_refreshButton_clicked()
342
343 @pyqtSlot()
344 def on_removeButton_clicked(self):
345 """
346 Private slot to remove a remote repository.
347 """
348 remoteName = self.repolist.selectedItems()[0].text(0)
349 self.vcs.gitRemoveRemote(self.projectDir, remoteName)
350 self.on_refreshButton_clicked()
351
352 @pyqtSlot()
353 def on_showInfoButton_clicked(self):
354 """
355 Private slot to show information about a remote repository.
356 """
357 remoteName = self.repolist.selectedItems()[0].text(0)
358 self.vcs.gitShowRemote(self.projectDir, remoteName)
359
360 @pyqtSlot()
361 def on_pruneButton_clicked(self):
362 """
363 Private slot to prune all stale remote-tracking branches.
364 """
365 remoteName = self.repolist.selectedItems()[0].text(0)
366 self.vcs.gitPruneRemote(self.projectDir, remoteName)
367
368 @pyqtSlot()
369 def on_renameButton_clicked(self):
370 """
371 Private slot to rename a remote repository.
372 """
373 remoteName = self.repolist.selectedItems()[0].text(0)
374 self.vcs.gitRenameRemote(self.projectDir, remoteName)
375 self.on_refreshButton_clicked()
376
377 @pyqtSlot()
378 def on_changeUrlButton_clicked(self):
379 """
380 Private slot to change the URL of a remote repository.
381 """
382 repositoryItem = self.repolist.selectedItems()[0]
383 remoteName = repositoryItem.text(0)
384 remoteUrl = repositoryItem.text(1)
385 self.vcs.gitChangeRemoteUrl(self.projectDir, remoteName, remoteUrl)
386 self.on_refreshButton_clicked()
387
388 @pyqtSlot()
389 def on_credentialsButton_clicked(self):
390 """
391 Private slot to change the credentials of a remote repository.
392 """
393 repositoryItem = self.repolist.selectedItems()[0]
394 remoteName = repositoryItem.text(0)
395 remoteUrl = repositoryItem.text(1)
396 self.vcs.gitChangeRemoteCredentials(self.projectDir, remoteName,
397 remoteUrl)
398 self.on_refreshButton_clicked()

eric ide

mercurial