eric6/Plugins/VcsPlugins/vcsGit/GitRemoteRepositoriesDialog.py

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

eric ide

mercurial