Enhanced LogBrowserDialog.

Sat, 17 Apr 2010 16:20:11 +0000

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 17 Apr 2010 16:20:11 +0000
changeset 180
40ac468c2558
parent 179
09260f69bf37
child 181
4af57f97c1bc

Enhanced LogBrowserDialog.

APIs/Python3/eric5.api file | annotate | diff | comparison | revisions
Documentation/Help/source.qch file | annotate | diff | comparison | revisions
Documentation/Help/source.qhp file | annotate | diff | comparison | revisions
Documentation/Source/eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/HgLogBrowserDialog.py file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/HgLogBrowserDialog.ui file | annotate | diff | comparison | revisions
--- a/APIs/Python3/eric5.api	Fri Apr 16 06:13:49 2010 +0000
+++ b/APIs/Python3/eric5.api	Sat Apr 17 16:20:11 2010 +0000
@@ -2841,7 +2841,17 @@
 eric5.Plugins.VcsPlugins.vcsMercurial.HgDiffDialog.HgDiffDialog.on_sendButton_clicked?4()
 eric5.Plugins.VcsPlugins.vcsMercurial.HgDiffDialog.HgDiffDialog.start?4(fn, versions = None)
 eric5.Plugins.VcsPlugins.vcsMercurial.HgDiffDialog.HgDiffDialog?1(vcs, parent = None)
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.COLORNAMES?7
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.COLORS?7
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.AuthorColumn?7
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.BranchColumn?7
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.DateColumn?7
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.IconColumn?7
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.MessageColumn?7
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.RevisionColumn?7
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.TagsColumn?7
 eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.closeEvent?4(e)
+eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.col2x?4(radius)
 eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.keyPressEvent?4(evt)
 eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.on_buttonBox_clicked?4(button)
 eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.HgLogBrowserDialog.on_clearRxEditButton_clicked?4()
Binary file Documentation/Help/source.qch has changed
--- a/Documentation/Help/source.qhp	Fri Apr 16 06:13:49 2010 +0000
+++ b/Documentation/Help/source.qhp	Sat Apr 17 16:20:11 2010 +0000
@@ -7963,11 +7963,15 @@
       <keyword name="HgLogBrowserDialog (Module)" id="HgLogBrowserDialog (Module)" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html" />
       <keyword name="HgLogBrowserDialog" id="HgLogBrowserDialog" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog" />
       <keyword name="HgLogBrowserDialog (Constructor)" id="HgLogBrowserDialog (Constructor)" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__init__" />
+      <keyword name="HgLogBrowserDialog.__branchColor" id="HgLogBrowserDialog.__branchColor" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__branchColor" />
       <keyword name="HgLogBrowserDialog.__diffRevisions" id="HgLogBrowserDialog.__diffRevisions" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__diffRevisions" />
       <keyword name="HgLogBrowserDialog.__filterLogs" id="HgLogBrowserDialog.__filterLogs" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__filterLogs" />
       <keyword name="HgLogBrowserDialog.__finish" id="HgLogBrowserDialog.__finish" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__finish" />
+      <keyword name="HgLogBrowserDialog.__generateEdges" id="HgLogBrowserDialog.__generateEdges" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__generateEdges" />
       <keyword name="HgLogBrowserDialog.__generateFileItem" id="HgLogBrowserDialog.__generateFileItem" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__generateFileItem" />
+      <keyword name="HgLogBrowserDialog.__generateIcon" id="HgLogBrowserDialog.__generateIcon" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__generateIcon" />
       <keyword name="HgLogBrowserDialog.__generateLogItem" id="HgLogBrowserDialog.__generateLogItem" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__generateLogItem" />
+      <keyword name="HgLogBrowserDialog.__getColor" id="HgLogBrowserDialog.__getColor" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__getColor" />
       <keyword name="HgLogBrowserDialog.__getLogEntries" id="HgLogBrowserDialog.__getLogEntries" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__getLogEntries" />
       <keyword name="HgLogBrowserDialog.__procFinished" id="HgLogBrowserDialog.__procFinished" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__procFinished" />
       <keyword name="HgLogBrowserDialog.__processBuffer" id="HgLogBrowserDialog.__processBuffer" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__processBuffer" />
@@ -7976,8 +7980,8 @@
       <keyword name="HgLogBrowserDialog.__resizeColumnsFiles" id="HgLogBrowserDialog.__resizeColumnsFiles" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__resizeColumnsFiles" />
       <keyword name="HgLogBrowserDialog.__resizeColumnsLog" id="HgLogBrowserDialog.__resizeColumnsLog" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__resizeColumnsLog" />
       <keyword name="HgLogBrowserDialog.__resortFiles" id="HgLogBrowserDialog.__resortFiles" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__resortFiles" />
-      <keyword name="HgLogBrowserDialog.__resortLog" id="HgLogBrowserDialog.__resortLog" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.__resortLog" />
       <keyword name="HgLogBrowserDialog.closeEvent" id="HgLogBrowserDialog.closeEvent" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.closeEvent" />
+      <keyword name="HgLogBrowserDialog.col2x" id="HgLogBrowserDialog.col2x" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.col2x" />
       <keyword name="HgLogBrowserDialog.keyPressEvent" id="HgLogBrowserDialog.keyPressEvent" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.keyPressEvent" />
       <keyword name="HgLogBrowserDialog.on_buttonBox_clicked" id="HgLogBrowserDialog.on_buttonBox_clicked" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.on_buttonBox_clicked" />
       <keyword name="HgLogBrowserDialog.on_clearRxEditButton_clicked" id="HgLogBrowserDialog.on_clearRxEditButton_clicked" ref="eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html#HgLogBrowserDialog.on_clearRxEditButton_clicked" />
--- a/Documentation/Source/eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html	Fri Apr 16 06:13:49 2010 +0000
+++ b/Documentation/Source/eric5.Plugins.VcsPlugins.vcsMercurial.HgLogBrowserDialog.html	Sat Apr 17 16:20:11 2010 +0000
@@ -26,7 +26,7 @@
 </p>
 <h3>Global Attributes</h3>
 <table>
-<tr><td>None</td></tr>
+<tr><td>COLORNAMES</td></tr><tr><td>COLORS</td></tr>
 </table>
 <h3>Classes</h3>
 <table>
@@ -49,7 +49,7 @@
 QDialog, Ui_HgLogBrowserDialog
 <h3>Class Attributes</h3>
 <table>
-<tr><td>None</td></tr>
+<tr><td>AuthorColumn</td></tr><tr><td>BranchColumn</td></tr><tr><td>DateColumn</td></tr><tr><td>IconColumn</td></tr><tr><td>MessageColumn</td></tr><tr><td>RevisionColumn</td></tr><tr><td>TagsColumn</td></tr>
 </table>
 <h3>Methods</h3>
 <table>
@@ -57,6 +57,9 @@
 <td><a href="#HgLogBrowserDialog.__init__">HgLogBrowserDialog</a></td>
 <td>Constructor</td>
 </tr><tr>
+<td><a href="#HgLogBrowserDialog.__branchColor">__branchColor</a></td>
+<td>Private method to calculate a color for a given branch name.</td>
+</tr><tr>
 <td><a href="#HgLogBrowserDialog.__diffRevisions">__diffRevisions</a></td>
 <td>Private method to do a diff of two revisions.</td>
 </tr><tr>
@@ -66,12 +69,21 @@
 <td><a href="#HgLogBrowserDialog.__finish">__finish</a></td>
 <td>Private slot called when the process finished or the user pressed the button.</td>
 </tr><tr>
+<td><a href="#HgLogBrowserDialog.__generateEdges">__generateEdges</a></td>
+<td>Private method to generate edge info for the give data.</td>
+</tr><tr>
 <td><a href="#HgLogBrowserDialog.__generateFileItem">__generateFileItem</a></td>
 <td>Private method to generate a changed files tree entry.</td>
 </tr><tr>
+<td><a href="#HgLogBrowserDialog.__generateIcon">__generateIcon</a></td>
+<td>Private method to generate an icon containing the revision tree for the given data.</td>
+</tr><tr>
 <td><a href="#HgLogBrowserDialog.__generateLogItem">__generateLogItem</a></td>
 <td>Private method to generate a log tree entry.</td>
 </tr><tr>
+<td><a href="#HgLogBrowserDialog.__getColor">__getColor</a></td>
+<td>Private method to get the (rotating) name of the color given an index.</td>
+</tr><tr>
 <td><a href="#HgLogBrowserDialog.__getLogEntries">__getLogEntries</a></td>
 <td>Private method to retrieve log entries from the repository.</td>
 </tr><tr>
@@ -96,12 +108,12 @@
 <td><a href="#HgLogBrowserDialog.__resortFiles">__resortFiles</a></td>
 <td>Private method to resort the changed files tree.</td>
 </tr><tr>
-<td><a href="#HgLogBrowserDialog.__resortLog">__resortLog</a></td>
-<td>Private method to resort the log tree.</td>
-</tr><tr>
 <td><a href="#HgLogBrowserDialog.closeEvent">closeEvent</a></td>
 <td>Private slot implementing a close event handler.</td>
 </tr><tr>
+<td><a href="#HgLogBrowserDialog.col2x">col2x</a></td>
+<td></td>
+</tr><tr>
 <td><a href="#HgLogBrowserDialog.keyPressEvent">keyPressEvent</a></td>
 <td>Protected slot to handle a key press event.</td>
 </tr><tr>
@@ -167,6 +179,21 @@
 <dd>
 parent widget (QWidget)
 </dd>
+</dl><a NAME="HgLogBrowserDialog.__branchColor" ID="HgLogBrowserDialog.__branchColor"></a>
+<h4>HgLogBrowserDialog.__branchColor</h4>
+<b>__branchColor</b>(<i>branchName</i>)
+<p>
+        Private method to calculate a color for a given branch name.
+</p><dl>
+<dt><i>branchName</i></dt>
+<dd>
+name of the branch (string)
+</dd>
+</dl><dl>
+<dt>Returns:</dt>
+<dd>
+name of the color to use (string)
+</dd>
 </dl><a NAME="HgLogBrowserDialog.__diffRevisions" ID="HgLogBrowserDialog.__diffRevisions"></a>
 <h4>HgLogBrowserDialog.__diffRevisions</h4>
 <b>__diffRevisions</b>(<i>rev1, rev2</i>)
@@ -190,7 +217,28 @@
 <b>__finish</b>(<i></i>)
 <p>
         Private slot called when the process finished or the user pressed the button.
-</p><a NAME="HgLogBrowserDialog.__generateFileItem" ID="HgLogBrowserDialog.__generateFileItem"></a>
+</p><a NAME="HgLogBrowserDialog.__generateEdges" ID="HgLogBrowserDialog.__generateEdges"></a>
+<h4>HgLogBrowserDialog.__generateEdges</h4>
+<b>__generateEdges</b>(<i>rev, parents</i>)
+<p>
+        Private method to generate edge info for the give data.
+</p><dl>
+<dt><i>rev</i></dt>
+<dd>
+revision to calculate edge info for (integer)
+</dd><dt><i>parents</i></dt>
+<dd>
+list of parent revisions (list of integers)
+</dd>
+</dl><dl>
+<dt>Returns:</dt>
+<dd>
+tuple containing the column and color index for
+            the given node and a list of tuples indicating the edges
+            between the given node and its parents 
+            (integer, integer, [(integer, integer, integer), ...])
+</dd>
+</dl><a NAME="HgLogBrowserDialog.__generateFileItem" ID="HgLogBrowserDialog.__generateFileItem"></a>
 <h4>HgLogBrowserDialog.__generateFileItem</h4>
 <b>__generateFileItem</b>(<i>action, path</i>)
 <p>
@@ -208,9 +256,39 @@
 <dd>
 reference to the generated item (QTreeWidgetItem)
 </dd>
+</dl><a NAME="HgLogBrowserDialog.__generateIcon" ID="HgLogBrowserDialog.__generateIcon"></a>
+<h4>HgLogBrowserDialog.__generateIcon</h4>
+<b>__generateIcon</b>(<i>column, color, bottomedges, topedges, dotColor</i>)
+<p>
+        Private method to generate an icon containing the revision tree for the
+        given data.
+</p><dl>
+<dt><i>column</i></dt>
+<dd>
+column index of the revision (integer)
+</dd><dt><i>color</i></dt>
+<dd>
+color of the node (integer)
+</dd><dt><i>bottomedges</i></dt>
+<dd>
+list of edges for the bottom of the node
+            (list of tuples of three integers)
+</dd><dt><i>topedges</i></dt>
+<dd>
+list of edges for the top of the node
+            (list of tuples of three integers)
+</dd><dt><i>dotColor</i></dt>
+<dd>
+color to be used for the dot (QColor)
+</dd>
+</dl><dl>
+<dt>Returns:</dt>
+<dd>
+icon for the node (QIcon)
+</dd>
 </dl><a NAME="HgLogBrowserDialog.__generateLogItem" ID="HgLogBrowserDialog.__generateLogItem"></a>
 <h4>HgLogBrowserDialog.__generateLogItem</h4>
-<b>__generateLogItem</b>(<i>author, date, message, revision, changedPaths</i>)
+<b>__generateLogItem</b>(<i>author, date, message, revision, changedPaths, parents, branches, tags</i>)
 <p>
         Private method to generate a log tree entry.
 </p><dl>
@@ -229,13 +307,37 @@
 </dd><dt><i>changedPaths</i></dt>
 <dd>
 list of dictionary objects containing
-            info about the changed files/directories 
+            info about the changed files/directories
+</dd><dt><i>parents</i></dt>
+<dd>
+list of parent revisions (list of integers)
+</dd><dt><i>branches</i></dt>
+<dd>
+list of branches (list of strings)
+</dd><dt><i>tags</i></dt>
+<dd>
+list of tags (string)
 </dd>
 </dl><dl>
 <dt>Returns:</dt>
 <dd>
 reference to the generated item (QTreeWidgetItem)
 </dd>
+</dl><a NAME="HgLogBrowserDialog.__getColor" ID="HgLogBrowserDialog.__getColor"></a>
+<h4>HgLogBrowserDialog.__getColor</h4>
+<b>__getColor</b>(<i>n</i>)
+<p>
+        Private method to get the (rotating) name of the color given an index.
+</p><dl>
+<dt><i>n</i></dt>
+<dd>
+color index (integer)
+</dd>
+</dl><dl>
+<dt>Returns:</dt>
+<dd>
+color name (string)
+</dd>
 </dl><a NAME="HgLogBrowserDialog.__getLogEntries" ID="HgLogBrowserDialog.__getLogEntries"></a>
 <h4>HgLogBrowserDialog.__getLogEntries</h4>
 <b>__getLogEntries</b>(<i>startRev = None</i>)
@@ -294,11 +396,6 @@
 <b>__resortFiles</b>(<i></i>)
 <p>
         Private method to resort the changed files tree.
-</p><a NAME="HgLogBrowserDialog.__resortLog" ID="HgLogBrowserDialog.__resortLog"></a>
-<h4>HgLogBrowserDialog.__resortLog</h4>
-<b>__resortLog</b>(<i></i>)
-<p>
-        Private method to resort the log tree.
 </p><a NAME="HgLogBrowserDialog.closeEvent" ID="HgLogBrowserDialog.closeEvent"></a>
 <h4>HgLogBrowserDialog.closeEvent</h4>
 <b>closeEvent</b>(<i>e</i>)
@@ -309,7 +406,10 @@
 <dd>
 close event (QCloseEvent)
 </dd>
-</dl><a NAME="HgLogBrowserDialog.keyPressEvent" ID="HgLogBrowserDialog.keyPressEvent"></a>
+</dl><a NAME="HgLogBrowserDialog.col2x" ID="HgLogBrowserDialog.col2x"></a>
+<h4>HgLogBrowserDialog.col2x</h4>
+<b>col2x</b>(<i>radius</i>)
+<a NAME="HgLogBrowserDialog.keyPressEvent" ID="HgLogBrowserDialog.keyPressEvent"></a>
 <h4>HgLogBrowserDialog.keyPressEvent</h4>
 <b>keyPressEvent</b>(<i>evt</i>)
 <p>
--- a/Plugins/VcsPlugins/vcsMercurial/HgLogBrowserDialog.py	Fri Apr 16 06:13:49 2010 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/HgLogBrowserDialog.py	Sat Apr 17 16:20:11 2010 +0000
@@ -9,9 +9,10 @@
 
 import os
 
-from PyQt4.QtCore import pyqtSlot, SIGNAL, Qt, QDate, QProcess, QTimer, QRegExp
+from PyQt4.QtCore import pyqtSlot, SIGNAL, Qt, QDate, QProcess, QTimer, QRegExp, QSize
 from PyQt4.QtGui import QDialog, QDialogButtonBox, QHeaderView, QTreeWidgetItem, \
-    QApplication, QMessageBox, QCursor, QWidget, QLineEdit
+    QApplication, QMessageBox, QCursor, QWidget, QLineEdit, QColor, QPixmap, \
+    QPainter, QPen, QBrush, QIcon
 
 from .Ui_HgLogBrowserDialog import Ui_HgLogBrowserDialog
 from .HgDiffDialog import HgDiffDialog
@@ -20,10 +21,23 @@
 
 import Preferences
 
+COLORNAMES = ["blue", "darkgreen", "red", "green", "darkblue", "purple",
+              "cyan", "olive", "magenta", "darkred", "darkmagenta",
+              "darkcyan", "gray", "yellow"]
+COLORS = [str(QColor(x).name()) for x in COLORNAMES]
+
 class HgLogBrowserDialog(QDialog, Ui_HgLogBrowserDialog):
     """
     Class implementing a dialog to browse the log history.
     """
+    IconColumn     = 0
+    BranchColumn   = 1
+    RevisionColumn = 2
+    AuthorColumn   = 3
+    DateColumn     = 4
+    MessageColumn  = 5
+    TagsColumn     = 6
+    
     def __init__(self, vcs, parent = None):
         """
         Constructor
@@ -56,8 +70,8 @@
         self.stopCheckBox.setChecked(self.vcs.getPlugin().getPreferences("StopLogOnCopy"))
         
         self.__messageRole = Qt.UserRole
-        self.__changesRole = Qt.UserRole + 1
-        self.__parentsRole = Qt.UserRole + 2
+        self.__changesRole = Qt.UserRole + 1
+        self.__edgesRole   = Qt.UserRole + 2
         
         self.process = QProcess()
         self.connect(self.process, SIGNAL('finished(int, QProcess::ExitStatus)'),
@@ -77,6 +91,18 @@
         self.diff = None
         self.__started = False
         self.__lastRev = 0
+        
+        # attributes to store log graph data
+        self.__revs = []
+        self.__revColors = {}
+        self.__revColor = 0
+        
+        self.__dotRadius = 8
+        self.__rowHeight = 20
+        
+        self.__branchColors = {}
+        
+        self.logTree.setIconSize(QSize(100 * self.__rowHeight, self.__rowHeight))
     
     def closeEvent(self, e):
         """
@@ -99,13 +125,6 @@
         self.logTree.header().resizeSections(QHeaderView.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())
-    
     def __resizeColumnsFiles(self):
         """
         Private method to resize the changed files tree columns.
@@ -123,7 +142,139 @@
         self.filesTree.sortItems(sortColumn, 
             self.filesTree.header().sortIndicatorOrder())
     
-    def __generateLogItem(self, author, date, message, revision, changedPaths, parents):
+    def __getColor(self, n):
+        """
+        Private method to get the (rotating) name of the color given an index.
+        
+        @param n color index (integer)
+        @return color name (string)
+        """
+        return COLORS[n % len(COLORS)]
+    
+    def __branchColor(self, branchName):
+        """
+        Private method to calculate a color for a given branch name.
+        
+        @param branchName name of the branch (string)
+        @return name of the color to use (string)
+        """
+        if branchName not in self.__branchColors:
+            self.__branchColors[branchName] = self.__getColor(len(self.__branchColors))
+        return self.__branchColors[branchName]
+    
+    def __generateEdges(self, rev, parents):
+        """
+        Private method to generate edge info for the give data.
+        
+        @param rev revision to calculate edge info for (integer)
+        @param parents list of parent revisions (list of integers)
+        @return tuple containing the column and color index for
+            the given node and a list of tuples indicating the edges
+            between the given node and its parents 
+            (integer, integer, [(integer, integer, integer), ...])
+        """
+        if not parents:
+            parents = [rev - 1]
+        
+        if rev not in self.__revs:
+            # new head
+            self.__revs.append(rev)
+            self.__revColors[rev] = self.__revColor
+            self.__revColor += 1
+        
+        col = self.__revs.index(rev)
+        color = self.__revColors.pop(rev)
+        next = self.__revs[:]
+        
+        # add parents to next
+        addparents = [p for p in parents if p not in next]
+        next[col:col + 1] = addparents
+        
+        # set colors for the parents
+        for i, p in enumerate(addparents):
+            if not i:
+                self.__revColors[p] = color
+            else:
+                self.__revColors[p] = self.__revColor
+                self.__revColor += 1
+        
+        # add edges to the graph
+        edges = []
+        if rev:
+            for ecol, erev in enumerate(self.__revs):
+                if erev in next:
+                    edges.append((ecol, next.index(erev), self.__revColors[erev]))
+                elif erev == rev:
+                    for p in parents:
+                        edges.append((ecol, next.index(p), self.__revColors[p]))
+        
+        self.__revs = next
+        return col, color, edges
+    
+    def __generateIcon(self, column, color, bottomedges, topedges, dotColor):
+        """
+        Private method to generate an icon containing the revision tree for the
+        given data.
+        
+        @param column column index of the revision (integer)
+        @param color color of the node (integer)
+        @param bottomedges list of edges for the bottom of the node 
+            (list of tuples of three integers)
+        @param topedges list of edges for the top of the node 
+            (list of tuples of three integers)
+        @param dotColor color to be used for the dot (QColor)
+        @return icon for the node (QIcon)
+        """
+        def col2x(col, radius):
+            return int(1.2 * radius) * col + radius // 2 + 3
+        
+        radius = self.__dotRadius
+        w = len(bottomedges) * radius + 20
+        h = self.__rowHeight
+
+        dot_x = col2x(column, radius) - radius // 2
+        dot_y = h // 2
+
+        pix = QPixmap(w, h)
+        pix.fill(QColor(0, 0, 0, 0))
+        painter = QPainter(pix)
+        painter.setRenderHint(QPainter.Antialiasing)
+
+        pen = QPen(Qt.blue)
+        pen.setWidth(2)
+        painter.setPen(pen)
+
+        lpen = QPen(pen)
+        lpen.setColor(Qt.black)
+        painter.setPen(lpen)
+
+        for y1, y2, lines in ((0, h, bottomedges),
+                              (-h, 0, topedges)):
+            if lines:
+                for start, end, ecolor in lines:
+                    lpen = QPen(pen)
+                    lpen.setColor(QColor(self.__getColor(ecolor)))
+                    lpen.setWidth(2)
+                    painter.setPen(lpen)
+                    x1 = col2x(start, radius)
+                    x2 = col2x(end, radius)
+                    painter.drawLine(x1, dot_y + y1, x2, dot_y + y2)
+
+        penradius = 1
+        pencolor = Qt.black
+
+        dot_y = (h // 2) - radius // 2
+
+        painter.setBrush(dotColor)
+        pen = QPen(pencolor)
+        pen.setWidth(penradius)
+        painter.setPen(pen)
+        painter.drawEllipse(dot_x, dot_y, radius, radius)
+        painter.end()
+        return QIcon(pix)
+    
+    def __generateLogItem(self, author, date, message, revision, changedPaths, parents, 
+                          branches, tags):
         """
         Private method to generate a log tree entry.
         
@@ -132,8 +283,10 @@
         @param message text of the log message (list of strings)
         @param revision revision info (string)
         @param changedPaths list of dictionary objects containing
-            info about the changed files/directories
+            info about the changed files/directories
         @param parents list of parent revisions (list of integers)
+        @param branches list of branches (list of strings)
+        @param tags list of tags (string)
         @return reference to the generated item (QTreeWidgetItem)
         """
         msg = []
@@ -142,21 +295,35 @@
         
         rev, node = revision.split(":")
         itm = QTreeWidgetItem(self.logTree, [
+            "", 
+            branches[0], 
             "{0:>7}:{1}".format(rev, node), 
             author, 
             date, 
-            " ".join(msg), 
+            " ".join(msg[:1]), 
+            ", ".join(tags), 
         ])
         
+        itm.setForeground(self.BranchColumn, 
+                          QBrush(QColor(self.__branchColor(branches[0]))))
+        
+        column, color, edges = self.__generateEdges(int(rev), parents)
+        
         itm.setData(0, self.__messageRole, message)
-        itm.setData(0, self.__changesRole, changedPaths)
-        itm.setData(0, self.__parentsRole, parents)
+        itm.setData(0, self.__changesRole, changedPaths)
+        itm.setData(0, self.__edgesRole, edges)
         
-        itm.setTextAlignment(0, Qt.AlignLeft)
-        itm.setTextAlignment(1, Qt.AlignLeft)
-        itm.setTextAlignment(2, Qt.AlignLeft)
-        itm.setTextAlignment(3, Qt.AlignLeft)
-        itm.setTextAlignment(4, Qt.AlignLeft)
+        if self.fname == "." and self.dname == self.repodir:
+            if self.logTree.topLevelItemCount() > 1:
+                topedges = \
+                    self.logTree.topLevelItem(self.logTree.indexOfTopLevelItem(itm) - 1)\
+                    .data(0, self.__edgesRole)
+            else:
+                topedges = None
+            
+            icon = self.__generateIcon(column, color, edges, topedges, 
+                                       QColor(self.__branchColor(branches[0])))
+            itm.setIcon(0, icon)
         
         try:
             self.__lastRev = int(revision.split(":")[0])
@@ -217,13 +384,15 @@
             args.append('--follow')
         args.append('--template')
         args.append("change|{rev}:{node|short}\n"
-                    "user|{author}\n"
+                    "user|{email}\n"
                     "parents|{parents}\n"
                     "date|{date|isodate}\n"
                     "description|{desc}\n"
                     "file_adds|{file_adds}\n"
                     "files_mods|{file_mods}\n"
                     "file_dels|{file_dels}\n"
+                    "branches|{branches}\n"
+                    "tags|{tags}\n"
                     "@@@\n")
         if self.fname != "." or self.dname != self.repodir:
             args.append(self.filename)
@@ -313,9 +482,10 @@
                 if key == "change":
                     log["revision"] = value.strip()
                 elif key == "user":
-                    log["author"] = value.strip()
-                elif key == "parents":
-                    log["parents"] = [int(x) for x in value.strip().split()]
+                    log["author"] = value.strip()
+                elif key == "parents":
+                    log["parents"] = \
+                        [int(x.split(":", 1)[0]) for x in value.strip().split()]
                 elif key == "date":
                     log["date"] = " ".join(value.strip().split()[:2])
                 elif key == "description":
@@ -341,14 +511,21 @@
                                 "action" : "D", 
                                 "path"   : f, 
                             })
+                elif key == "branches":
+                    if value.strip():
+                        log["branches"] = value.strip().split()
+                    else:
+                        log["branches"] = ["default"]
+                elif key == "tags":
+                    log["tags"] = value.strip().split()
                 else:
                     if value.strip():
                         log["message"].append(value.strip())
             else:
                 if len(log) > 1:
                     self.__generateLogItem(log["author"], log["date"], 
-                        log["message"], log["revision"], changedPaths,
-                        log["parents"])
+                        log["message"], log["revision"], changedPaths,
+                        log["parents"], log["branches"], log["tags"])
                     dt = QDate.fromString(log["date"], Qt.ISODate)
                     if not self.__maxDate.isValid() and not self.__minDate.isValid():
                         self.__maxDate = dt
@@ -364,7 +541,6 @@
         
         self.logTree.doItemsLayout()
         self.__resizeColumnsLog()
-        self.__resortLog()
         
         if self.__started:
             self.logTree.setCurrentItem(self.logTree.topLevelItem(0))
@@ -486,13 +662,13 @@
         if itm is None:
             self.diffPreviousButton.setEnabled(False)
             return
-        rev2 = int(itm.text(0).split(":")[0])
+        rev2 = int(itm.text(self.RevisionColumn).split(":")[0])
         
         itm = self.logTree.topLevelItem(self.logTree.indexOfTopLevelItem(itm) + 1)
         if itm is None:
             self.diffPreviousButton.setEnabled(False)
             return
-        rev1 = int(itm.text(0).split(":")[0])
+        rev1 = int(itm.text(self.RevisionColumn).split(":")[0])
         
         self.__diffRevisions(rev1, rev2)
     
@@ -506,8 +682,8 @@
             self.diffRevisionsButton.setEnabled(False)
             return
         
-        rev2 = int(items[0].text(0).split(":")[0])
-        rev1 = int(items[1].text(0).split(":")[0])
+        rev2 = int(items[0].text(self.RevisionColumn).split(":")[0])
+        rev1 = int(items[1].text(self.RevisionColumn).split(":")[0])
         
         self.__diffRevisions(min(rev1, rev2), max(rev1, rev2))
     
@@ -556,23 +732,24 @@
             to_ = self.toDate.date().addDays(1).toString("yyyy-MM-dd")
             txt = self.fieldCombo.currentText()
             if txt == self.trUtf8("Author"):
-                fieldIndex = 1
+                fieldIndex = self.AuthorColumn
                 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive)
             elif txt == self.trUtf8("Revision"):
-                fieldIndex = 0
+                fieldIndex = self.RevisionColumn
                 txt = self.rxEdit.text()
                 if txt.startswith("^"):
                     searchRx = QRegExp("^\s*%s" % txt[1:], Qt.CaseInsensitive)
                 else:
                     searchRx = QRegExp(txt, Qt.CaseInsensitive)
             else:
-                fieldIndex = 3
+                fieldIndex = self.MessageColumn
                 searchRx = QRegExp(self.rxEdit.text(), Qt.CaseInsensitive)
             
             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 \
+                if topItem.text(self.DateColumn) <= to_ and \
+                   topItem.text(self.DateColumn) >= from_ and \
                    searchRx.indexIn(topItem.text(fieldIndex)) > -1:
                     topItem.setHidden(False)
                     if topItem is currentItem:
--- a/Plugins/VcsPlugins/vcsMercurial/HgLogBrowserDialog.ui	Fri Apr 16 06:13:49 2010 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/HgLogBrowserDialog.ui	Sat Apr 17 16:20:11 2010 +0000
@@ -111,14 +111,21 @@
      <property name="itemsExpandable">
       <bool>false</bool>
      </property>
-     <property name="sortingEnabled">
-      <bool>true</bool>
-     </property>
      <property name="allColumnsShowFocus">
       <bool>true</bool>
      </property>
      <column>
       <property name="text">
+       <string> </string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Branch</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
        <string>Revision</string>
       </property>
      </column>
@@ -137,6 +144,11 @@
        <string>Message</string>
       </property>
      </column>
+     <column>
+      <property name="text">
+       <string>Tags</string>
+      </property>
+     </column>
     </widget>
    </item>
    <item>

eric ide

mercurial