src/eric7/Plugins/VcsPlugins/vcsSubversion/SvnChangeListsDialog.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) 2012 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a dialog to browse the change lists.
8 """
9
10 import re
11 import os
12
13 from PyQt6.QtCore import pyqtSlot, Qt, QProcess, QTimer
14 from PyQt6.QtWidgets import (
15 QDialog, QDialogButtonBox, QListWidgetItem, QLineEdit
16 )
17
18 from EricWidgets import EricMessageBox
19
20 from .Ui_SvnChangeListsDialog import Ui_SvnChangeListsDialog
21
22 import Preferences
23 from Globals import strToQByteArray
24
25
26 class SvnChangeListsDialog(QDialog, Ui_SvnChangeListsDialog):
27 """
28 Class implementing a dialog to browse the change lists.
29 """
30 def __init__(self, vcs, parent=None):
31 """
32 Constructor
33
34 @param vcs reference to the vcs object
35 @param parent parent widget (QWidget)
36 """
37 super().__init__(parent)
38 self.setupUi(self)
39 self.setWindowFlags(Qt.WindowType.Window)
40
41 self.buttonBox.button(
42 QDialogButtonBox.StandardButton.Close).setEnabled(False)
43 self.buttonBox.button(
44 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
45
46 self.vcs = vcs
47
48 self.process = QProcess()
49 self.process.finished.connect(self.__procFinished)
50 self.process.readyReadStandardOutput.connect(self.__readStdout)
51 self.process.readyReadStandardError.connect(self.__readStderr)
52
53 self.rx_status = re.compile(
54 '(.{8,9})\\s+([0-9-]+)\\s+([0-9?]+)\\s+(\\S+)\\s+(.+)\\s*')
55 # flags (8 or 9 anything), revision, changed rev, author, path
56 self.rx_status2 = re.compile('(.{8,9})\\s+(.+)\\s*')
57 # flags (8 or 9 anything), path
58 self.rx_changelist = re.compile('--- \\S+ .([\\w\\s]+).:\\s+')
59 # three dashes, Changelist (translated), quote,
60 # changelist name, quote, :
61
62 @pyqtSlot(QListWidgetItem, QListWidgetItem)
63 def on_changeLists_currentItemChanged(self, current, previous):
64 """
65 Private slot to handle the selection of a new item.
66
67 @param current current item (QListWidgetItem)
68 @param previous previous current item (QListWidgetItem)
69 """
70 self.filesList.clear()
71 if current is not None:
72 changelist = current.text()
73 if changelist in self.changeListsDict:
74 self.filesList.addItems(
75 sorted(self.changeListsDict[changelist]))
76
77 def start(self, path):
78 """
79 Public slot to populate the data.
80
81 @param path directory name to show change lists for (string)
82 """
83 self.changeListsDict = {}
84
85 self.filesLabel.setText(
86 self.tr("Files (relative to {0}):").format(path))
87
88 self.errorGroup.hide()
89 self.intercept = False
90
91 self.path = path
92 self.currentChangelist = ""
93
94 args = []
95 args.append('status')
96 self.vcs.addArguments(args, self.vcs.options['global'])
97 self.vcs.addArguments(args, self.vcs.options['status'])
98 if (
99 '--verbose' not in self.vcs.options['global'] and
100 '--verbose' not in self.vcs.options['status']
101 ):
102 args.append('--verbose')
103 if isinstance(path, list):
104 self.dname, fnames = self.vcs.splitPathList(path)
105 self.vcs.addArguments(args, fnames)
106 else:
107 self.dname, fname = self.vcs.splitPath(path)
108 args.append(fname)
109
110 self.process.setWorkingDirectory(self.dname)
111
112 self.process.start('svn', args)
113 procStarted = self.process.waitForStarted(5000)
114 if not procStarted:
115 self.inputGroup.setEnabled(False)
116 self.inputGroup.hide()
117 EricMessageBox.critical(
118 self,
119 self.tr('Process Generation Error'),
120 self.tr(
121 'The process {0} could not be started. '
122 'Ensure, that it is in the search path.'
123 ).format('svn'))
124 else:
125 self.inputGroup.setEnabled(True)
126 self.inputGroup.show()
127
128 def __finish(self):
129 """
130 Private slot called when the process finished or the user pressed
131 the button.
132 """
133 if (
134 self.process is not None and
135 self.process.state() != QProcess.ProcessState.NotRunning
136 ):
137 self.process.terminate()
138 QTimer.singleShot(2000, self.process.kill)
139 self.process.waitForFinished(3000)
140
141 self.buttonBox.button(
142 QDialogButtonBox.StandardButton.Close).setEnabled(True)
143 self.buttonBox.button(
144 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
145 self.buttonBox.button(
146 QDialogButtonBox.StandardButton.Close).setDefault(True)
147
148 self.inputGroup.setEnabled(False)
149 self.inputGroup.hide()
150
151 if len(self.changeListsDict) == 0:
152 self.changeLists.addItem(self.tr("No changelists found"))
153 self.buttonBox.button(
154 QDialogButtonBox.StandardButton.Close).setFocus(
155 Qt.FocusReason.OtherFocusReason)
156 else:
157 self.changeLists.addItems(sorted(self.changeListsDict.keys()))
158 self.changeLists.setCurrentRow(0)
159 self.changeLists.setFocus(Qt.FocusReason.OtherFocusReason)
160
161 def on_buttonBox_clicked(self, button):
162 """
163 Private slot called by a button of the button box clicked.
164
165 @param button button that was clicked (QAbstractButton)
166 """
167 if button == self.buttonBox.button(
168 QDialogButtonBox.StandardButton.Close
169 ):
170 self.close()
171 elif button == self.buttonBox.button(
172 QDialogButtonBox.StandardButton.Cancel
173 ):
174 self.__finish()
175
176 def __procFinished(self, exitCode, exitStatus):
177 """
178 Private slot connected to the finished signal.
179
180 @param exitCode exit code of the process (integer)
181 @param exitStatus exit status of the process (QProcess.ExitStatus)
182 """
183 self.__finish()
184
185 def __readStdout(self):
186 """
187 Private slot to handle the readyReadStandardOutput signal.
188
189 It reads the output of the process, formats it and inserts it into
190 the contents pane.
191 """
192 if self.process is not None:
193 self.process.setReadChannel(QProcess.ProcessChannel.StandardOutput)
194
195 while self.process.canReadLine():
196 s = str(self.process.readLine(),
197 Preferences.getSystem("IOEncoding"),
198 'replace')
199 match = (
200 self.rx_status.fullmatch(s) or
201 self.rx_status2.fullmatch(s)
202 )
203 if self.currentChangelist != "" and match is not None:
204 if match.re is self.rx_status:
205 file = match.group(5).strip()
206 filename = file.replace(self.path + os.sep, "")
207 if filename not in self.changeListsDict[
208 self.currentChangelist
209 ]:
210 self.changeListsDict[
211 self.currentChangelist].append(filename)
212 else:
213 file = match.group(2).strip()
214 filename = file.replace(self.path + os.sep, "")
215 if filename not in self.changeListsDict[
216 self.currentChangelist
217 ]:
218 self.changeListsDict[
219 self.currentChangelist].append(filename)
220 else:
221 match = self.rx_changelist.fullmatch(s)
222 if match is not None:
223 self.currentChangelist = match.group(1)
224 if self.currentChangelist not in self.changeListsDict:
225 self.changeListsDict[self.currentChangelist] = []
226
227 def __readStderr(self):
228 """
229 Private slot to handle the readyReadStandardError signal.
230
231 It reads the error output of the process and inserts it into the
232 error pane.
233 """
234 if self.process is not None:
235 self.errorGroup.show()
236 s = str(self.process.readAllStandardError(),
237 Preferences.getSystem("IOEncoding"),
238 'replace')
239 self.errors.insertPlainText(s)
240 self.errors.ensureCursorVisible()
241
242 def on_passwordCheckBox_toggled(self, isOn):
243 """
244 Private slot to handle the password checkbox toggled.
245
246 @param isOn flag indicating the status of the check box (boolean)
247 """
248 if isOn:
249 self.input.setEchoMode(QLineEdit.EchoMode.Password)
250 else:
251 self.input.setEchoMode(QLineEdit.EchoMode.Normal)
252
253 @pyqtSlot()
254 def on_sendButton_clicked(self):
255 """
256 Private slot to send the input to the subversion process.
257 """
258 inputTxt = self.input.text()
259 inputTxt += os.linesep
260
261 if self.passwordCheckBox.isChecked():
262 self.errors.insertPlainText(os.linesep)
263 self.errors.ensureCursorVisible()
264 else:
265 self.errors.insertPlainText(inputTxt)
266 self.errors.ensureCursorVisible()
267
268 self.process.write(strToQByteArray(inputTxt))
269
270 self.passwordCheckBox.setChecked(False)
271 self.input.clear()
272
273 def on_input_returnPressed(self):
274 """
275 Private slot to handle the press of the return key in the input field.
276 """
277 self.intercept = True
278 self.on_sendButton_clicked()
279
280 def keyPressEvent(self, evt):
281 """
282 Protected slot to handle a key press event.
283
284 @param evt the key press event (QKeyEvent)
285 """
286 if self.intercept:
287 self.intercept = False
288 evt.accept()
289 return
290 super().keyPressEvent(evt)

eric ide

mercurial