Changeset 47fd8085e741…
Parent 6875ddceb84d…
by
Changes to one file · Browse files at 47fd8085e741 Showing diff from parent 6875ddceb84d Diff from another changeset...
|
|
@@ -30,6 +30,7 @@
repo = hg.repository(ui.ui(), path=paths.find_root(root))
self.repo = repo
+ self.thread = None
reponame = hglib.get_reponame(repo)
self.setWindowTitle(_('Detect Copies/Renames in %s') % reponame)
@@ -39,19 +40,6 @@ layout = QVBoxLayout()
self.setLayout(layout)
- tophbox = QHBoxLayout()
- layout.addLayout(tophbox)
-
- lbl = QLabel(_('Minimum Simularity Percentage'))
- tophbox.addWidget(lbl)
-
- slider = QSlider(Qt.Horizontal)
- slider.setRange(0, 100)
- slider.setTickInterval(10)
- slider.setTickPosition(QSlider.TicksBelow)
- tophbox.addWidget(slider)
- lbl.setBuddy(slider)
-
# vsplit for top & diff
vsplit = QSplitter(Qt.Horizontal)
vsplit.restoreState(s.value('guess/vsplit-state').toByteArray())
@@ -71,13 +59,55 @@ utlbl = QLabel(_('<b>Unrevisioned Files</b>'))
utvbox.addWidget(utlbl)
self.unrevlist = QListWidget()
- self.unrevlist.setSelectionMode(QAbstractItemView.MultiSelection)
+ self.unrevlist.setSelectionMode(QAbstractItemView.ExtendedSelection)
utvbox.addWidget(self.unrevlist)
+ simhbox = QHBoxLayout()
+ utvbox.addLayout(simhbox)
+ lbl = QLabel()
+ slider = QSlider(Qt.Horizontal)
+ slider.setRange(0, 100)
+ slider.setTickInterval(10)
+ slider.setPageStep(10)
+ slider.setTickPosition(QSlider.TicksBelow)
+ slider.changefunc = lambda v: lbl.setText(
+ _('Min Simularity: %d%%') % v)
+ slider.valueChanged.connect(slider.changefunc)
+ slider.setValue(100)
+ self.simslider = slider
+ lbl.setBuddy(slider)
+ simhbox.addWidget(lbl)
+ simhbox.addWidget(slider, 1)
+
+ buthbox = QHBoxLayout()
+ utvbox.addLayout(buthbox)
+ copycheck = QCheckBox(_('Only consider deleted files'))
+ copycheck.setToolTip(_('Uncheck to consider all revisioned files'
+ ' for copy sources'))
+ copycheck.setChecked(True)
+ findrenames = QPushButton(_('Find Rename'))
+ findrenames.setToolTip(_('Find copy and/or rename sources'))
+ findrenames.clicked.connect(self.findRenames)
+ buthbox.addWidget(copycheck)
+ buthbox.addStretch(1)
+ buthbox.addWidget(findrenames)
+ self.findbtn, self.copycheck = findrenames, copycheck
+
matchlbl = QLabel(_('<b>Candidate Matches</b>'))
matchvbox.addWidget(matchlbl)
- token = QListWidget()
- matchvbox.addWidget(token)
+ self.matchlv = QTreeView()
+ self.matchlv.setItemsExpandable(False)
+ self.matchlv.setRootIsDecorated(False)
+ self.matchlv.setModel(MatchModel())
+ buthbox = QHBoxLayout()
+ matchbtn = QPushButton(_('Accept Selected Matches'))
+ matchbtn.clicked.connect(self.acceptMatch)
+ matchbtn.setEnabled(False)
+ self.matchbtn = matchbtn
+ buthbox.addStretch(1)
+ buthbox.addWidget(matchbtn)
+ matchvbox.addWidget(self.matchlv)
+ matchvbox.addLayout(buthbox)
diffframe = QFrame(hsplit)
diffvbox = QVBoxLayout()
@@ -104,58 +134,92 @@ wctx = self.repo[None]
wctx.status(unknown=True)
self.unrevlist.clear()
- self.unrevlcl = []
+ dests = []
for u in wctx.unknown():
- self.unrevlcl.append(u)
+ dests.append(u)
for a in wctx.added():
if not wctx[a].renamed():
- self.unrevlcl.append(a)
- for x in self.unrevlcl:
- self.unrevlist.addItem(hglib.tounicode(x))
+ dests.append(a)
+ for x in dests:
+ item = QListWidgetItem(hglib.tounicode(x))
+ item.orig = x
+ self.unrevlist.addItem(item)
+ self.difftb.clear()
- def findRenames(self, copy=False):
+ def findRenames(self):
'User pressed "find renames" button'
- # get selected rows from self.unrevlist
- # pass to search thread
+ if self.thread and self.thread.isRunning():
+ QMessageBox.information(self, _('Search already in progress'),
+ _('Cannot start a new search'))
+ return
+ ulist = []
+ for item in self.unrevlist.selectedItems():
+ ulist.append(item.orig)
+ if not ulist:
+ QMessageBox.information(self, _('No rows selected'),
+ _('Select one or more rows for search'))
+ return
- def findCopies(self):
- 'User pressed "find copies" button'
- # call rename function with simularity = 100%
- self.findRenames(copy=True)
+ def done():
+ for col in xrange(3):
+ self.matchlv.resizeColumnToContents(col)
+ self.findbtn.setEnabled(True)
+ self.matchbtn.setDisabled(model.isEmpty())
+
+ pct = self.simslider.value() / 100
+ copies = not self.copycheck.isChecked()
+ model = self.matchlv.model()
+ model.clear()
+ self.findbtn.setEnabled(False)
+ self.matchbtn.setEnabled(False)
+
+ self.thread = RenameSearchThread(self.repo, ulist, pct, copies)
+ self.thread.match.connect(model.appendRow)
+ #self.thread.error.connect(print)
+ #self.thread.progress.connect(print)
+ self.thread.searchComplete.connect(done)
+ self.thread.start()
def acceptMatch(self):
'User pressed "accept match" button'
hglib.invalidaterepo(self.repo)
- canmodel, upaths = self.cantree.get_selection().get_selected_rows()
- for path in upaths:
- row = canmodel[path]
- src, usrc, dest, udest, percent, sensitive = row
- if not sensitive:
- continue
+ sel = self.matchlv.selectionModel()
+ for index in sel.selectedIndexes():
+ src, dest, percent = self.matchlv.model().getRow(index)
if not os.path.exists(self.repo.wjoin(src)):
# Mark missing rename source as removed
self.repo.remove([src])
self.repo.copy(src, dest)
shlib.shell_notify([self.repo.wjoin(src), self.repo.wjoin(dest)])
# Mark all rows with this target file as non-sensitive
- for row in canmodel:
- if row[2] == dest:
- row[5] = False
- # emit matchAccepted()
+ #for row in self.matchlv.model().getRows():
+ # if row[1] == dest:
+ # row[5] = False
+ self.matchAccepted.emit()
self.refresh()
def showDiff(self):
'User selected a row in the candidate tree'
hglib.invalidaterepo(self.repo)
- src, usrc, dest, udest, percent, sensitive = row
ctx = self.repo['.']
- aa = self.repo.wread(dest)
- rr = ctx.filectx(src).data()
- opts = mdiff.defaultopts
- difftext = mdiff.unidiff(rr, '', aa, '', src, dest, None, opts=opts)
- if not difftext:
- l = _('== %s and %s have identical contents ==\n\n') % (src, dest)
- # pass through qtlib.difflabel and htmlui
+ hu = htmlui.htmlui()
+ for index in self.matchlv.selectedIndexes():
+ row = self.matchlv.model().getRow(index)
+ src, dest, percent = self.matchlv.model().getRow(index)
+ aa = self.repo.wread(dest)
+ rr = ctx.filectx(src).data()
+ opts = mdiff.defaultopts
+ difftext = mdiff.unidiff(rr, '', aa, '', src,
+ dest, None, opts=opts)
+ if not difftext:
+ t = _('%s and %s have identical contents\n\n') % (usrc, udest)
+ hu.write(t, label='ui.error')
+ else:
+ for t, l in qtlib.difflabel(difftext.splitlines, True):
+ hu.write(t, label=l)
+ # for now, only show one at a time
+ break
+ self.difftb.setHtml(hu.getdata()[0])
def accept(self):
s = QSettings()
@@ -173,10 +237,84 @@ QDialog.reject(self)
+class MatchModel(QAbstractTableModel):
+ def __init__(self, parent=None):
+ QAbstractTableModel.__init__(self, parent)
+ self.rows = []
+ self.headers = (_('Source'), _('Dest'), _('% Match'))
+
+ def rowCount(self, parent):
+ return len(self.rows)
+
+ def columnCount(self, parent):
+ return len(self.headers)
+
+ def data(self, index, role):
+ if not index.isValid():
+ return QVariant()
+ if role == Qt.DisplayRole:
+ s = self.rows[index.row()][index.column()]
+ return QVariant(hglib.tounicode(s))
+ '''
+ elif role == Qt.TextColorRole:
+ src, dst, pct = self.rows[index.row()]
+ if pct == 1.0:
+ return QColor('green')
+ else:
+ return QColor('black')
+ elif role == Qt.ToolTipRole:
+ # explain what row means?
+ '''
+ return QVariant()
+
+ def headerData(self, col, orientation, role):
+ if role != Qt.DisplayRole or orientation != Qt.Horizontal:
+ return QVariant()
+ else:
+ return QVariant(self.headers[col])
+
+ def flags(self, index):
+ return Qt.ItemIsSelectable | Qt.ItemIsEnabled
+
+ # Custom methods
+
+ def getRow(self, index):
+ assert index.isValid()
+ return self.rows[index.row()]
+
+ def appendRow(self, *args):
+ self.beginInsertRows(QModelIndex(), len(self.rows), len(self.rows))
+ vals = [str(a) for a in args] # PyQt is upgrading to QString
+ self.rows.append(vals)
+ self.endInsertRows()
+ self.emit(SIGNAL("dataChanged()"))
+
+ def clear(self):
+ self.beginRemoveRows(QModelIndex(), 0, len(self.rows)-1)
+ self.rows = []
+ self.endRemoveRows()
+ self.emit(SIGNAL("dataChanged()"))
+
+ def sort(self, col, order):
+ self.emit(SIGNAL("layoutAboutToBeChanged()"))
+ if col == COL_PATH:
+ c = self.checked
+ self.rows.sort(lambda x, y: cmp(c[x[col]], c[y[col]]))
+ else:
+ self.rows.sort(lambda x, y: cmp(x[col], y[col]))
+ if order == Qt.DescendingOrder:
+ self.rows.reverse()
+ self.emit(SIGNAL("layoutChanged()"))
+ self.reset()
+
+ def isEmpty(self):
+ return not bool(self.rows)
+
class RenameSearchThread(QThread):
'''Background thread for searching repository history'''
- match = pyqtSignal()
- error = pyqtSignal()
+ match = pyqtSignal(str, str, str)
+ error = pyqtSignal(QString)
+ progress = pyqtSignal()
searchComplete = pyqtSignal()
def __init__(self, repo, ufiles, minpct, copies):
@@ -194,7 +332,7 @@ print e
self.searchComplete.emit()
- def search(repo):
+ def search(self, repo):
hglib.invalidaterepo(repo)
wctx = repo[None]
pctx = repo['.']
@@ -211,12 +349,12 @@ removed = sorted([fctx for fctx in removed if fctx.size() > 0])
for o, n in similar._findexactmatches(repo, added, removed):
old, new = o.path(), n.path()
- self.match.emit( [old, new, '100%'] )
+ self.match.emit(old, new, '100%')
if self.minpct < 1.0:
for o, n, s in similar._findsimilarmatches(repo, added, removed,
self.minpct):
old, new = o.path(), n.path()
- self.match.emit( [old, new, '%d%%' % (s*100)] )
+ self.match.emit(old, new, '%d%%' % (s*100))
def run(ui, *pats, **opts):
return DetectRenameDialog()
|
Loading...