Plugins/VcsPlugins/vcsMercurial/HgLogBrowserDialog.py

changeset 932
efd23a913a09
parent 921
ae00e4fbcec0
child 945
8cd4d08fa9f6
equal deleted inserted replaced
930:95309225005e 932:efd23a913a09
7 Module implementing a dialog to browse the log history. 7 Module implementing a dialog to browse the log history.
8 """ 8 """
9 9
10 import os 10 import os
11 11
12 from PyQt4.QtCore import pyqtSlot, Qt, QDate, QProcess, QTimer, QRegExp, QSize, QPoint 12 from PyQt4.QtCore import pyqtSlot, Qt, QDate, QProcess, QTimer, QRegExp, \
13 from PyQt4.QtGui import QDialog, QDialogButtonBox, QHeaderView, QTreeWidgetItem, \ 13 QSize, QPoint
14 QApplication, QCursor, QWidget, QLineEdit, QColor, QPixmap, \ 14 from PyQt4.QtGui import QDialog, QDialogButtonBox, QHeaderView, \
15 QPainter, QPen, QBrush, QIcon 15 QTreeWidgetItem, QApplication, QCursor, QWidget, QLineEdit, QColor, \
16 QPixmap, QPainter, QPen, QBrush, QIcon
16 17
17 from E5Gui.E5Application import e5App 18 from E5Gui.E5Application import e5App
18 from E5Gui import E5MessageBox 19 from E5Gui import E5MessageBox
19 20
20 from .Ui_HgLogBrowserDialog import Ui_HgLogBrowserDialog 21 from .Ui_HgLogBrowserDialog import Ui_HgLogBrowserDialog
79 80
80 self.fromDate.setDisplayFormat("yyyy-MM-dd") 81 self.fromDate.setDisplayFormat("yyyy-MM-dd")
81 self.toDate.setDisplayFormat("yyyy-MM-dd") 82 self.toDate.setDisplayFormat("yyyy-MM-dd")
82 self.fromDate.setDate(QDate.currentDate()) 83 self.fromDate.setDate(QDate.currentDate())
83 self.toDate.setDate(QDate.currentDate()) 84 self.toDate.setDate(QDate.currentDate())
84 self.fieldCombo.setCurrentIndex(self.fieldCombo.findText(self.trUtf8("Message"))) 85 self.fieldCombo.setCurrentIndex(self.fieldCombo.findText(
86 self.trUtf8("Message")))
85 self.clearRxEditButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png")) 87 self.clearRxEditButton.setIcon(UI.PixmapCache.getIcon("clearLeft.png"))
86 self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences("LogLimit")) 88 self.limitSpinBox.setValue(self.vcs.getPlugin().getPreferences(
87 self.stopCheckBox.setChecked(self.vcs.getPlugin().getPreferences("StopLogOnCopy")) 89 "LogLimit"))
90 self.stopCheckBox.setChecked(self.vcs.getPlugin().getPreferences(
91 "StopLogOnCopy"))
88 92
89 if mode in ("incoming", "outgoing"): 93 if mode in ("incoming", "outgoing"):
90 self.nextButton.setEnabled(False) 94 self.nextButton.setEnabled(False)
91 self.limitSpinBox.setEnabled(False) 95 self.limitSpinBox.setEnabled(False)
92 96
121 self.__rowHeight = 20 125 self.__rowHeight = 20
122 126
123 self.__branchColors = {} 127 self.__branchColors = {}
124 self.__allBranchesFilter = self.trUtf8("All") 128 self.__allBranchesFilter = self.trUtf8("All")
125 129
126 self.logTree.setIconSize(QSize(100 * self.__rowHeight, self.__rowHeight)) 130 self.logTree.setIconSize(
131 QSize(100 * self.__rowHeight, self.__rowHeight))
127 132
128 self.__projectRevision = -1 133 self.__projectRevision = -1
129 134
130 def closeEvent(self, e): 135 def closeEvent(self, e):
131 """ 136 """
180 185
181 @param branchName name of the branch (string) 186 @param branchName name of the branch (string)
182 @return name of the color to use (string) 187 @return name of the color to use (string)
183 """ 188 """
184 if branchName not in self.__branchColors: 189 if branchName not in self.__branchColors:
185 self.__branchColors[branchName] = self.__getColor(len(self.__branchColors)) 190 self.__branchColors[branchName] = self.__getColor(
191 len(self.__branchColors))
186 return self.__branchColors[branchName] 192 return self.__branchColors[branchName]
187 193
188 def __generateEdges(self, rev, parents): 194 def __generateEdges(self, rev, parents):
189 """ 195 """
190 Private method to generate edge info for the give data. 196 Private method to generate edge info for the give data.
221 # add edges to the graph 227 # add edges to the graph
222 edges = [] 228 edges = []
223 if parents[0] != -1: 229 if parents[0] != -1:
224 for ecol, erev in enumerate(self.__revs): 230 for ecol, erev in enumerate(self.__revs):
225 if erev in next: 231 if erev in next:
226 edges.append((ecol, next.index(erev), self.__revColors[erev])) 232 edges.append(
233 (ecol, next.index(erev), self.__revColors[erev]))
227 elif erev == rev: 234 elif erev == rev:
228 for p in parents: 235 for p in parents:
229 edges.append((ecol, next.index(p), self.__revColors[p])) 236 edges.append(
237 (ecol, next.index(p), self.__revColors[p]))
230 238
231 self.__revs = next 239 self.__revs = next
232 return col, color, edges 240 return col, color, edges
233 241
234 def __generateIcon(self, column, color, bottomedges, topedges, dotColor, currentRev): 242 def __generateIcon(self, column, color, bottomedges, topedges, dotColor,
243 currentRev, closed):
235 """ 244 """
236 Private method to generate an icon containing the revision tree for the 245 Private method to generate an icon containing the revision tree for the
237 given data. 246 given data.
238 247
239 @param column column index of the revision (integer) 248 @param column column index of the revision (integer)
243 @param topedges list of edges for the top of the node 252 @param topedges list of edges for the top of the node
244 (list of tuples of three integers) 253 (list of tuples of three integers)
245 @param dotColor color to be used for the dot (QColor) 254 @param dotColor color to be used for the dot (QColor)
246 @param currentRev flag indicating to draw the icon for the 255 @param currentRev flag indicating to draw the icon for the
247 current revision (boolean) 256 current revision (boolean)
257 @param closed flag indicating to draw an icon for a closed
258 branch (boolean)
248 @return icon for the node (QIcon) 259 @return icon for the node (QIcon)
249 """ 260 """
250 def col2x(col, radius): 261 def col2x(col, radius):
251 """ 262 """
252 Local function to calculate a x-position for a column. 263 Local function to calculate a x-position for a column.
303 dot_x -= delta 314 dot_x -= delta
304 painter.setBrush(dotColor) 315 painter.setBrush(dotColor)
305 pen = QPen(pencolor) 316 pen = QPen(pencolor)
306 pen.setWidth(penradius) 317 pen.setWidth(penradius)
307 painter.setPen(pen) 318 painter.setPen(pen)
308 if self.commandMode in ("incoming", "outgoing"): 319 if closed:
320 painter.drawRect(dot_x - 2, dot_y + 1,
321 radius + 4, radius - 2)
322 elif self.commandMode in ("incoming", "outgoing"):
309 offset = radius // 2 323 offset = radius // 2
310 painter.drawConvexPolygon( 324 painter.drawConvexPolygon(
311 QPoint(dot_x + offset, dot_y), 325 QPoint(dot_x + offset, dot_y),
312 QPoint(dot_x, dot_y + offset), 326 QPoint(dot_x, dot_y + offset),
313 QPoint(dot_x + offset, dot_y + 2 * offset), 327 QPoint(dot_x + offset, dot_y + 2 * offset),
318 painter.end() 332 painter.end()
319 return QIcon(pix) 333 return QIcon(pix)
320 334
321 def __getParents(self, rev): 335 def __getParents(self, rev):
322 """ 336 """
323 Private method to get the parents of the currently viewed file/directory. 337 Private method to get the parents of the currently viewed
338 file/directory.
324 339
325 @param rev revision number to get parents for (string) 340 @param rev revision number to get parents for (string)
326 @return list of parent revisions (list of integers) 341 @return list of parent revisions (list of integers)
327 """ 342 """
328 errMsg = "" 343 errMsg = ""
356 Preferences.getSystem("IOEncoding"), 371 Preferences.getSystem("IOEncoding"),
357 'replace') 372 'replace')
358 parents = [int(p) for p in output.strip().splitlines()] 373 parents = [int(p) for p in output.strip().splitlines()]
359 else: 374 else:
360 if not finished: 375 if not finished:
361 errMsg = self.trUtf8("The hg process did not finish within 30s.") 376 errMsg = self.trUtf8(
377 "The hg process did not finish within 30s.")
362 else: 378 else:
363 errMsg = self.trUtf8("Could not start the hg executable.") 379 errMsg = self.trUtf8("Could not start the hg executable.")
364 380
365 if errMsg: 381 if errMsg:
366 E5MessageBox.critical(self, 382 E5MessageBox.critical(self,
393 self.__projectRevision = output.strip() 409 self.__projectRevision = output.strip()
394 if self.__projectRevision.endswith("+"): 410 if self.__projectRevision.endswith("+"):
395 self.__projectRevision = self.__projectRevision[:-1] 411 self.__projectRevision = self.__projectRevision[:-1]
396 else: 412 else:
397 if not finished: 413 if not finished:
398 errMsg = self.trUtf8("The hg process did not finish within 30s.") 414 errMsg = self.trUtf8(
415 "The hg process did not finish within 30s.")
399 else: 416 else:
400 errMsg = self.trUtf8("Could not start the hg executable.") 417 errMsg = self.trUtf8("Could not start the hg executable.")
401 418
402 if errMsg: 419 if errMsg:
403 E5MessageBox.critical(self, 420 E5MessageBox.critical(self,
431 parts = line.split() 448 parts = line.split()
432 self.__closedBranchesRevs.append( 449 self.__closedBranchesRevs.append(
433 parts[-2].split(":", 1)[0]) 450 parts[-2].split(":", 1)[0])
434 else: 451 else:
435 if not finished: 452 if not finished:
436 errMsg = self.trUtf8("The hg process did not finish within 30s.") 453 errMsg = self.trUtf8(
454 "The hg process did not finish within 30s.")
437 else: 455 else:
438 errMsg = self.trUtf8("Could not start the hg executable.") 456 errMsg = self.trUtf8("Could not start the hg executable.")
439 457
440 if errMsg: 458 if errMsg:
441 E5MessageBox.critical(self, 459 E5MessageBox.critical(self,
442 self.trUtf8("Mercurial Error"), 460 self.trUtf8("Mercurial Error"),
443 errMsg) 461 errMsg)
444 462
445 def __generateLogItem(self, author, date, message, revision, changedPaths, parents, 463 def __generateLogItem(self, author, date, message, revision, changedPaths,
446 branches, tags): 464 parents, branches, tags):
447 """ 465 """
448 Private method to generate a log tree entry. 466 Private method to generate a log tree entry.
449 467
450 @param author author info (string) 468 @param author author info (string)
451 @param date date info (string) 469 @param date date info (string)
494 itm.setData(0, self.__edgesRole, edges) 512 itm.setData(0, self.__edgesRole, edges)
495 itm.setData(0, self.__parentsRole, parents) 513 itm.setData(0, self.__parentsRole, parents)
496 514
497 if self.logTree.topLevelItemCount() > 1: 515 if self.logTree.topLevelItemCount() > 1:
498 topedges = \ 516 topedges = \
499 self.logTree.topLevelItem(self.logTree.indexOfTopLevelItem(itm) - 1)\ 517 self.logTree.topLevelItem(
518 self.logTree.indexOfTopLevelItem(itm) - 1)\
500 .data(0, self.__edgesRole) 519 .data(0, self.__edgesRole)
501 else: 520 else:
502 topedges = None 521 topedges = None
503 522
504 icon = self.__generateIcon(column, color, edges, topedges, 523 icon = self.__generateIcon(column, color, edges, topedges,
505 QColor(self.__branchColor(branches[0])), 524 QColor(self.__branchColor(branches[0])),
506 rev == self.__projectRevision) 525 rev == self.__projectRevision,
526 rev in self.__closedBranchesRevs)
507 itm.setIcon(0, icon) 527 itm.setIcon(0, icon)
508 528
509 try: 529 try:
510 self.__lastRev = int(revision.split(":")[0]) 530 self.__lastRev = int(revision.split(":")[0])
511 except ValueError: 531 except ValueError:
572 not self.stopCheckBox.isChecked(): 592 not self.stopCheckBox.isChecked():
573 args.append('--follow') 593 args.append('--follow')
574 if self.commandMode == "log": 594 if self.commandMode == "log":
575 args.append('--copies') 595 args.append('--copies')
576 args.append('--style') 596 args.append('--style')
577 args.append(os.path.join(os.path.dirname(__file__), "styles", "logBrowser.style")) 597 args.append(os.path.join(os.path.dirname(__file__),
598 "styles", "logBrowser.style"))
578 if self.commandMode == "incoming": 599 if self.commandMode == "incoming":
579 if self.bundle: 600 if self.bundle:
580 args.append(self.bundle) 601 args.append(self.bundle)
581 else: 602 else:
582 project = e5App().getObject("Project") 603 project = e5App().getObject("Project")
640 self.__processBuffer() 661 self.__processBuffer()
641 self.__finish() 662 self.__finish()
642 663
643 def __finish(self): 664 def __finish(self):
644 """ 665 """
645 Private slot called when the process finished or the user pressed the button. 666 Private slot called when the process finished or the user pressed
667 the button.
646 """ 668 """
647 if self.process is not None and \ 669 if self.process is not None and \
648 self.process.state() != QProcess.NotRunning: 670 self.process.state() != QProcess.NotRunning:
649 self.process.terminate() 671 self.process.terminate()
650 QTimer.singleShot(2000, self.process.kill) 672 QTimer.singleShot(2000, self.process.kill)
680 log["revision"] = value.strip() 702 log["revision"] = value.strip()
681 elif key == "user": 703 elif key == "user":
682 log["author"] = value.strip() 704 log["author"] = value.strip()
683 elif key == "parents": 705 elif key == "parents":
684 log["parents"] = \ 706 log["parents"] = \
685 [int(x.split(":", 1)[0]) for x in value.strip().split()] 707 [int(x.split(":", 1)[0])
708 for x in value.strip().split()]
686 elif key == "date": 709 elif key == "date":
687 log["date"] = " ".join(value.strip().split()[:2]) 710 log["date"] = " ".join(value.strip().split()[:2])
688 elif key == "description": 711 elif key == "description":
689 log["message"].append(value.strip()) 712 log["message"].append(value.strip())
690 elif key == "file_adds": 713 elif key == "file_adds":
739 if len(log) > 1: 762 if len(log) > 1:
740 self.__generateLogItem(log["author"], log["date"], 763 self.__generateLogItem(log["author"], log["date"],
741 log["message"], log["revision"], changedPaths, 764 log["message"], log["revision"], changedPaths,
742 log["parents"], log["branches"], log["tags"]) 765 log["parents"], log["branches"], log["tags"])
743 dt = QDate.fromString(log["date"], Qt.ISODate) 766 dt = QDate.fromString(log["date"], Qt.ISODate)
744 if not self.__maxDate.isValid() and not self.__minDate.isValid(): 767 if not self.__maxDate.isValid() and \
768 not self.__minDate.isValid():
745 self.__maxDate = dt 769 self.__maxDate = dt
746 self.__minDate = dt 770 self.__minDate = dt
747 else: 771 else:
748 if self.__maxDate < dt: 772 if self.__maxDate < dt:
749 self.__maxDate = dt 773 self.__maxDate = dt
784 if not branchFilter: 808 if not branchFilter:
785 branchFilter = self.__allBranchesFilter 809 branchFilter = self.__allBranchesFilter
786 self.branchCombo.clear() 810 self.branchCombo.clear()
787 self.branchCombo.addItems( 811 self.branchCombo.addItems(
788 [self.__allBranchesFilter] + sorted(self.__branchColors.keys())) 812 [self.__allBranchesFilter] + sorted(self.__branchColors.keys()))
789 self.branchCombo.setCurrentIndex(self.branchCombo.findText(branchFilter)) 813 self.branchCombo.setCurrentIndex(
814 self.branchCombo.findText(branchFilter))
790 815
791 self.__filterLogsEnabled = True 816 self.__filterLogsEnabled = True
792 self.__filterLogs() 817 self.__filterLogs()
793 818
794 def __readStdout(self): 819 def __readStdout(self):
1009 """ 1034 """
1010 if self.__filterLogsEnabled: 1035 if self.__filterLogsEnabled:
1011 from_ = self.fromDate.date().toString("yyyy-MM-dd") 1036 from_ = self.fromDate.date().toString("yyyy-MM-dd")
1012 to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd") 1037 to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd")
1013 branch = self.branchCombo.currentText() 1038 branch = self.branchCombo.currentText()
1039 closedBranch = branch + '--'
1014 1040
1015 txt = self.fieldCombo.currentText() 1041 txt = self.fieldCombo.currentText()
1016 if txt == self.trUtf8("Author"): 1042 if txt == self.trUtf8("Author"):
1017 fieldIndex = self.AuthorColumn 1043 fieldIndex = self.AuthorColumn
1018 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive) 1044 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive)
1019 elif txt == self.trUtf8("Revision"): 1045 elif txt == self.trUtf8("Revision"):
1020 fieldIndex = self.RevisionColumn 1046 fieldIndex = self.RevisionColumn
1021 txt = self.rxEdit.text() 1047 txt = self.rxEdit.text()
1022 if txt.startswith("^"): 1048 if txt.startswith("^"):
1023 searchRx = QRegExp("^\s*{0}".format(txt[1:]), Qt.CaseInsensitive) 1049 searchRx = QRegExp("^\s*{0}".format(txt[1:]),
1050 Qt.CaseInsensitive)
1024 else: 1051 else:
1025 searchRx = QRegExp(txt, Qt.CaseInsensitive) 1052 searchRx = QRegExp(txt, Qt.CaseInsensitive)
1026 else: 1053 else:
1027 fieldIndex = self.MessageColumn 1054 fieldIndex = self.MessageColumn
1028 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive) 1055 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive)
1031 for topIndex in range(self.logTree.topLevelItemCount()): 1058 for topIndex in range(self.logTree.topLevelItemCount()):
1032 topItem = self.logTree.topLevelItem(topIndex) 1059 topItem = self.logTree.topLevelItem(topIndex)
1033 if topItem.text(self.DateColumn) <= to_ and \ 1060 if topItem.text(self.DateColumn) <= to_ and \
1034 topItem.text(self.DateColumn) >= from_ and \ 1061 topItem.text(self.DateColumn) >= from_ and \
1035 (branch == self.__allBranchesFilter or \ 1062 (branch == self.__allBranchesFilter or \
1036 topItem.text(self.BranchColumn) == branch) and \ 1063 topItem.text(self.BranchColumn) in \
1064 [branch, closedBranch]) and \
1037 searchRx.indexIn(topItem.text(fieldIndex)) > -1: 1065 searchRx.indexIn(topItem.text(fieldIndex)) > -1:
1038 topItem.setHidden(False) 1066 topItem.setHidden(False)
1039 if topItem is currentItem: 1067 if topItem is currentItem:
1040 self.on_logTree_currentItemChanged(topItem, None) 1068 self.on_logTree_currentItemChanged(topItem, None)
1041 else: 1069 else:

eric ide

mercurial