eric6/Plugins/VcsPlugins/vcsGit/GitLogBrowserDialog.py

branch
maintenance
changeset 8176
31965986ecd1
parent 8043
0acf98cd089a
parent 8151
8c1445825e7b
child 8273
698ae46f40a4
equal deleted inserted replaced
8153:e01ae92db699 8176:31965986ecd1
71 """ 71 """
72 super(GitLogBrowserDialog, self).__init__(parent) 72 super(GitLogBrowserDialog, self).__init__(parent)
73 self.setupUi(self) 73 self.setupUi(self)
74 74
75 windowFlags = self.windowFlags() 75 windowFlags = self.windowFlags()
76 windowFlags |= Qt.WindowContextHelpButtonHint 76 windowFlags |= Qt.WindowType.WindowContextHelpButtonHint
77 self.setWindowFlags(windowFlags) 77 self.setWindowFlags(windowFlags)
78 78
79 self.mainSplitter.setSizes([300, 400]) 79 self.mainSplitter.setSizes([300, 400])
80 self.mainSplitter.setStretchFactor(0, 1) 80 self.mainSplitter.setStretchFactor(0, 1)
81 self.mainSplitter.setStretchFactor(1, 2) 81 self.mainSplitter.setStretchFactor(1, 2)
82 self.diffSplitter.setStretchFactor(0, 1) 82 self.diffSplitter.setStretchFactor(0, 1)
83 self.diffSplitter.setStretchFactor(1, 2) 83 self.diffSplitter.setStretchFactor(1, 2)
84 84
85 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) 85 self.buttonBox.button(
86 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) 86 QDialogButtonBox.StandardButton.Close).setEnabled(False)
87 self.buttonBox.button(
88 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
87 89
88 self.filesTree.headerItem().setText(self.filesTree.columnCount(), "") 90 self.filesTree.headerItem().setText(self.filesTree.columnCount(), "")
89 self.filesTree.header().setSortIndicator(1, Qt.AscendingOrder) 91 self.filesTree.header().setSortIndicator(
92 1, Qt.SortOrder.AscendingOrder)
90 93
91 self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow")) 94 self.upButton.setIcon(UI.PixmapCache.getIcon("1uparrow"))
92 self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow")) 95 self.downButton.setIcon(UI.PixmapCache.getIcon("1downarrow"))
93 96
94 self.refreshButton = self.buttonBox.addButton( 97 self.refreshButton = self.buttonBox.addButton(
95 self.tr("&Refresh"), QDialogButtonBox.ActionRole) 98 self.tr("&Refresh"), QDialogButtonBox.ButtonRole.ActionRole)
96 self.refreshButton.setToolTip( 99 self.refreshButton.setToolTip(
97 self.tr("Press to refresh the list of commits")) 100 self.tr("Press to refresh the list of commits"))
98 self.refreshButton.setEnabled(False) 101 self.refreshButton.setEnabled(False)
99 102
100 self.findPrevButton.setIcon(UI.PixmapCache.getIcon("1leftarrow")) 103 self.findPrevButton.setIcon(UI.PixmapCache.getIcon("1leftarrow"))
180 self.fromDate.setDisplayFormat("yyyy-MM-dd") 183 self.fromDate.setDisplayFormat("yyyy-MM-dd")
181 self.toDate.setDisplayFormat("yyyy-MM-dd") 184 self.toDate.setDisplayFormat("yyyy-MM-dd")
182 self.__resetUI() 185 self.__resetUI()
183 186
184 # roles used in the log tree 187 # roles used in the log tree
185 self.__subjectRole = Qt.UserRole 188 self.__subjectRole = Qt.ItemDataRole.UserRole
186 self.__messageRole = Qt.UserRole + 1 189 self.__messageRole = Qt.ItemDataRole.UserRole + 1
187 self.__changesRole = Qt.UserRole + 2 190 self.__changesRole = Qt.ItemDataRole.UserRole + 2
188 self.__edgesRole = Qt.UserRole + 3 191 self.__edgesRole = Qt.ItemDataRole.UserRole + 3
189 self.__parentsRole = Qt.UserRole + 4 192 self.__parentsRole = Qt.ItemDataRole.UserRole + 4
190 self.__branchesRole = Qt.UserRole + 5 193 self.__branchesRole = Qt.ItemDataRole.UserRole + 5
191 self.__authorMailRole = Qt.UserRole + 6 194 self.__authorMailRole = Qt.ItemDataRole.UserRole + 6
192 self.__committerMailRole = Qt.UserRole + 7 195 self.__committerMailRole = Qt.ItemDataRole.UserRole + 7
193 196
194 # roles used in the file tree 197 # roles used in the file tree
195 self.__diffFileLineRole = Qt.UserRole 198 self.__diffFileLineRole = Qt.ItemDataRole.UserRole
196 199
197 self.__process = E5OverrideCursorProcess() 200 self.__process = E5OverrideCursorProcess()
198 self.__process.finished.connect(self.__procFinished) 201 self.__process.finished.connect(self.__procFinished)
199 self.__process.readyReadStandardOutput.connect(self.__readStdout) 202 self.__process.readyReadStandardOutput.connect(self.__readStdout)
200 self.__process.readyReadStandardError.connect(self.__readStderr) 203 self.__process.readyReadStandardError.connect(self.__readStderr)
382 385
383 @param e close event (QCloseEvent) 386 @param e close event (QCloseEvent)
384 """ 387 """
385 if ( 388 if (
386 self.__process is not None and 389 self.__process is not None and
387 self.__process.state() != QProcess.NotRunning 390 self.__process.state() != QProcess.ProcessState.NotRunning
388 ): 391 ):
389 self.__process.terminate() 392 self.__process.terminate()
390 QTimer.singleShot(2000, self.__process.kill) 393 QTimer.singleShot(2000, self.__process.kill)
391 self.__process.waitForFinished(3000) 394 self.__process.waitForFinished(3000)
392 395
451 454
452 def __resizeColumnsLog(self): 455 def __resizeColumnsLog(self):
453 """ 456 """
454 Private method to resize the log tree columns. 457 Private method to resize the log tree columns.
455 """ 458 """
456 self.logTree.header().resizeSections(QHeaderView.ResizeToContents) 459 self.logTree.header().resizeSections(
460 QHeaderView.ResizeMode.ResizeToContents)
457 self.logTree.header().setStretchLastSection(True) 461 self.logTree.header().setStretchLastSection(True)
458 462
459 def __resizeColumnsFiles(self): 463 def __resizeColumnsFiles(self):
460 """ 464 """
461 Private method to resize the changed files tree columns. 465 Private method to resize the changed files tree columns.
462 """ 466 """
463 self.filesTree.header().resizeSections(QHeaderView.ResizeToContents) 467 self.filesTree.header().resizeSections(
468 QHeaderView.ResizeMode.ResizeToContents)
464 self.filesTree.header().setStretchLastSection(True) 469 self.filesTree.header().setStretchLastSection(True)
465 470
466 def __resortFiles(self): 471 def __resortFiles(self):
467 """ 472 """
468 Private method to resort the changed files tree. 473 Private method to resort the changed files tree.
469 """ 474 """
470 self.filesTree.setSortingEnabled(True) 475 self.filesTree.setSortingEnabled(True)
471 self.filesTree.sortItems(1, Qt.AscendingOrder) 476 self.filesTree.sortItems(1, Qt.SortOrder.AscendingOrder)
472 self.filesTree.setSortingEnabled(False) 477 self.filesTree.setSortingEnabled(False)
473 478
474 def __getColor(self, n): 479 def __getColor(self, n):
475 """ 480 """
476 Private method to get the (rotating) name of the color given an index. 481 Private method to get the (rotating) name of the color given an index.
569 dot_y = h // 2 574 dot_y = h // 2
570 575
571 pix = QPixmap(w, h) 576 pix = QPixmap(w, h)
572 pix.fill(QColor(0, 0, 0, 0)) # draw transparent background 577 pix.fill(QColor(0, 0, 0, 0)) # draw transparent background
573 painter = QPainter(pix) 578 painter = QPainter(pix)
574 painter.setRenderHint(QPainter.Antialiasing) 579 painter.setRenderHint(QPainter.RenderHint.Antialiasing)
575 580
576 # draw the revision history lines 581 # draw the revision history lines
577 for y1, y2, lines in ((0, h, bottomedges), 582 for y1, y2, lines in ((0, h, bottomedges),
578 (-h, 0, topedges)): 583 (-h, 0, topedges)):
579 if lines: 584 if lines:
584 x1 = col2x(start, radius) 589 x1 = col2x(start, radius)
585 x2 = col2x(end, radius) 590 x2 = col2x(end, radius)
586 painter.drawLine(x1, dot_y + y1, x2, dot_y + y2) 591 painter.drawLine(x1, dot_y + y1, x2, dot_y + y2)
587 592
588 penradius = 1 593 penradius = 1
589 pencolor = self.logTree.palette().color(QPalette.Text) 594 pencolor = self.logTree.palette().color(QPalette.ColorRole.Text)
590 595
591 dot_y = (h // 2) - radius // 2 596 dot_y = (h // 2) - radius // 2
592 597
593 # draw a dot for the revision 598 # draw a dot for the revision
594 if currentCommit: 599 if currentCommit:
763 str(additions), 768 str(additions),
764 str(deletions), 769 str(deletions),
765 copyfrom, 770 copyfrom,
766 ]) 771 ])
767 772
768 itm.setTextAlignment(2, Qt.AlignRight) 773 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignRight)
769 itm.setTextAlignment(3, Qt.AlignRight) 774 itm.setTextAlignment(3, Qt.AlignmentFlag.AlignRight)
770 775
771 return itm 776 return itm
772 777
773 def __getLogEntries(self, skip=0, noEntries=0): 778 def __getLogEntries(self, skip=0, noEntries=0):
774 """ 779 """
775 Private method to retrieve log entries from the repository. 780 Private method to retrieve log entries from the repository.
776 781
777 @param skip number of log entries to skip (integer) 782 @param skip number of log entries to skip (integer)
778 @param noEntries number of entries to get (0 = default) (int) 783 @param noEntries number of entries to get (0 = default) (int)
779 """ 784 """
780 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) 785 self.buttonBox.button(
781 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True) 786 QDialogButtonBox.StandardButton.Close).setEnabled(False)
782 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) 787 self.buttonBox.button(
788 QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
789 self.buttonBox.button(
790 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
783 QApplication.processEvents() 791 QApplication.processEvents()
784 792
785 self.buf = [] 793 self.buf = []
786 self.cancelled = False 794 self.cancelled = False
787 self.errors.clear() 795 self.errors.clear()
877 Private slot called when the process finished or the user pressed 885 Private slot called when the process finished or the user pressed
878 the button. 886 the button.
879 """ 887 """
880 if ( 888 if (
881 self.__process is not None and 889 self.__process is not None and
882 self.__process.state() != QProcess.NotRunning 890 self.__process.state() != QProcess.ProcessState.NotRunning
883 ): 891 ):
884 self.__process.terminate() 892 self.__process.terminate()
885 QTimer.singleShot(2000, self.__process.kill) 893 QTimer.singleShot(2000, self.__process.kill)
886 self.__process.waitForFinished(3000) 894 self.__process.waitForFinished(3000)
887 895
888 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) 896 self.buttonBox.button(
889 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) 897 QDialogButtonBox.StandardButton.Close).setEnabled(True)
890 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) 898 self.buttonBox.button(
899 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
900 self.buttonBox.button(
901 QDialogButtonBox.StandardButton.Close).setDefault(True)
891 902
892 self.inputGroup.setEnabled(False) 903 self.inputGroup.setEnabled(False)
893 self.inputGroup.hide() 904 self.inputGroup.hide()
894 self.refreshButton.setEnabled(True) 905 self.refreshButton.setEnabled(True)
895 906
909 logEntry["commit"], logEntry["changed_files"], 920 logEntry["commit"], logEntry["changed_files"],
910 logEntry["parents"], logEntry["refnames"], 921 logEntry["parents"], logEntry["refnames"],
911 logEntry["authormail"], logEntry["committermail"] 922 logEntry["authormail"], logEntry["committermail"]
912 ) 923 )
913 for date in [logEntry["authordate"], logEntry["committerdate"]]: 924 for date in [logEntry["authordate"], logEntry["committerdate"]]:
914 dt = QDate.fromString(date, Qt.ISODate) 925 dt = QDate.fromString(date, Qt.DateFormat.ISODate)
915 if ( 926 if (
916 not self.__maxDate.isValid() and 927 not self.__maxDate.isValid() and
917 not self.__minDate.isValid() 928 not self.__minDate.isValid()
918 ): 929 ):
919 self.__maxDate = dt 930 self.__maxDate = dt
1001 self.__resizeColumnsLog() 1012 self.__resizeColumnsLog()
1002 1013
1003 if self.__started: 1014 if self.__started:
1004 if self.__selectedCommitIDs: 1015 if self.__selectedCommitIDs:
1005 self.logTree.setCurrentItem(self.logTree.findItems( 1016 self.logTree.setCurrentItem(self.logTree.findItems(
1006 self.__selectedCommitIDs[0], Qt.MatchExactly, 1017 self.__selectedCommitIDs[0], Qt.MatchFlag.MatchExactly,
1007 self.CommitIdColumn)[0]) 1018 self.CommitIdColumn)[0])
1008 else: 1019 else:
1009 self.logTree.setCurrentItem(self.logTree.topLevelItem(0)) 1020 self.logTree.setCurrentItem(self.logTree.topLevelItem(0))
1010 self.__started = False 1021 self.__started = False
1011 1022
1034 1045
1035 # restore selected items 1046 # restore selected items
1036 if self.__selectedCommitIDs: 1047 if self.__selectedCommitIDs:
1037 for commitID in self.__selectedCommitIDs: 1048 for commitID in self.__selectedCommitIDs:
1038 items = self.logTree.findItems( 1049 items = self.logTree.findItems(
1039 commitID, Qt.MatchExactly, self.CommitIdColumn) 1050 commitID, Qt.MatchFlag.MatchExactly, self.CommitIdColumn)
1040 if items: 1051 if items:
1041 items[0].setSelected(True) 1052 items[0].setSelected(True)
1042 self.__selectedCommitIDs = [] 1053 self.__selectedCommitIDs = []
1043 1054
1044 def __readStdout(self): 1055 def __readStdout(self):
1045 """ 1056 """
1046 Private slot to handle the readyReadStandardOutput signal. 1057 Private slot to handle the readyReadStandardOutput signal.
1047 1058
1048 It reads the output of the process and inserts it into a buffer. 1059 It reads the output of the process and inserts it into a buffer.
1049 """ 1060 """
1050 self.__process.setReadChannel(QProcess.StandardOutput) 1061 self.__process.setReadChannel(QProcess.ProcessChannel.StandardOutput)
1051 1062
1052 while self.__process.canReadLine(): 1063 while self.__process.canReadLine():
1053 line = str(self.__process.readLine(), 1064 line = str(self.__process.readLine(),
1054 Preferences.getSystem("IOEncoding"), 1065 Preferences.getSystem("IOEncoding"),
1055 'replace') 1066 'replace')
1086 """ 1097 """
1087 Private slot called by a button of the button box clicked. 1098 Private slot called by a button of the button box clicked.
1088 1099
1089 @param button button that was clicked (QAbstractButton) 1100 @param button button that was clicked (QAbstractButton)
1090 """ 1101 """
1091 if button == self.buttonBox.button(QDialogButtonBox.Close): 1102 if button == self.buttonBox.button(
1103 QDialogButtonBox.StandardButton.Close
1104 ):
1092 self.close() 1105 self.close()
1093 elif button == self.buttonBox.button(QDialogButtonBox.Cancel): 1106 elif button == self.buttonBox.button(
1107 QDialogButtonBox.StandardButton.Cancel
1108 ):
1094 self.cancelled = True 1109 self.cancelled = True
1095 self.__finish() 1110 self.__finish()
1096 elif button == self.refreshButton: 1111 elif button == self.refreshButton:
1097 self.on_refreshButton_clicked() 1112 self.on_refreshButton_clicked()
1098 1113
1099 @pyqtSlot() 1114 @pyqtSlot()
1100 def on_refreshButton_clicked(self): 1115 def on_refreshButton_clicked(self):
1101 """ 1116 """
1102 Private slot to refresh the log. 1117 Private slot to refresh the log.
1103 """ 1118 """
1104 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) 1119 self.buttonBox.button(
1105 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(True) 1120 QDialogButtonBox.StandardButton.Close).setEnabled(False)
1106 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) 1121 self.buttonBox.button(
1122 QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
1123 self.buttonBox.button(
1124 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
1107 1125
1108 self.refreshButton.setEnabled(False) 1126 self.refreshButton.setEnabled(False)
1109 1127
1110 # save the selected items commit IDs 1128 # save the selected items commit IDs
1111 self.__selectedCommitIDs = [] 1129 self.__selectedCommitIDs = []
1120 Private slot to handle the password checkbox toggled. 1138 Private slot to handle the password checkbox toggled.
1121 1139
1122 @param isOn flag indicating the status of the check box (boolean) 1140 @param isOn flag indicating the status of the check box (boolean)
1123 """ 1141 """
1124 if isOn: 1142 if isOn:
1125 self.input.setEchoMode(QLineEdit.Password) 1143 self.input.setEchoMode(QLineEdit.EchoMode.Password)
1126 else: 1144 else:
1127 self.input.setEchoMode(QLineEdit.Normal) 1145 self.input.setEchoMode(QLineEdit.EchoMode.Normal)
1128 1146
1129 @pyqtSlot() 1147 @pyqtSlot()
1130 def on_sendButton_clicked(self): 1148 def on_sendButton_clicked(self):
1131 """ 1149 """
1132 Private slot to send the input to the git process. 1150 Private slot to send the input to the git process.
1630 @param date new date (QDate) 1648 @param date new date (QDate)
1631 """ 1649 """
1632 if self.__actionMode() == "filter": 1650 if self.__actionMode() == "filter":
1633 self.__filterLogs() 1651 self.__filterLogs()
1634 1652
1635 @pyqtSlot(str) 1653 @pyqtSlot(int)
1636 def on_fieldCombo_activated(self, txt): 1654 def on_fieldCombo_activated(self, index):
1637 """ 1655 """
1638 Private slot called, when a new filter field is selected. 1656 Private slot called, when a new filter field is selected.
1639 1657
1640 @param txt text of the selected field (string) 1658 @param index index of the selected entry
1659 @type int
1641 """ 1660 """
1642 if self.__actionMode() == "filter": 1661 if self.__actionMode() == "filter":
1643 self.__filterLogs() 1662 self.__filterLogs()
1644 1663
1645 @pyqtSlot(str) 1664 @pyqtSlot(str)
2064 @type QUrl 2083 @type QUrl
2065 """ 2084 """
2066 if url.scheme() == "rev": 2085 if url.scheme() == "rev":
2067 # a commit ID was clicked, show the respective item 2086 # a commit ID was clicked, show the respective item
2068 commitId = url.path() 2087 commitId = url.path()
2069 items = self.logTree.findItems(commitId, Qt.MatchStartsWith, 2088 items = self.logTree.findItems(
2070 self.CommitIdColumn) 2089 commitId, Qt.MatchFlag.MatchStartsWith, self.CommitIdColumn)
2071 if items: 2090 if items:
2072 itm = items[0] 2091 itm = items[0]
2073 if itm.isHidden(): 2092 if itm.isHidden():
2074 itm.setHidden(False) 2093 itm.setHidden(False)
2075 self.logTree.setCurrentItem(itm) 2094 self.logTree.setCurrentItem(itm)
2167 else: 2186 else:
2168 for oldFileName, newFileName, lineNumber, _ in fileSeparators: 2187 for oldFileName, newFileName, lineNumber, _ in fileSeparators:
2169 for fileName in (oldFileName, newFileName): 2188 for fileName in (oldFileName, newFileName):
2170 if fileName != "/dev/null": 2189 if fileName != "/dev/null":
2171 items = self.filesTree.findItems( 2190 items = self.filesTree.findItems(
2172 fileName, Qt.MatchExactly, 1) 2191 fileName, Qt.MatchFlag.MatchExactly, 1)
2173 for item in items: 2192 for item in items:
2174 item.setData(0, self.__diffFileLineRole, 2193 item.setData(0, self.__diffFileLineRole,
2175 lineNumber) 2194 lineNumber)
2176 2195
2177 tc = self.diffEdit.textCursor() 2196 tc = self.diffEdit.textCursor()
2178 tc.movePosition(QTextCursor.Start) 2197 tc.movePosition(QTextCursor.MoveOperation.Start)
2179 self.diffEdit.setTextCursor(tc) 2198 self.diffEdit.setTextCursor(tc)
2180 self.diffEdit.ensureCursorVisible() 2199 self.diffEdit.ensureCursorVisible()
2181 2200
2182 def __mergeFileSeparators(self, fileSeparators): 2201 def __mergeFileSeparators(self, fileSeparators):
2183 """ 2202 """
2208 if current: 2227 if current:
2209 para = current.data(0, self.__diffFileLineRole) 2228 para = current.data(0, self.__diffFileLineRole)
2210 if para is not None: 2229 if para is not None:
2211 if para == 0: 2230 if para == 0:
2212 tc = self.diffEdit.textCursor() 2231 tc = self.diffEdit.textCursor()
2213 tc.movePosition(QTextCursor.Start) 2232 tc.movePosition(QTextCursor.MoveOperation.Start)
2214 self.diffEdit.setTextCursor(tc) 2233 self.diffEdit.setTextCursor(tc)
2215 self.diffEdit.ensureCursorVisible() 2234 self.diffEdit.ensureCursorVisible()
2216 elif para == -1: 2235 elif para == -1:
2217 tc = self.diffEdit.textCursor() 2236 tc = self.diffEdit.textCursor()
2218 tc.movePosition(QTextCursor.End) 2237 tc.movePosition(QTextCursor.MoveOperation.End)
2219 self.diffEdit.setTextCursor(tc) 2238 self.diffEdit.setTextCursor(tc)
2220 self.diffEdit.ensureCursorVisible() 2239 self.diffEdit.ensureCursorVisible()
2221 else: 2240 else:
2222 # step 1: move cursor to end 2241 # step 1: move cursor to end
2223 tc = self.diffEdit.textCursor() 2242 tc = self.diffEdit.textCursor()
2224 tc.movePosition(QTextCursor.End) 2243 tc.movePosition(QTextCursor.MoveOperation.End)
2225 self.diffEdit.setTextCursor(tc) 2244 self.diffEdit.setTextCursor(tc)
2226 self.diffEdit.ensureCursorVisible() 2245 self.diffEdit.ensureCursorVisible()
2227 2246
2228 # step 2: move cursor to desired line 2247 # step 2: move cursor to desired line
2229 tc = self.diffEdit.textCursor() 2248 tc = self.diffEdit.textCursor()
2230 delta = tc.blockNumber() - para 2249 delta = tc.blockNumber() - para
2231 tc.movePosition(QTextCursor.PreviousBlock, 2250 tc.movePosition(QTextCursor.MoveOperation.PreviousBlock,
2232 QTextCursor.MoveAnchor, delta) 2251 QTextCursor.MoveMode.MoveAnchor, delta)
2233 self.diffEdit.setTextCursor(tc) 2252 self.diffEdit.setTextCursor(tc)
2234 self.diffEdit.ensureCursorVisible() 2253 self.diffEdit.ensureCursorVisible()
2235 2254
2236 @pyqtSlot(str) 2255 @pyqtSlot(str)
2237 def on_diffSelectLabel_linkActivated(self, link): 2256 def on_diffSelectLabel_linkActivated(self, link):

eric ide

mercurial