Staging
v0.8.1
v0.8.1
https://foss.heptapod.net/mercurial/hgview
Tip revision: 512051e5f2cfbf321ffb9c31cb4692f6d03d3b06 authored by Julien Cristau on 29 September 2011, 12:43:42 UTC
[packaging] Fix upgrades from << 1.4
[packaging] Fix upgrades from << 1.4
Tip revision: 512051e
hgrepomodel.py
# Copyright (c) 2009-2011 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""
Qt4 model for hg repo changelogs and filelogs
"""
import sys
import re
import os, os.path as osp
from mercurial.node import nullrev
from mercurial.node import hex, short as short_hex
from mercurial.revlog import LookupError
from mercurial import util, error
from hgviewlib.hggraph import Graph, ismerge, diff as revdiff, HgRepoListWalker
from hgviewlib.hggraph import revision_grapher, filelog_grapher, getlog, gettags
from hgviewlib.config import HgConfig
from hgviewlib.util import tounicode, isbfile, Curry
from hgviewlib.qt4 import icon as geticon
from hgviewlib.decorators import timeit
from PyQt4 import QtCore, QtGui
connect = QtCore.QObject.connect
SIGNAL = QtCore.SIGNAL
nullvariant = QtCore.QVariant()
# XXX make this better than a poor hard written list...
COLORS = [ "blue", "darkgreen", "red", "green", "darkblue", "purple",
"cyan", QtCore.Qt.darkYellow, "magenta", "darkred", "darkmagenta",
"darkcyan", "gray", "yellow", ]
COLORS = [str(QtGui.QColor(x).name()) for x in COLORS]
#COLORS = [str(color) for color in QtGui.QColor.colorNames()]
def cvrt_date(date):
"""
Convert a date given the hg way, ie. couple (date, tz), into a
formatted QString
"""
date, tzdelay = date
return QtCore.QDateTime.fromTime_t(int(date)).toString(QtCore.Qt.LocaleDate)
# XXX maybe it's time to make these methods of the model...
# in following lambdas, ctx is a hg changectx
_columnmap = {'ID': lambda model, ctx, gnode: ctx.rev() is not None and str(ctx.rev()) or "",
'Log': getlog,
'Author': lambda model, ctx, gnode: tounicode(ctx.user()),
'Date': lambda model, ctx, gnode: cvrt_date(ctx.date()),
'Tags': gettags,
'Branch': lambda model, ctx, gnode: ctx.branch(),
'Filename': lambda model, ctx, gnode: gnode.extra[0],
}
_tooltips = {'ID': lambda model, ctx, gnode: ctx.rev() is not None and ctx.hex() or "Working Directory",
}
def auth_width(model, repo):
auths = model._aliases.values()
if not auths:
return None
return sorted(auths, cmp=lambda x,y: cmp(len(x), len(y)))[-1]
# in following lambdas, r is a hg repo
_maxwidth = {'ID': lambda self, r: str(len(r.changelog)),
'Date': lambda self, r: cvrt_date(r.changectx(0).date()),
'Tags': lambda self, r: sorted(r.tags().keys(),
key=lambda x: len(x))[-1][:10],
'Branch': lambda self, r: sorted(r.branchtags().keys(),
key=lambda x: len(x))[-1]
if r.branchtags().keys() else None,
'Author': lambda self, r: 'author name',
'Filename': lambda self, r: self.filename,
}
def datacached(meth):
"""
decorator used to cache 'data' method of Qt models. It will *not*
cache nullvariant return values (so costly non-null values
can be computed and filled as a background process)
"""
def data(self, index, role):
if not index.isValid():
return nullvariant
row = index.row()
col = index.column()
if (row, col, role) in self._datacache:
return self._datacache[(row, col, role)]
result = meth(self, index, role)
if result is not nullvariant:
self._datacache[(row, col, role)] = result
return result
return data
class HgRepoListModel(QtCore.QAbstractTableModel, HgRepoListWalker):
"""
Model used for displaying the revisions of a Hg *local* repository
"""
_allcolumns = ('ID', 'Branch', 'Log', 'Author', 'Date', 'Tags',)
_columns = ('ID', 'Branch', 'Log', 'Author', 'Date', 'Tags',)
_stretchs = {'Log': 1, }
_getcolumns = "getChangelogColumns"
def __init__(self, repo, branch='', fromhead=None, follow=False, parent=None):
"""
repo is a hg repo instance
"""
self._fill_timer = None
QtCore.QAbstractTableModel.__init__(self, parent)
HgRepoListWalker.__init__(self, repo, branch, fromhead, follow)
def setRepo(self, repo, branch='', fromhead=None, follow=False):
HgRepoListWalker.setRepo(self, repo, branch, fromhead, follow)
self.emit(SIGNAL('layoutChanged()'))
QtCore.QTimer.singleShot(0, Curry(self.emit, SIGNAL('filled')))
self._fill_timer = self.startTimer(50)
def timerEvent(self, event):
if event.timerId() == self._fill_timer:
self.emit(SIGNAL('showMessage'), 'filling (%s)'%(len(self.graph)))
if self.graph.isfilled():
self.killTimer(self._fill_timer)
self._fill_timer = None
self.emit(SIGNAL('showMessage'), '')
# we only fill the graph data strctures without telling
# views (until we atually did the full job), to keep
# maximal GUI reactivity
elif not self.graph.build_nodes(nnodes=self.fill_step):
self.killTimer(self._fill_timer)
self._fill_timer = None
self.updateRowCount()
self.emit(SIGNAL('showMessage'), '')
def updateRowCount(self):
currentlen = self.rowcount
newlen = len(self.graph)
if newlen > self.rowcount:
self.beginInsertRows(QtCore.QModelIndex(), currentlen, newlen-1)
self.rowcount = newlen
self.endInsertRows()
@staticmethod
def get_color(n, ignore=()):
"""
Return a color at index 'n' rotating in the available
colors. 'ignore' is a list of colors not to be chosen.
"""
ignore = [str(QtGui.QColor(x).name()) for x in ignore]
colors = [x for x in COLORS if x not in ignore]
if not colors: # ghh, no more available colors...
colors = COLORS
return colors[n % len(colors)]
def user_color(self, user):
if user in self._aliases:
user = self._aliases[user]
if user in self._users:
try:
color = self._users[user]['color']
color = QtGui.QColor(color).name()
self._user_colors[user] = color
except:
pass
return HgRepoListWalker.user_color(self, user)
def col2x(self, col):
return (1.2*self.dot_radius + 0) * col + self.dot_radius/2 + 3
@datacached
def data(self, index, role):
if not index.isValid():
return nullvariant
row = index.row()
self.ensureBuilt(row=row)
column = self._columns[index.column()]
gnode = self.graph[row]
ctx = self.repo.changectx(gnode.rev)
if role == QtCore.Qt.DisplayRole:
if column == 'Author': #author
return QtCore.QVariant(self.user_name(_columnmap[column](self, ctx, gnode)))
elif column == 'Log':
msg = _columnmap[column](self, ctx, gnode)
return QtCore.QVariant(msg)
return QtCore.QVariant(_columnmap[column](self, ctx, gnode))
elif role == QtCore.Qt.ToolTipRole:
msg = "<b>Branch:</b> %s<br>\n" % ctx.branch()
if gnode.rev in self.wd_revs:
msg += " <i>Working Directory position"
states = 'modified added removed deleted'.split()
status = self.wd_status[self.wd_revs.index(gnode.rev)]
status = [state for st, state in zip(status, states) if st]
if status:
msg += ' (%s)' % (', '.join(status))
msg += "</i><br>\n"
msg += _tooltips.get(column, _columnmap[column])(self, ctx, gnode)
return QtCore.QVariant(msg)
elif role == QtCore.Qt.ForegroundRole:
if column == 'Author': #author
return QtCore.QVariant(QtGui.QColor(self.user_color(ctx.user())))
if column == 'Branch': #branch
return QtCore.QVariant(QtGui.QColor(self.namedbranch_color(ctx.branch())))
elif role == QtCore.Qt.DecorationRole:
if column == 'Log':
radius = self.dot_radius
w = (gnode.cols)*(1*radius + 0) + 20
h = self.rowheight
dot_x = self.col2x(gnode.x) - radius / 2
dot_y = h / 2
pix = QtGui.QPixmap(w, h)
pix.fill(QtGui.QColor(0,0,0,0))
painter = QtGui.QPainter(pix)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
pen = QtGui.QPen(QtCore.Qt.blue)
pen.setWidth(2)
painter.setPen(pen)
lpen = QtGui.QPen(pen)
lpen.setColor(QtCore.Qt.black)
painter.setPen(lpen)
for y1, y2, lines in ((0, h, gnode.bottomlines),
(-h, 0, gnode.toplines)):
for start, end, color in lines:
lpen = QtGui.QPen(pen)
lpen.setColor(QtGui.QColor(self.get_color(color)))
lpen.setWidth(2)
painter.setPen(lpen)
x1 = self.col2x(start)
x2 = self.col2x(end)
painter.drawLine(x1, dot_y + y1, x2, dot_y + y2)
dot_color = QtGui.QColor(self.namedbranch_color(ctx.branch()))
dotcolor = QtGui.QColor(dot_color)
if gnode.rev in self.heads:
penradius = 2
pencolor = dotcolor.darker()
else:
penradius = 1
pencolor = QtCore.Qt.black
dot_y = (h/2) - radius / 2
painter.setBrush(dotcolor)
pen = QtGui.QPen(pencolor)
pen.setWidth(penradius)
painter.setPen(pen)
tags = set(ctx.tags())
icn = None
modified = False
atwd = False
if gnode.rev in self.wd_revs:
atwd = True
status = self.wd_status[self.wd_revs.index(gnode.rev)]
if [True for st in status if st]:
modified = True
if gnode.rev is None:
# WD is displayed only if there are local
# modifications, so let's use the modified icon
icn = geticon('modified')
elif tags.intersection(self.mqueues):
icn = geticon('mqpatch')
#elif modified:
# icn = geticon('modified')
elif atwd:
icn = geticon('clean')
if icn:
icn.paint(painter, dot_x-5, dot_y-5, 17, 17)
else:
painter.drawEllipse(dot_x, dot_y, radius, radius)
painter.end()
ret = QtCore.QVariant(pix)
return ret
return nullvariant
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant(self._columns[section])
return nullvariant
def maxWidthValueForColumn(self, column):
column = self._columns[column]
if column in _maxwidth:
return _maxwidth[column](self, self.repo)
return None
def clear(self):
"""empty the list"""
self.graph = None
self._datacache = {}
self.notify_data_changed()
def notify_data_changed(self):
self.emit(SIGNAL("layoutChanged()"))
class FileRevModel(HgRepoListModel):
"""
Model used to manage the list of revisions of a file, in file
viewer of in diff-file viewer dialogs.
"""
_allcolumns = ('ID', 'Branch', 'Log', 'Author', 'Date', 'Tags', 'Filename')
_columns = ('ID', 'Branch', 'Log', 'Author', 'Date', 'Filename')
_stretchs = {'Log': 1, }
_getcolumns = "getFilelogColumns"
def __init__(self, repo, filename=None, parent=None):
"""
data is a HgHLRepo instance
"""
HgRepoListModel.__init__(self, repo, parent=parent)
self.setFilename(filename)
def setRepo(self, repo, branch='', fromhead=None, follow=False):
self.repo = repo
self._datacache = {}
self.load_config()
def setFilename(self, filename):
self.filename = filename
self._user_colors = {}
self._branch_colors = {}
self.rowcount = 0
self._datacache = {}
if self.filename:
grapher = filelog_grapher(self.repo, self.filename)
self.graph = Graph(self.repo, grapher, self.max_file_size)
fl = self.repo.file(self.filename)
# we use fl.index here (instead of linkrev) cause
# linkrev API changed between 1.0 and 1.?. So this
# works with both versions.
self.heads = [fl.index[fl.rev(x)][4] for x in fl.heads()]
self.ensureBuilt(row=self.fill_step/2)
QtCore.QTimer.singleShot(0, Curry(self.emit, SIGNAL('filled')))
self._fill_timer = self.startTimer(500)
else:
self.graph = None
self.heads = []
replus = re.compile(r'^[+][^+].*', re.M)
reminus = re.compile(r'^[-][^-].*', re.M)
class HgFileListModel(QtCore.QAbstractTableModel):
"""
Model used for listing (modified) files of a given Hg revision
"""
def __init__(self, repo, parent=None):
"""
data is a HgHLRepo instance
"""
QtCore.QAbstractTableModel.__init__(self, parent)
self.repo = repo
self._datacache = {}
self.load_config()
self.current_ctx = None
self._files = []
self._filesdict = {}
self.diffwidth = 100
self._fulllist = False
self._fill_iter = None
def toggleFullFileList(self):
self._fulllist = not self._fulllist
self.loadFiles()
self.emit(SIGNAL('layoutChanged()'))
def load_config(self):
cfg = HgConfig(self.repo.ui)
self._flagcolor = {}
self._flagcolor['='] = cfg.getFileModifiedColor(default='blue')
self._flagcolor['-'] = cfg.getFileRemovedColor(default='red')
self._flagcolor['-'] = cfg.getFileDeletedColor(default='red')
self._flagcolor['+'] = cfg.getFileAddedColor(default='green')
self._displaydiff = cfg.getDisplayDiffStats()
def setDiffWidth(self, w):
if w != self.diffwidth:
self.diffwidth = w
self._datacache = {}
self.emit(SIGNAL('dataChanged(const QModelIndex &, const QModelIndex & )'),
self.index(1, 0),
self.index(1, self.rowCount()))
def __len__(self):
return len(self._files)
def rowCount(self, parent=None):
return len(self)
def columnCount(self, parent=None):
return 1 + self._displaydiff
def file(self, row):
return self._files[row]['path']
def fileflag(self, fn):
return self._filesdict[fn]['flag']
def fileparentctx(self, fn, ctx=None):
if ctx is None:
return self._filesdict[fn]['parent']
return ctx.parents()[0]
def fileFromIndex(self, index):
if not index.isValid() or index.row()>=len(self) or not self.current_ctx:
return None
row = index.row()
return self._files[row]['path']
def revFromIndex(self, index):
if self._fulllist and ismerge(self.current_ctx):
if not index.isValid() or index.row()>=len(self) or not self.current_ctx:
return None
row = index.row()
current_file_desc = self._files[row]
if current_file_desc['fromside'] == 'right':
return self.current_ctx.parents()[1].rev()
else:
return self.current_ctx.parents()[0].rev()
return None
def indexFromFile(self, filename):
if filename in self._filesdict:
row = self._files.index(self._filesdict[filename])
return self.index(row, 0)
return QtCore.QModelIndex()
def _filterFile(self, filename, ctxfiles):
if self._fulllist:
return True
return filename in ctxfiles #self.current_ctx.files()
def _buildDesc(self, parent, fromside):
_files = []
ctx = self.current_ctx
ctxfiles = ctx.files()
changes = self.repo.status(parent.node(), ctx.node())[:3]
modified, added, removed = changes
for lst, flag in ((added, '+'), (modified, '='), (removed, '-')):
for f in [x for x in lst if self._filterFile(x, ctxfiles)]:
_files.append({'path': f, 'flag': flag, 'desc': f,
'parent': parent, 'fromside': fromside,
'infiles': f in ctxfiles})
# renamed/copied files are handled by background
# filling process since it can be a bit long
for fdesc in _files:
bfile = isbfile(fdesc['path'])
fdesc['bfile'] = bfile
if bfile:
fdesc['desc'] = fdesc['desc'].replace('.hgbfiles'+os.sep, '')
return _files
def loadFiles(self):
self._fill_iter = None
self._files = []
self._datacache = {}
self._files = self._buildDesc(self.current_ctx.parents()[0], 'left')
if ismerge(self.current_ctx):
_paths = [x['path'] for x in self._files]
_files = self._buildDesc(self.current_ctx.parents()[1], 'right')
self._files += [x for x in _files if x['path'] not in _paths]
self._filesdict = dict([(f['path'], f) for f in self._files])
self.fillFileStats()
def setSelectedRev(self, ctx):
if ctx != self.current_ctx:
self.current_ctx = ctx
self._datacache = {}
self.loadFiles()
self.emit(SIGNAL("layoutChanged()"))
def fillFileStats(self):
"""
Method called to start the background process of computing
file stats, which are to be displayed in the 'Stats' column
"""
self._fill_iter = self._fill()
self._fill_one_step()
def _fill_one_step(self):
if self._fill_iter is None:
return
try:
nextfill = self._fill_iter.next()
if nextfill is not None:
row, col = nextfill
idx = self.index(row, col)
self.emit(SIGNAL('dataChanged(const QModelIndex &, const QModelIndex &)'),
idx, idx)
QtCore.QTimer.singleShot(10, lambda self=self: self._fill_one_step())
except StopIteration:
self._fill_iter = None
def _fill(self):
# the generator used to fill file stats as a background process
for row, desc in enumerate(self._files):
filename = desc['path']
if desc['flag'] == '=' and self._displaydiff:
diff = revdiff(self.repo, self.current_ctx, desc['parent'],
files=[filename])
try:
tot = self.current_ctx.filectx(filename).data().count('\n')
except LookupError:
tot = 0
add = len(replus.findall(diff))
rem = len(reminus.findall(diff))
if tot == 0:
tot = max(add + rem, 1)
desc['stats'] = (tot, add, rem)
yield row, 1
if desc['flag'] == '+':
m = self.current_ctx.filectx(filename).renamed()
if m:
removed = self.repo.status(desc['parent'].node(),
self.current_ctx.node())[2]
oldname, node = m
if oldname in removed:
# removed.remove(oldname) XXX
desc['renamedfrom'] = (oldname, node)
desc['flag'] = '='
desc['desc'] += '\n (was %s)' % oldname
else:
desc['copiedfrom'] = (oldname, node)
desc['flag'] = '='
desc['desc'] += '\n (copy of %s)' % oldname
yield row, 0
yield None
def data(self, index, role):
if not index.isValid() or index.row()>len(self) or not self.current_ctx:
return nullvariant
row = index.row()
column = index.column()
current_file_desc = self._files[row]
current_file = current_file_desc['path']
stats = current_file_desc.get('stats')
if column == 1:
if stats is not None:
if role == QtCore.Qt.DecorationRole:
tot, add, rem = stats
w = self.diffwidth - 20
h = 20
np = int(w*add/tot)
nm = int(w*rem/tot)
nd = w-np-nm
pix = QtGui.QPixmap(w+10, h)
pix.fill(QtGui.QColor(0,0,0,0))
painter = QtGui.QPainter(pix)
for x0,w0, color in ((0, nm, 'red'),
(nm, np, 'green'),
(nm+np, nd, 'gray')):
color = QtGui.QColor(color)
painter.setBrush(color)
painter.setPen(color)
painter.drawRect(x0+5, 0, w0, h-3)
painter.setBrush(QtGui.QColor(0,0,0,0))
pen = QtGui.QPen(QtCore.Qt.black)
pen.setWidth(0)
painter.setPen(pen)
painter.drawRect(5, 0, w+1, h-3)
painter.end()
return QtCore.QVariant(pix)
elif role == QtCore.Qt.ToolTipRole:
tot, add, rem = stats
msg = "Diff stats:<br>"
msg += " <b>File: </b>%s lines<br>" % tot
msg += " <b>added lines: </b> %s<br>" % add
msg += " <b>removed lines: </b> %s" % rem
return QtCore.QVariant(msg)
elif column == 0:
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.ToolTipRole):
return QtCore.QVariant(current_file_desc['desc'])
elif role == QtCore.Qt.DecorationRole:
if self._fulllist and ismerge(self.current_ctx):
if current_file_desc['infiles']:
icn = geticon('leftright')
elif current_file_desc['fromside'] == 'left':
icn = geticon('left')
elif current_file_desc['fromside'] == 'right':
icn = geticon('right')
return QtCore.QVariant(icn.pixmap(20,20))
elif role == QtCore.Qt.FontRole:
if self._fulllist and current_file_desc['infiles']:
font = QtGui.QFont()
font.setBold(True)
return QtCore.QVariant(font)
elif role == QtCore.Qt.ForegroundRole:
color = self._flagcolor.get(current_file_desc['flag'], 'black')
if color is not None:
return QtCore.QVariant(QtGui.QColor(color))
return nullvariant
def headerData(self, section, orientation, role):
if ismerge(self.current_ctx):
if self._fulllist:
header = ('File (all)', 'Diff')
else:
header = ('File (merged only)', 'Diff')
else:
header = ('File', 'Diff')
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant(header[section])
return nullvariant
class TreeItem(object):
def __init__(self, data, parent=None):
self.parentItem = parent
self.itemData = data
self.childItems = []
def appendChild(self, item):
self.childItems.append(item)
return item
addChild = appendChild
def child(self, row):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def columnCount(self):
return len(self.itemData)
def data(self, column):
return self.itemData[column]
def parent(self):
return self.parentItem
def row(self):
if self.parentItem:
return self.parentItem.childItems.index(self)
return 0
def __getitem__(self, idx):
return self.childItems[idx]
def __len__(self):
return len(self.childItems)
def __iter__(self):
for ch in self.childItems:
yield ch
class ManifestModel(QtCore.QAbstractItemModel):
"""
Qt model to display a hg manifest, ie. the tree of files at a
given revision. To be used with a QTreeView.
"""
def __init__(self, repo, rev, parent=None):
QtCore.QAbstractItemModel.__init__(self, parent)
self.repo = repo
self.changectx = self.repo.changectx(rev)
self.setupModelData()
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
if role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
item = index.internalPointer()
return QtCore.QVariant(item.data(index.column()))
def flags(self, index):
if not index.isValid():
return QtCore.Qt.ItemIsEnabled
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return QtCore.QVariant(self.rootItem.data(section))
return QtCore.QVariant()
def index(self, row, column, parent):
if row < 0 or column < 0 or row >= self.rowCount(parent) or column >= self.columnCount(parent):
return QtCore.QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
childItem = parentItem.child(row)
if childItem is not None:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent()
if parentItem == self.rootItem:
return QtCore.QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent):
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
return parentItem.childCount()
def columnCount(self, parent):
if parent.isValid():
return parent.internalPointer().columnCount()
else:
return self.rootItem.columnCount()
def setupModelData(self):
rootData = ["rev %s:%s" % (self.changectx.rev(),
short_hex(self.changectx.node()))]
self.rootItem = TreeItem(rootData)
for path in sorted(self.changectx.manifest()):
path = path.split(osp.sep)
node = self.rootItem
for p in path:
for ch in node:
if ch.data(0) == p:
node = ch
break
else:
node = node.addChild(TreeItem([p], node))
def pathFromIndex(self, index):
idxs = []
while index.isValid():
idxs.insert(0, index)
index = self.parent(index)
return osp.sep.join([index.internalPointer().data(0) for index in idxs])
if __name__ == "__main__":
from mercurial import ui, hg
from optparse import OptionParser
p = OptionParser()
p.add_option('-R', '--root', default='.',
dest='root',
help="Repository main directory")
p.add_option('-f', '--file', default=None,
dest='filename',
help="display the revision graph of this file (if not given, display the whole rev graph)")
opt, args = p.parse_args()
u = ui.ui()
repo = hg.repository(u, opt.root)
app = QtGui.QApplication(sys.argv)
if opt.filename is not None:
model = FileRevModel(repo, opt.filename)
else:
model = HgRepoListModel(repo)
view = QtGui.QTableView()
#delegate = GraphDelegate()
#view.setItemDelegateForColumn(1, delegate)
view.setShowGrid(False)
view.verticalHeader().hide()
view.verticalHeader().setDefaultSectionSize(20)
view.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
view.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
view.setModel(model)
view.setWindowTitle("Simple Hg List Model")
view.show()
view.setAlternatingRowColors(True)
#view.resizeColumnsToContents()
sys.exit(app.exec_())