--- a/src/eric7/Plugins/VcsPlugins/vcsPySvn/SvnLogBrowserDialog.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Plugins/VcsPlugins/vcsPySvn/SvnLogBrowserDialog.py Wed Jul 13 14:55:47 2022 +0200 @@ -14,7 +14,11 @@ from PyQt6.QtCore import Qt, QDate, pyqtSlot, QPoint from PyQt6.QtWidgets import ( - QHeaderView, QWidget, QApplication, QDialogButtonBox, QTreeWidgetItem + QHeaderView, + QWidget, + QApplication, + QDialogButtonBox, + QTreeWidgetItem, ) from EricWidgets import EricMessageBox @@ -34,61 +38,59 @@ """ Class implementing a dialog to browse the log history. """ + def __init__(self, vcs, parent=None): """ Constructor - + @param vcs reference to the vcs object @param parent parent widget (QWidget) """ super().__init__(parent) self.setupUi(self) SvnDialogMixin.__init__(self) - + self.__position = QPoint() - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) - + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) + self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow")) - + self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") - self.filesTree.header().setSortIndicator( - 0, Qt.SortOrder.AscendingOrder) - + self.filesTree.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder) + self.vcs = vcs - + self.__initData() - + self.fromDate.setDisplayFormat("yyyy-MM-dd") self.toDate.setDisplayFormat("yyyy-MM-dd") self.__resetUI() - + self.__messageRole = Qt.ItemDataRole.UserRole self.__changesRole = Qt.ItemDataRole.UserRole + 1 - + self.flags = { - 'A': self.tr('Added'), - 'D': self.tr('Deleted'), - 'M': self.tr('Modified'), - 'R': self.tr('Replaced'), + "A": self.tr("Added"), + "D": self.tr("Deleted"), + "M": self.tr("Modified"), + "R": self.tr("Replaced"), } - + self.__logTreeNormalFont = self.logTree.font() self.__logTreeNormalFont.setBold(False) self.__logTreeBoldFont = self.logTree.font() self.__logTreeBoldFont.setBold(True) - + self.client = self.vcs.getClient() self.client.callback_cancel = self._clientCancelCallback self.client.callback_get_login = self._clientLoginCallback self.client.callback_ssl_server_trust_prompt = ( self._clientSslServerTrustPromptCallback ) - + def __initData(self): """ Private method to (re-)initialize some data. @@ -96,20 +98,20 @@ self.__maxDate = QDate() self.__minDate = QDate() self.__filterLogsEnabled = True - + self.diff = None self.__lastRev = 0 - + def closeEvent(self, e): """ Protected slot implementing a close event handler. - + @param e close event (QCloseEvent) """ self.__position = self.pos() - + e.accept() - + def show(self): """ Public slot to show the dialog. @@ -117,24 +119,23 @@ if not self.__position.isNull(): self.move(self.__position) self.__resetUI() - + super().show() - + def __resetUI(self): """ Private method to reset the user interface. """ self.fromDate.setDate(QDate.currentDate()) self.toDate.setDate(QDate.currentDate()) - self.fieldCombo.setCurrentIndex(self.fieldCombo.findText( - self.tr("Message"))) - self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences( - "LogLimit")) - self.stopCheckBox.setChecked(self.vcs.getPlugin().getPreferences( - "StopLogOnCopy")) - + self.fieldCombo.setCurrentIndex(self.fieldCombo.findText(self.tr("Message"))) + self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences("LogLimit")) + self.stopCheckBox.setChecked( + self.vcs.getPlugin().getPreferences("StopLogOnCopy") + ) + self.logTree.clear() - + self.nextButton.setEnabled(True) self.limitSpinBox.setEnabled(True) @@ -143,55 +144,50 @@ Protected method to reset the internal state of the dialog. """ SvnDialogMixin._reset(self) - + self.cancelled = False - - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setDefault(True) + + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True) QApplication.processEvents() - + def __resizeColumnsLog(self): """ Private method to resize the log tree columns. """ - self.logTree.header().resizeSections( - QHeaderView.ResizeMode.ResizeToContents) + self.logTree.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) self.logTree.header().setStretchLastSection(True) - + def __resortLog(self): """ Private method to resort the log tree. """ self.logTree.sortItems( - self.logTree.sortColumn(), - self.logTree.header().sortIndicatorOrder()) - + self.logTree.sortColumn(), self.logTree.header().sortIndicatorOrder() + ) + def __resizeColumnsFiles(self): """ Private method to resize the changed files tree columns. """ - self.filesTree.header().resizeSections( - QHeaderView.ResizeMode.ResizeToContents) + self.filesTree.header().resizeSections(QHeaderView.ResizeMode.ResizeToContents) self.filesTree.header().setStretchLastSection(True) - + def __resortFiles(self): """ Private method to resort the changed files tree. """ sortColumn = self.filesTree.sortColumn() - self.filesTree.sortItems( - 1, self.filesTree.header().sortIndicatorOrder()) + self.filesTree.sortItems(1, self.filesTree.header().sortIndicatorOrder()) self.filesTree.sortItems( - sortColumn, self.filesTree.header().sortIndicatorOrder()) - + sortColumn, self.filesTree.header().sortIndicatorOrder() + ) + def __generateLogItem(self, author, date, message, revision, changedPaths): """ Private method to generate a log tree entry. - + @param author author info (string) @param date date info (integer) @param message text of the log message (string) @@ -207,25 +203,24 @@ rev = revision.number self.__lastRev = revision.number dt = formatTime(date) if date else "" - + itm = QTreeWidgetItem(self.logTree) itm.setData(0, Qt.ItemDataRole.DisplayRole, rev) itm.setData(1, Qt.ItemDataRole.DisplayRole, author) itm.setData(2, Qt.ItemDataRole.DisplayRole, dt) - itm.setData(3, Qt.ItemDataRole.DisplayRole, - " ".join(message.splitlines())) - + itm.setData(3, Qt.ItemDataRole.DisplayRole, " ".join(message.splitlines())) + changes = [] for changedPath in changedPaths: copyPath = ( "" - if changedPath["copyfrom_path"] is None else - changedPath["copyfrom_path"] + if changedPath["copyfrom_path"] is None + else changedPath["copyfrom_path"] ) copyRev = ( "" - if changedPath["copyfrom_revision"] is None else - "{0:7d}".format(changedPath["copyfrom_revision"].number) + if changedPath["copyfrom_revision"] is None + else "{0:7d}".format(changedPath["copyfrom_revision"].number) ) change = { "action": changedPath["action"], @@ -236,19 +231,19 @@ changes.append(change) itm.setData(0, self.__messageRole, message) itm.setData(0, self.__changesRole, changes) - + itm.setTextAlignment(0, Qt.AlignmentFlag.AlignRight) itm.setTextAlignment(1, Qt.AlignmentFlag.AlignLeft) itm.setTextAlignment(2, Qt.AlignmentFlag.AlignLeft) itm.setTextAlignment(3, Qt.AlignmentFlag.AlignLeft) itm.setTextAlignment(4, Qt.AlignmentFlag.AlignLeft) - + return itm - + def __generateFileItem(self, action, path, copyFrom, copyRev): """ Private method to generate a changed files tree entry. - + @param action indicator for the change action ("A", "D" or "M") @param path path of the file in the repository (string) @param copyFrom path the file was copied from (None, string) @@ -256,33 +251,31 @@ @return reference to the generated item (QTreeWidgetItem) """ itm = QTreeWidgetItem( - self.filesTree, - [self.flags[action], path, copyFrom, copyRev] + self.filesTree, [self.flags[action], path, copyFrom, copyRev] ) - + itm.setTextAlignment(3, Qt.AlignmentFlag.AlignRight) - + return itm - + def __getLogEntries(self, startRev=None): """ Private method to retrieve log entries from the repository. - + @param startRev revision number to start from (integer, string) """ fetchLimit = 10 self._reset() - + limit = self.limitSpinBox.value() if startRev is None: start = pysvn.Revision(pysvn.opt_revision_kind.head) else: try: - start = pysvn.Revision(pysvn.opt_revision_kind.number, - int(startRev)) + start = pysvn.Revision(pysvn.opt_revision_kind.number, int(startRev)) except TypeError: start = pysvn.Revision(pysvn.opt_revision_kind.head) - + with EricOverrideCursor(): cwd = os.getcwd() os.chdir(self.dname) @@ -295,36 +288,36 @@ flimit = min(fetchLimit, limit - fetched) revstart = ( start - if fetched == 0 else - pysvn.Revision(pysvn.opt_revision_kind.number, - nextRev) + if fetched == 0 + else pysvn.Revision(pysvn.opt_revision_kind.number, nextRev) ) allLogs = self.client.log( - self.fname, revision_start=revstart, - discover_changed_paths=True, limit=flimit + 1, - strict_node_history=self.stopCheckBox.isChecked()) - if ( - len(allLogs) <= flimit or - self._clientCancelCallback() - ): + self.fname, + revision_start=revstart, + discover_changed_paths=True, + limit=flimit + 1, + strict_node_history=self.stopCheckBox.isChecked(), + ) + if len(allLogs) <= flimit or self._clientCancelCallback(): logs.extend(allLogs) break else: logs.extend(allLogs[:-1]) nextRev = allLogs[-1]["revision"].number fetched += fetchLimit - + for log in logs: author = log["author"] message = log["message"] self.__generateLogItem( - author, log["date"], message, - log["revision"], log['changed_paths']) + author, + log["date"], + message, + log["revision"], + log["changed_paths"], + ) dt = dateFromTime_t(log["date"]) - if ( - not self.__maxDate.isValid() and - not self.__minDate.isValid() - ): + if not self.__maxDate.isValid() and not self.__minDate.isValid(): self.__maxDate = dt self.__minDate = dt else: @@ -343,7 +336,7 @@ self.toDate.setMaximumDate(self.__maxDate) self.toDate.setDate(self.__maxDate) self.__filterLogsEnabled = True - + self.__resizeColumnsLog() self.__resortLog() self.__filterLogs() @@ -351,125 +344,118 @@ self.__showError(e.args[0]) os.chdir(cwd) self.__finish() - + def start(self, fn, isFile=False): """ Public slot to start the svn log command. - + @param fn filename to show the log for (string) @param isFile flag indicating log for a file is to be shown (boolean) """ self.sbsCheckBox.setEnabled(isFile) self.sbsCheckBox.setVisible(isFile) - + self.__initData() - + self.filename = fn self.dname, self.fname = self.vcs.splitPath(fn) - + self.activateWindow() self.raise_() - + self.logTree.clear() self.__getLogEntries() self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) - + def __finish(self): """ Private slot called when the user pressed the button. """ - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setEnabled(True) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel).setEnabled(False) - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close).setDefault(True) - + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) + self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False) + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) + self._cancel() - + def __diffRevisions(self, rev1, rev2, peg_rev): """ Private method to do a diff of two revisions. - + @param rev1 first revision number (integer) @param rev2 second revision number (integer) @param peg_rev revision number to use as a reference (integer) """ if self.sbsCheckBox.isEnabled() and self.sbsCheckBox.isChecked(): - self.vcs.vcsSbsDiff(self.filename, - revisions=(str(rev1), str(rev2))) + self.vcs.vcsSbsDiff(self.filename, revisions=(str(rev1), str(rev2))) else: if self.diff is None: from .SvnDiffDialog import SvnDiffDialog + self.diff = SvnDiffDialog(self.vcs) self.diff.show() self.diff.raise_() QApplication.processEvents() self.diff.start(self.filename, [rev1, rev2], pegRev=peg_rev) - + def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. - + @param button button that was clicked (QAbstractButton) """ - if button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Close - ): + if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close): self.close() - elif button == self.buttonBox.button( - QDialogButtonBox.StandardButton.Cancel - ): + elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel): self.cancelled = True self.__finish() - + @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) def on_logTree_currentItemChanged(self, current, previous): """ Private slot called, when the current item of the log tree changes. - + @param current reference to the new current item (QTreeWidgetItem) @param previous reference to the old current item (QTreeWidgetItem) """ if current is not None: self.messageEdit.setPlainText(current.data(0, self.__messageRole)) - + self.filesTree.clear() changes = current.data(0, self.__changesRole) if len(changes) > 0: for change in changes: self.__generateFileItem( - change["action"], change["path"], - change["copyfrom_path"], change["copyfrom_revision"]) + change["action"], + change["path"], + change["copyfrom_path"], + change["copyfrom_revision"], + ) self.__resizeColumnsFiles() self.__resortFiles() - + self.diffPreviousButton.setEnabled( - current != self.logTree.topLevelItem( - self.logTree.topLevelItemCount() - 1)) - + current != self.logTree.topLevelItem(self.logTree.topLevelItemCount() - 1) + ) + # Highlight the current entry using a bold font for col in range(self.logTree.columnCount()): current and current.setFont(col, self.__logTreeBoldFont) previous and previous.setFont(col, self.__logTreeNormalFont) - + # set the state of the up and down buttons self.upButton.setEnabled( - current is not None and - self.logTree.indexOfTopLevelItem(current) > 0) - self.downButton.setEnabled( - current is not None and - int(current.text(0)) > 1) - + current is not None and self.logTree.indexOfTopLevelItem(current) > 0 + ) + self.downButton.setEnabled(current is not None and int(current.text(0)) > 1) + @pyqtSlot() def on_logTree_itemSelectionChanged(self): """ Private slot called, when the selection has changed. """ - self.diffRevisionsButton.setEnabled( - len(self.logTree.selectedItems()) == 2) - + self.diffRevisionsButton.setEnabled(len(self.logTree.selectedItems()) == 2) + @pyqtSlot() def on_nextButton_clicked(self): """ @@ -477,7 +463,7 @@ """ if self.__lastRev > 1: self.__getLogEntries(self.__lastRev - 1) - + @pyqtSlot() def on_diffPreviousButton_clicked(self): """ @@ -488,22 +474,21 @@ self.diffPreviousButton.setEnabled(False) return peg_rev = int(itm.text(0)) - + itm = self.logTree.currentItem() if itm is None: self.diffPreviousButton.setEnabled(False) return rev2 = int(itm.text(0)) - - itm = self.logTree.topLevelItem( - self.logTree.indexOfTopLevelItem(itm) + 1) + + itm = self.logTree.topLevelItem(self.logTree.indexOfTopLevelItem(itm) + 1) if itm is None: self.diffPreviousButton.setEnabled(False) return rev1 = int(itm.text(0)) - + self.__diffRevisions(rev1, rev2, peg_rev) - + @pyqtSlot() def on_diffRevisionsButton_clicked(self): """ @@ -513,66 +498,63 @@ if len(items) != 2: self.diffRevisionsButton.setEnabled(False) return - + rev2 = int(items[0].text(0)) rev1 = int(items[1].text(0)) - + itm = self.logTree.topLevelItem(0) if itm is None: self.diffPreviousButton.setEnabled(False) return peg_rev = int(itm.text(0)) - + self.__diffRevisions(min(rev1, rev2), max(rev1, rev2), peg_rev) - + def __showError(self, msg): """ Private slot to show an error message. - + @param msg error message to show (string) """ - EricMessageBox.critical( - self, - self.tr("Subversion Error"), - msg) - + EricMessageBox.critical(self, self.tr("Subversion Error"), msg) + @pyqtSlot(QDate) def on_fromDate_dateChanged(self, date): """ Private slot called, when the from date changes. - + @param date new date (QDate) """ self.__filterLogs() - + @pyqtSlot(QDate) def on_toDate_dateChanged(self, date): """ Private slot called, when the from date changes. - + @param date new date (QDate) """ self.__filterLogs() - + @pyqtSlot(int) def on_fieldCombo_activated(self, index): """ Private slot called, when a new filter field is selected. - + @param index index of the selected entry @type int """ self.__filterLogs() - + @pyqtSlot(str) def on_rxEdit_textChanged(self, txt): """ Private slot called, when a filter expression is entered. - + @param txt filter expression (string) """ self.__filterLogs() - + def __filterLogs(self): """ Private method to filter the log entries. @@ -588,21 +570,20 @@ fieldIndex = 0 txt = self.rxEdit.text() if txt.startswith("^"): - searchRx = re.compile( - r"^\s*{0}".format(txt[1:]), re.IGNORECASE) + searchRx = re.compile(r"^\s*{0}".format(txt[1:]), re.IGNORECASE) else: searchRx = re.compile(txt, re.IGNORECASE) else: fieldIndex = 3 searchRx = re.compile(self.rxEdit.text(), re.IGNORECASE) - + currentItem = self.logTree.currentItem() for topIndex in range(self.logTree.topLevelItemCount()): topItem = self.logTree.topLevelItem(topIndex) if ( - topItem.text(2) <= to_ and - topItem.text(2) >= from_ and - searchRx.search(topItem.text(fieldIndex)) is not None + topItem.text(2) <= to_ + and topItem.text(2) >= from_ + and searchRx.search(topItem.text(fieldIndex)) is not None ): topItem.setHidden(False) if topItem is currentItem: @@ -612,19 +593,20 @@ if topItem is currentItem: self.messageEdit.clear() self.filesTree.clear() - + @pyqtSlot(bool) def on_stopCheckBox_clicked(self, checked): """ Private slot called, when the stop on copy/move checkbox is clicked. - + @param checked flag indicating the check box state (boolean) """ - self.vcs.getPlugin().setPreferences("StopLogOnCopy", - int(self.stopCheckBox.isChecked())) + self.vcs.getPlugin().setPreferences( + "StopLogOnCopy", int(self.stopCheckBox.isChecked()) + ) self.nextButton.setEnabled(True) self.limitSpinBox.setEnabled(True) - + @pyqtSlot() def on_upButton_clicked(self): """ @@ -633,7 +615,7 @@ itm = self.logTree.itemAbove(self.logTree.currentItem()) if itm: self.logTree.setCurrentItem(itm) - + @pyqtSlot() def on_downButton_clicked(self): """