8 """ |
8 """ |
9 |
9 |
10 import re |
10 import re |
11 |
11 |
12 from PyQt6.QtCore import pyqtSlot, Qt, QCoreApplication |
12 from PyQt6.QtCore import pyqtSlot, Qt, QCoreApplication |
13 from PyQt6.QtWidgets import ( |
13 from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QHeaderView, QTreeWidgetItem |
14 QDialog, QDialogButtonBox, QHeaderView, QTreeWidgetItem |
|
15 ) |
|
16 |
14 |
17 from .Ui_HgGpgSignaturesDialog import Ui_HgGpgSignaturesDialog |
15 from .Ui_HgGpgSignaturesDialog import Ui_HgGpgSignaturesDialog |
18 |
16 |
19 |
17 |
20 class HgGpgSignaturesDialog(QDialog, Ui_HgGpgSignaturesDialog): |
18 class HgGpgSignaturesDialog(QDialog, Ui_HgGpgSignaturesDialog): |
21 """ |
19 """ |
22 Class implementing a dialog showing signed changesets. |
20 Class implementing a dialog showing signed changesets. |
23 """ |
21 """ |
|
22 |
24 def __init__(self, vcs, parent=None): |
23 def __init__(self, vcs, parent=None): |
25 """ |
24 """ |
26 Constructor |
25 Constructor |
27 |
26 |
28 @param vcs reference to the vcs object |
27 @param vcs reference to the vcs object |
29 @param parent reference to the parent widget (QWidget) |
28 @param parent reference to the parent widget (QWidget) |
30 """ |
29 """ |
31 super().__init__(parent) |
30 super().__init__(parent) |
32 self.setupUi(self) |
31 self.setupUi(self) |
33 self.setWindowFlags(Qt.WindowType.Window) |
32 self.setWindowFlags(Qt.WindowType.Window) |
34 |
33 |
35 self.buttonBox.button( |
34 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) |
36 QDialogButtonBox.StandardButton.Close).setEnabled(False) |
35 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
37 self.buttonBox.button( |
36 |
38 QDialogButtonBox.StandardButton.Cancel).setDefault(True) |
|
39 |
|
40 self.vcs = vcs |
37 self.vcs = vcs |
41 self.__hgClient = vcs.getClient() |
38 self.__hgClient = vcs.getClient() |
42 |
39 |
43 self.show() |
40 self.show() |
44 QCoreApplication.processEvents() |
41 QCoreApplication.processEvents() |
45 |
42 |
46 def closeEvent(self, e): |
43 def closeEvent(self, e): |
47 """ |
44 """ |
48 Protected slot implementing a close event handler. |
45 Protected slot implementing a close event handler. |
49 |
46 |
50 @param e close event (QCloseEvent) |
47 @param e close event (QCloseEvent) |
51 """ |
48 """ |
52 if self.__hgClient.isExecuting(): |
49 if self.__hgClient.isExecuting(): |
53 self.__hgClient.cancel() |
50 self.__hgClient.cancel() |
54 |
51 |
55 e.accept() |
52 e.accept() |
56 |
53 |
57 def start(self): |
54 def start(self): |
58 """ |
55 """ |
59 Public slot to start the list command. |
56 Public slot to start the list command. |
60 """ |
57 """ |
61 self.errorGroup.hide() |
58 self.errorGroup.hide() |
62 |
59 |
63 self.intercept = False |
60 self.intercept = False |
64 self.activateWindow() |
61 self.activateWindow() |
65 |
62 |
66 args = self.vcs.initCommand("sigs") |
63 args = self.vcs.initCommand("sigs") |
67 |
64 |
68 out, err = self.__hgClient.runcommand(args) |
65 out, err = self.__hgClient.runcommand(args) |
69 if err: |
66 if err: |
70 self.__showError(err) |
67 self.__showError(err) |
71 if out: |
68 if out: |
72 for line in out.splitlines(): |
69 for line in out.splitlines(): |
73 self.__processOutputLine(line) |
70 self.__processOutputLine(line) |
74 if self.__hgClient.wasCanceled(): |
71 if self.__hgClient.wasCanceled(): |
75 break |
72 break |
76 self.__finish() |
73 self.__finish() |
77 |
74 |
78 def __finish(self): |
75 def __finish(self): |
79 """ |
76 """ |
80 Private slot called when the process finished or the user pressed |
77 Private slot called when the process finished or the user pressed |
81 the button. |
78 the button. |
82 """ |
79 """ |
83 self.buttonBox.button( |
80 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) |
84 QDialogButtonBox.StandardButton.Close).setEnabled(True) |
81 self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
85 self.buttonBox.button( |
82 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) |
86 QDialogButtonBox.StandardButton.Cancel).setEnabled(False) |
83 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setFocus( |
87 self.buttonBox.button( |
84 Qt.FocusReason.OtherFocusReason |
88 QDialogButtonBox.StandardButton.Close).setDefault(True) |
85 ) |
89 self.buttonBox.button( |
86 |
90 QDialogButtonBox.StandardButton.Close).setFocus( |
|
91 Qt.FocusReason.OtherFocusReason) |
|
92 |
|
93 if self.signaturesList.topLevelItemCount() == 0: |
87 if self.signaturesList.topLevelItemCount() == 0: |
94 # no patches present |
88 # no patches present |
95 self.__generateItem("", "", self.tr("no signatures found")) |
89 self.__generateItem("", "", self.tr("no signatures found")) |
96 self.__resizeColumns() |
90 self.__resizeColumns() |
97 self.__resort() |
91 self.__resort() |
98 |
92 |
99 def on_buttonBox_clicked(self, button): |
93 def on_buttonBox_clicked(self, button): |
100 """ |
94 """ |
101 Private slot called by a button of the button box clicked. |
95 Private slot called by a button of the button box clicked. |
102 |
96 |
103 @param button button that was clicked (QAbstractButton) |
97 @param button button that was clicked (QAbstractButton) |
104 """ |
98 """ |
105 if button == self.buttonBox.button( |
99 if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): |
106 QDialogButtonBox.StandardButton.Close |
|
107 ): |
|
108 self.close() |
100 self.close() |
109 elif button == self.buttonBox.button( |
101 elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): |
110 QDialogButtonBox.StandardButton.Cancel |
|
111 ): |
|
112 self.__hgClient.cancel() |
102 self.__hgClient.cancel() |
113 |
103 |
114 def __resort(self): |
104 def __resort(self): |
115 """ |
105 """ |
116 Private method to resort the tree. |
106 Private method to resort the tree. |
117 """ |
107 """ |
118 self.signaturesList.sortItems( |
108 self.signaturesList.sortItems( |
119 self.signaturesList.sortColumn(), |
109 self.signaturesList.sortColumn(), |
120 self.signaturesList.header().sortIndicatorOrder()) |
110 self.signaturesList.header().sortIndicatorOrder(), |
121 |
111 ) |
|
112 |
122 def __resizeColumns(self): |
113 def __resizeColumns(self): |
123 """ |
114 """ |
124 Private method to resize the list columns. |
115 Private method to resize the list columns. |
125 """ |
116 """ |
126 self.signaturesList.header().resizeSections( |
117 self.signaturesList.header().resizeSections( |
127 QHeaderView.ResizeMode.ResizeToContents) |
118 QHeaderView.ResizeMode.ResizeToContents |
|
119 ) |
128 self.signaturesList.header().setStretchLastSection(True) |
120 self.signaturesList.header().setStretchLastSection(True) |
129 |
121 |
130 def __generateItem(self, revision, changeset, signature): |
122 def __generateItem(self, revision, changeset, signature): |
131 """ |
123 """ |
132 Private method to generate a patch item in the list of patches. |
124 Private method to generate a patch item in the list of patches. |
133 |
125 |
134 @param revision revision number (string) |
126 @param revision revision number (string) |
135 @param changeset changeset of the bookmark (string) |
127 @param changeset changeset of the bookmark (string) |
136 @param signature signature of the changeset (string) |
128 @param signature signature of the changeset (string) |
137 """ |
129 """ |
138 if revision == "" and changeset == "": |
130 if revision == "" and changeset == "": |
139 QTreeWidgetItem(self.signaturesList, [signature]) |
131 QTreeWidgetItem(self.signaturesList, [signature]) |
140 else: |
132 else: |
141 revString = "{0:>7}:{1}".format(revision, changeset) |
133 revString = "{0:>7}:{1}".format(revision, changeset) |
142 topItems = self.signaturesList.findItems( |
134 topItems = self.signaturesList.findItems( |
143 revString, Qt.MatchFlag.MatchExactly) |
135 revString, Qt.MatchFlag.MatchExactly |
|
136 ) |
144 if len(topItems) == 0: |
137 if len(topItems) == 0: |
145 # first signature for this changeset |
138 # first signature for this changeset |
146 topItm = QTreeWidgetItem(self.signaturesList, [ |
139 topItm = QTreeWidgetItem( |
147 "{0:>7}:{1}".format(revision, changeset)]) |
140 self.signaturesList, ["{0:>7}:{1}".format(revision, changeset)] |
|
141 ) |
148 topItm.setExpanded(True) |
142 topItm.setExpanded(True) |
149 font = topItm.font(0) |
143 font = topItm.font(0) |
150 font.setBold(True) |
144 font.setBold(True) |
151 topItm.setFont(0, font) |
145 topItm.setFont(0, font) |
152 else: |
146 else: |
153 topItm = topItems[0] |
147 topItm = topItems[0] |
154 QTreeWidgetItem(topItm, [signature]) |
148 QTreeWidgetItem(topItm, [signature]) |
155 |
149 |
156 def __processOutputLine(self, line): |
150 def __processOutputLine(self, line): |
157 """ |
151 """ |
158 Private method to process the lines of output. |
152 Private method to process the lines of output. |
159 |
153 |
160 @param line output line to be processed (string) |
154 @param line output line to be processed (string) |
161 """ |
155 """ |
162 li = line.split() |
156 li = line.split() |
163 if li[-1][0] in "1234567890": |
157 if li[-1][0] in "1234567890": |
164 # last element is a rev:changeset |
158 # last element is a rev:changeset |
165 rev, changeset = li[-1].split(":", 1) |
159 rev, changeset = li[-1].split(":", 1) |
166 del li[-1] |
160 del li[-1] |
167 signature = " ".join(li) |
161 signature = " ".join(li) |
168 self.__generateItem(rev, changeset, signature) |
162 self.__generateItem(rev, changeset, signature) |
169 |
163 |
170 def __showError(self, out): |
164 def __showError(self, out): |
171 """ |
165 """ |
172 Private slot to show some error. |
166 Private slot to show some error. |
173 |
167 |
174 @param out error to be shown (string) |
168 @param out error to be shown (string) |
175 """ |
169 """ |
176 self.errorGroup.show() |
170 self.errorGroup.show() |
177 self.errors.insertPlainText(out) |
171 self.errors.insertPlainText(out) |
178 self.errors.ensureCursorVisible() |
172 self.errors.ensureCursorVisible() |
179 |
173 |
180 @pyqtSlot() |
174 @pyqtSlot() |
181 def on_signaturesList_itemSelectionChanged(self): |
175 def on_signaturesList_itemSelectionChanged(self): |
182 """ |
176 """ |
183 Private slot handling changes of the selection. |
177 Private slot handling changes of the selection. |
184 """ |
178 """ |
185 selectedItems = self.signaturesList.selectedItems() |
179 selectedItems = self.signaturesList.selectedItems() |
186 if ( |
180 if ( |
187 len(selectedItems) == 1 and |
181 len(selectedItems) == 1 |
188 self.signaturesList.indexOfTopLevelItem(selectedItems[0]) != -1 |
182 and self.signaturesList.indexOfTopLevelItem(selectedItems[0]) != -1 |
189 ): |
183 ): |
190 self.verifyButton.setEnabled(True) |
184 self.verifyButton.setEnabled(True) |
191 else: |
185 else: |
192 self.verifyButton.setEnabled(False) |
186 self.verifyButton.setEnabled(False) |
193 |
187 |
194 @pyqtSlot() |
188 @pyqtSlot() |
195 def on_verifyButton_clicked(self): |
189 def on_verifyButton_clicked(self): |
196 """ |
190 """ |
197 Private slot to verify the signatures of the selected revision. |
191 Private slot to verify the signatures of the selected revision. |
198 """ |
192 """ |
199 rev = ( |
193 rev = self.signaturesList.selectedItems()[0].text(0).split(":")[0].strip() |
200 self.signaturesList.selectedItems()[0].text(0) |
|
201 .split(":")[0].strip() |
|
202 ) |
|
203 self.vcs.getExtensionObject("gpg").hgGpgVerifySignatures(rev) |
194 self.vcs.getExtensionObject("gpg").hgGpgVerifySignatures(rev) |
204 |
195 |
205 @pyqtSlot(int) |
196 @pyqtSlot(int) |
206 def on_categoryCombo_activated(self, index): |
197 def on_categoryCombo_activated(self, index): |
207 """ |
198 """ |
208 Private slot called, when a new filter category is selected. |
199 Private slot called, when a new filter category is selected. |
209 |
200 |
210 @param index index of the selected entry |
201 @param index index of the selected entry |
211 @type int |
202 @type int |
212 """ |
203 """ |
213 self.__filterSignatures() |
204 self.__filterSignatures() |
214 |
205 |
215 @pyqtSlot(str) |
206 @pyqtSlot(str) |
216 def on_rxEdit_textChanged(self, txt): |
207 def on_rxEdit_textChanged(self, txt): |
217 """ |
208 """ |
218 Private slot called, when a filter expression is entered. |
209 Private slot called, when a filter expression is entered. |
219 |
210 |
220 @param txt filter expression (string) |
211 @param txt filter expression (string) |
221 """ |
212 """ |
222 self.__filterSignatures() |
213 self.__filterSignatures() |
223 |
214 |
224 def __filterSignatures(self): |
215 def __filterSignatures(self): |
225 """ |
216 """ |
226 Private method to filter the log entries. |
217 Private method to filter the log entries. |
227 """ |
218 """ |
228 searchRxText = self.rxEdit.text() |
219 searchRxText = self.rxEdit.text() |
229 filterTop = self.categoryCombo.currentText() == self.tr("Revision") |
220 filterTop = self.categoryCombo.currentText() == self.tr("Revision") |
230 searchRx = ( |
221 searchRx = ( |
231 re.compile( |
222 re.compile(r"^\s*{0}".format(searchRxText[1:]), re.IGNORECASE) |
232 r"^\s*{0}".format(searchRxText[1:]), re.IGNORECASE) |
223 if filterTop and searchRxText.startswith("^") |
233 if filterTop and searchRxText.startswith("^") else |
224 else re.compile(searchRxText, re.IGNORECASE) |
234 re.compile(searchRxText, re.IGNORECASE) |
|
235 ) |
225 ) |
236 for topIndex in range(self.signaturesList.topLevelItemCount()): |
226 for topIndex in range(self.signaturesList.topLevelItemCount()): |
237 topLevelItem = self.signaturesList.topLevelItem(topIndex) |
227 topLevelItem = self.signaturesList.topLevelItem(topIndex) |
238 if filterTop: |
228 if filterTop: |
239 topLevelItem.setHidden( |
229 topLevelItem.setHidden(searchRx.search(topLevelItem.text(0)) is None) |
240 searchRx.search(topLevelItem.text(0)) is None) |
|
241 else: |
230 else: |
242 visibleChildren = topLevelItem.childCount() |
231 visibleChildren = topLevelItem.childCount() |
243 for childIndex in range(topLevelItem.childCount()): |
232 for childIndex in range(topLevelItem.childCount()): |
244 childItem = topLevelItem.child(childIndex) |
233 childItem = topLevelItem.child(childIndex) |
245 if searchRx.search(childItem.text(0)) is None: |
234 if searchRx.search(childItem.text(0)) is None: |