def _setupmodel(self): self._treemodel = ManifestModel( self._repo, self._rev, statusfilter=self._statusfilter.status(), parent=self) self._treemodel.setNameFilter(self.le.text()) oldmodel = self._treeview.model() oldselmodel = self._treeview.selectionModel() self._treeview.setModel(self._treemodel) if oldmodel: oldmodel.deleteLater() if oldselmodel: oldselmodel.deleteLater() selmodel = self._treeview.selectionModel() selmodel.currentChanged.connect(self._updatecontent) selmodel.currentChanged.connect(self._updateItemFileActions) selmodel.currentChanged.connect(self._emitPathChanged) self._statusfilter.statusChanged.connect( self._treemodel.setStatusFilter) self._statusfilter.statusChanged.connect(self._autoexpandtree) self._autoexpandtree()
def test_status_filter_change(self): m = ManifestModel(self.repo, rev=1) m.setStatusFilter('C') self.assertEqual(QModelIndex(), m.indexFromPath('bar')) # modified self.assertNotEqual(QModelIndex(), m.indexFromPath('foo')) # clean m.setStatusFilter('M') self.assertNotEqual(QModelIndex(), m.indexFromPath('bar')) # modified self.assertEqual(QModelIndex(), m.indexFromPath('foo')) # clean
def test_name_filter(self): m = ManifestModel(self.repo, rev=0) m.setNameFilter('ax') self.assertFalse(m.indexFromPath('bar').isValid()) self.assertTrue(m.indexFromPath('baz/bax').isValid()) self.assertFalse(m.indexFromPath('baz/box').isValid()) self.assertFalse(m.indexFromPath('foo').isValid())
def test_status_filter_multi(self): m = ManifestModel(self.repo, rev=1) m.setStatusFilter('MC') self.assertNotEqual(QModelIndex(), m.indexFromPath('bar')) # modified self.assertEqual(QModelIndex(), m.indexFromPath('zzz')) # added self.assertEqual(QModelIndex(), m.indexFromPath('baz/box')) # removed self.assertNotEqual(QModelIndex(), m.indexFromPath('foo')) # clean
def test_indexfrompath(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(QModelIndex(), m.indexFromPath('')) self.assertEqual(m.index(1, 0), m.indexFromPath('bar')) self.assertEqual(m.index(0, 0), m.indexFromPath('baz')) self.assertEqual(m.index(0, 0, m.index(0, 0)), m.indexFromPath('baz/bax'))
def _setupmodel(self): self._treemodel = ManifestModel(self._repo, self._rev, statusfilter=self._statusfilter.status(), parent=self) oldmodel = self._treeview.model() oldselmodel = self._treeview.selectionModel() self._treeview.setModel(self._treemodel) if oldmodel: oldmodel.deleteLater() if oldselmodel: oldselmodel.deleteLater() selmodel = self._treeview.selectionModel() selmodel.currentChanged.connect(self._updatecontent) selmodel.currentChanged.connect(self._emitPathChanged) self._statusfilter.statusChanged.connect(self._treemodel.setStatusFilter) self._statusfilter.statusChanged.connect(self._autoexpandtree) self._autoexpandtree()
def test_rowcount_invalid(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(0, m.rowCount(m.index(1, 0)))
def test_rowcount_subdirs(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(2, m.rowCount(m.index(0, 0)))
def test_status_role(self): m = ManifestModel(self.repo, rev=0) self.assertEqual('A', m.data(m.indexFromPath('foo'), role=ManifestModel.StatusRole)) m = ManifestModel(self.repo, rev=1) m.setStatusFilter('MARC') self.assertEqual('C', m.data(m.indexFromPath('foo'), role=ManifestModel.StatusRole)) self.assertEqual('R', m.data(m.indexFromPath('baz/box'), role=ManifestModel.StatusRole))
def test_indexfrompath_qstr(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(m.index(1, 0), m.indexFromPath(QString('bar')))
def test_fileicon_path_concat(self): m = ManifestModel(self.repo, rev=0) m.fileIcon(m.indexFromPath(_aloha_ja + '.txt')) # no unicode error
def test_indexfrompath(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(m.index(0, 0), m.indexFromPath(_aloha_ja))
def test_pathfromindex(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(_aloha_ja, m.filePath(m.index(0, 0)))
def test_data(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(_aloha_ja, m.data(m.index(0, 0)))
def test_pathfromindex(self): m = ManifestModel(self.repo, rev=0) self.assertEqual('', m.filePath(QModelIndex())) self.assertEqual('bar', m.filePath(m.index(1, 0))) self.assertEqual('baz', m.filePath(m.index(0, 0))) self.assertEqual('baz/bax', m.filePath(m.index(0, 0, m.index(0, 0))))
def test_data(self): m = ManifestModel(self.repo, rev=0) self.assertEqual('bar', m.data(m.index(1, 0))) self.assertEqual('baz', m.data(m.index(0, 0))) self.assertEqual('foo', m.data(m.index(2, 0)))
def test_data_subdir(self): m = ManifestModel(self.repo, rev=0) self.assertEqual('bax', m.data(m.index(0, 0, m.index(0, 0)))) self.assertEqual('box', m.data(m.index(1, 0, m.index(0, 0))))
def test_removed_should_be_listed(self): m = ManifestModel(self.repo, rev=1) m.setStatusFilter('MARC') self.assertTrue(m.indexFromPath('baz/box').isValid())
def test_data_inexistent(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(None, m.data(QModelIndex())) self.assertEqual(None, m.data(m.index(0, 0, m.index(1, 0))))
class ManifestWidget(QWidget, qtlib.TaskWidget): """Display file tree and contents at the specified revision""" revChanged = pyqtSignal(object) """Emitted (rev) when the current revision changed""" pathChanged = pyqtSignal(unicode) """Emitted (path) when the current file path changed""" showMessage = pyqtSignal(unicode) """Emitted when to show revision summary as a hint""" grepRequested = pyqtSignal(unicode, dict) """Emitted (pattern, opts) when user request to search changelog""" linkActivated = pyqtSignal(QString) """Emitted (path) when user clicks on link""" revsetFilterRequested = pyqtSignal(QString) """Ask the repowidget to change its revset filter""" runCustomCommandRequested = pyqtSignal(str, list) """Emitted when selects a custom tool on the context menu""" def canswitch(self): return False def __init__(self, repoagent, rev=None, parent=None): super(ManifestWidget, self).__init__(parent) self._repoagent = repoagent # TODO: replace by repoagent if setRepo(bundlerepo) can be removed self._repo = repoagent.rawRepo() self._rev = rev self._selectedrev = rev self._initwidget() self._initactions() self._setupmodel() self._setupfilterupdater() self._treeview.setCurrentIndex(self._treemodel.index(0, 0)) self.setRev(self._rev) def _initwidget(self): self.setLayout(QVBoxLayout()) self._splitter = QSplitter() self.layout().addWidget(self._splitter) self.layout().setContentsMargins(2, 2, 2, 2) navlayout = QVBoxLayout(spacing=0) navlayout.setContentsMargins(0, 0, 0, 0) self._toolbar = QToolBar() self._toolbar.setIconSize(QSize(16, 16)) self._toolbar.setStyleSheet(qtlib.tbstylesheet) self._treeview = QManifestTreeView(self, headerHidden=True, dragEnabled=True) self._treeview.setContextMenuPolicy(Qt.CustomContextMenu) self._treeview.customContextMenuRequested.connect(self.menuRequest) self._treeview.doubleClicked.connect(self.onDoubleClick) navlayout.addWidget(self._toolbar) navlayout.addWidget(self._treeview) navlayoutw = QWidget() navlayoutw.setLayout(navlayout) self._splitter.addWidget(navlayoutw) self._splitter.setStretchFactor(0, 1) vbox = QVBoxLayout(spacing=0) vbox.setMargin(0) self.revpanel = revpanel.RevPanelWidget(self._repo) self.revpanel.linkActivated.connect(self.linkActivated) vbox.addWidget(self.revpanel, 0) self._fileview = fileview.HgFileView(self._repoagent, self) vbox.addWidget(self._fileview, 0) w = QWidget() w.setLayout(vbox) self._splitter.addWidget(w) self._splitter.setStretchFactor(1, 3) self._fileview.revisionSelected.connect(self.setRev) self._fileview.linkActivated.connect(self.linkActivated) for name in ('showMessage', 'grepRequested'): getattr(self._fileview, name).connect(getattr(self, name)) def loadSettings(self, qs, prefix): prefix += '/manifest' self._fileview.loadSettings(qs, prefix + '/fileview') self._splitter.restoreState( qs.value(prefix + '/splitter').toByteArray()) expanded = qs.value(prefix + '/revpanel.expanded', False).toBool() self.revpanel.set_expanded(expanded) def saveSettings(self, qs, prefix): prefix += '/manifest' self._fileview.saveSettings(qs, prefix + '/fileview') qs.setValue(prefix + '/splitter', self._splitter.saveState()) qs.setValue(prefix + '/revpanel.expanded', self.revpanel.is_expanded()) def _initactions(self): self.le = QManifestLineEdit() #QLineEdit() if hasattr(self.le, 'setPlaceholderText'): # Qt >= 4.7 self.le.setPlaceholderText(_('### filter text ###')) else: lbl = QLabel(_('Filter:')) self._toolbar.addWidget(lbl) self.le.keypressed.connect(self._treeview.setFocus) self._treeview.topreached.connect(self.le.setFocus) self._toolbar.addWidget(self.le) self._statusfilter = status.StatusFilterButton(statustext='MASC', text=_('Status')) self._toolbar.addWidget(self._statusfilter) self._fileactions = filectxactions.FilectxActions(self._repo, self, rev=self._rev) self._fileactions.linkActivated.connect(self.linkActivated) self._fileactions.filterRequested.connect(self.revsetFilterRequested) self._fileactions.runCustomCommandRequested.connect( self.runCustomCommandRequested) self.addActions(self._fileactions.actions()) def showEvent(self, event): QWidget.showEvent(self, event) if self._selectedrev != self._rev: # If the selected revision is not the same as the current revision # we must "reload" the manifest contents with the selected revision self.setRev(self._selectedrev) #@pyqtSlot(QModelIndex) def onDoubleClick(self, index): itemissubrepo = (self._treemodel.fileStatus(index) == 'S') if itemissubrepo: self._fileactions.opensubrepo() elif not self._treemodel.isDir(index): if self._treemodel.fileStatus(index) in 'C?': self._fileactions.editfile() else: self._fileactions.vdiff() def menuRequest(self, point): selmodel = self._treeview.selectionModel() if not selmodel.selectedRows(): return point = self._treeview.viewport().mapToGlobal(point) contextmenu = self._fileactions.menu() if contextmenu: contextmenu.exec_(point) #@pyqtSlot(QModelIndex) def _updateItemFileActions(self, index): itemissubrepo = (self._treemodel.fileStatus(index) == 'S') itemisdir = self._treemodel.isDir(index) self._fileactions.setPaths([self.path], itemissubrepo=itemissubrepo, itemisdir=itemisdir) @property def toolbar(self): """Return toolbar for manifest widget""" return self._toolbar @pyqtSlot(unicode, bool, bool, bool) def find(self, pattern, icase=False, wrap=False, forward=True): return self._fileview.find(pattern, icase, wrap, forward) @pyqtSlot(unicode, bool) def highlightText(self, pattern, icase=False): self._fileview.highlightText(pattern, icase) def _setupmodel(self): self._treemodel = ManifestModel( self._repo, self._rev, statusfilter=self._statusfilter.status(), parent=self) self._treemodel.setNameFilter(self.le.text()) oldmodel = self._treeview.model() oldselmodel = self._treeview.selectionModel() self._treeview.setModel(self._treemodel) if oldmodel: oldmodel.deleteLater() if oldselmodel: oldselmodel.deleteLater() selmodel = self._treeview.selectionModel() selmodel.currentChanged.connect(self._updatecontent) selmodel.currentChanged.connect(self._updateItemFileActions) selmodel.currentChanged.connect(self._emitPathChanged) self._statusfilter.statusChanged.connect( self._treemodel.setStatusFilter) self._statusfilter.statusChanged.connect(self._autoexpandtree) self._autoexpandtree() def _setupfilterupdater(self): self._filterupdatetimer = QTimer(self, interval=200, singleShot=True) self.le.returnPressed.connect(self._treeview.expandAll) self.le.textChanged.connect(self._filterupdatetimer.start) self._filterupdatetimer.timeout.connect(self._applyFilter) @pyqtSlot() def _applyFilter(self): filtertext = self.le.text() self._treemodel.setNameFilter(filtertext) self._treeview.enablefilterpalette(filtertext) @pyqtSlot() def _autoexpandtree(self): """expand file tree if the number of the items isn't large""" if 'C' not in self._statusfilter.status(): self._treeview.expandAll() def reload(self): # TODO pass def setRepo(self, repo): self._repo = repo #self._fileview.setRepo(repo) self._fileview.repo = repo if len(repo) <= self._rev: self._rev = len(repo) - 1 self._setupmodel() self._fileactions.setRepo(repo) @property def rev(self): """Return current revision""" return self._rev def selectRev(self, rev): """ Select the revision that must be set when the dialog is shown again """ self._selectedrev = rev @pyqtSlot(int) @pyqtSlot(object) def setRev(self, rev): """Change revision to show""" self._selectedrev = rev self.revpanel.set_revision(rev) self.revpanel.update(repo=self._repo) if rev == self._rev: return self._rev = rev path = self.path self.revChanged.emit(rev) self._setupmodel() ctx = self._repo[rev] if path and hglib.fromunicode(path) in ctx: # recover file selection after reloading the model self.setPath(path) self._fileview.setContext(ctx) self._fileview.displayFile(self.path, self.status) self._fileactions.setRev(rev) @pyqtSlot(unicode, object) @pyqtSlot(unicode, object, int) def setSource(self, path, rev, line=None): """Change path and revision to show at once""" if self._rev != rev: self._rev = rev self._setupmodel() self._fileactions.setRev(rev) self.revChanged.emit(rev) if path != self.path: self.setPath(path) ctx = self._repo[rev] if hglib.fromunicode(self.path) in ctx: self._fileview.displayFile(path, self.status) else: self._fileview.clearDisplay() return if line: self._fileview.showLine(line - 1) @property def path(self): """Return currently selected path [unicode]""" return self._treemodel.filePath(self._treeview.currentIndex()) @property def status(self): """Return currently selected path""" return self._treemodel.fileStatus(self._treeview.currentIndex()) @pyqtSlot(unicode) def setPath(self, path): """Change path to show""" self._treeview.setCurrentIndex(self._treemodel.indexFromPath(path)) def displayFile(self): ctx, path = self._treemodel.fileSubrepoCtxFromPath(self.path) if ctx is None: ctx = self._repo[self._rev] else: ctx._repo.tabwidth = self._repo.tabwidth ctx._repo.maxdiff = self._repo.maxdiff self._fileview.setContext(ctx) self._fileview.displayFile(path, self.status) @pyqtSlot() def _updatecontent(self): self.displayFile() def setFileViewMode(self, mode): self._fileview.setMode(mode) @pyqtSlot() def _emitPathChanged(self): self.pathChanged.emit(self.path)
def test_isdir(self): m = ManifestModel(self.repo, rev=0) self.assertTrue(m.isDir(m.indexFromPath(''))) self.assertTrue(m.isDir(m.indexFromPath('baz'))) self.assertFalse(m.isDir(m.indexFromPath('foo')))
class ManifestWidget(QWidget, qtlib.TaskWidget): """Display file tree and contents at the specified revision""" revChanged = pyqtSignal(object) """Emitted (rev) when the current revision changed""" pathChanged = pyqtSignal(unicode) """Emitted (path) when the current file path changed""" showMessage = pyqtSignal(unicode) """Emitted when to show revision summary as a hint""" grepRequested = pyqtSignal(unicode, dict) """Emitted (pattern, opts) when user request to search changelog""" linkActivated = pyqtSignal(QString) """Emitted (path) when user clicks on link""" revsetFilterRequested = pyqtSignal(QString) """Ask the repowidget to change its revset filter""" runCustomCommandRequested = pyqtSignal(str, list) """Emitted when selects a custom tool on the context menu""" def canswitch(self): return False def __init__(self, repoagent, rev=None, parent=None): super(ManifestWidget, self).__init__(parent) self._repoagent = repoagent # TODO: replace by repoagent if setRepo(bundlerepo) can be removed self._repo = repoagent.rawRepo() self._rev = rev self._selectedrev = rev self._initwidget() self._initactions() self._setupmodel() self._setupfilterupdater() self._treeview.setCurrentIndex(self._treemodel.index(0, 0)) self.setRev(self._rev) def _initwidget(self): self.setLayout(QVBoxLayout()) self._splitter = QSplitter() self.layout().addWidget(self._splitter) self.layout().setContentsMargins(2, 2, 2, 2) navlayout = QVBoxLayout(spacing=0) navlayout.setContentsMargins(0, 0, 0, 0) self._toolbar = QToolBar() self._toolbar.setIconSize(QSize(16,16)) self._toolbar.setStyleSheet(qtlib.tbstylesheet) self._treeview = QManifestTreeView(self, headerHidden=True, dragEnabled=True) self._treeview.setContextMenuPolicy(Qt.CustomContextMenu) self._treeview.customContextMenuRequested.connect(self.menuRequest) self._treeview.doubleClicked.connect(self.onDoubleClick) navlayout.addWidget(self._toolbar) navlayout.addWidget(self._treeview) navlayoutw = QWidget() navlayoutw.setLayout(navlayout) self._splitter.addWidget(navlayoutw) self._splitter.setStretchFactor(0, 1) vbox = QVBoxLayout(spacing=0) vbox.setMargin(0) self.revpanel = revpanel.RevPanelWidget(self._repo) self.revpanel.linkActivated.connect(self.linkActivated) vbox.addWidget(self.revpanel, 0) self._fileview = fileview.HgFileView(self._repoagent, self) vbox.addWidget(self._fileview, 0) w = QWidget() w.setLayout(vbox) self._splitter.addWidget(w) self._splitter.setStretchFactor(1, 3) self._fileview.revisionSelected.connect(self.setRev) self._fileview.linkActivated.connect(self.linkActivated) for name in ('showMessage', 'grepRequested'): getattr(self._fileview, name).connect(getattr(self, name)) def loadSettings(self, qs, prefix): prefix += '/manifest' self._fileview.loadSettings(qs, prefix+'/fileview') self._splitter.restoreState(qs.value(prefix+'/splitter').toByteArray()) expanded = qs.value(prefix+'/revpanel.expanded', False).toBool() self.revpanel.set_expanded(expanded) def saveSettings(self, qs, prefix): prefix += '/manifest' self._fileview.saveSettings(qs, prefix+'/fileview') qs.setValue(prefix+'/splitter', self._splitter.saveState()) qs.setValue(prefix+'/revpanel.expanded', self.revpanel.is_expanded()) def _initactions(self): self.le = QManifestLineEdit() #QLineEdit() if hasattr(self.le, 'setPlaceholderText'): # Qt >= 4.7 self.le.setPlaceholderText(_('### filter text ###')) else: lbl = QLabel(_('Filter:')) self._toolbar.addWidget(lbl) self.le.keypressed.connect(self._treeview.setFocus) self._treeview.topreached.connect(self.le.setFocus) self._toolbar.addWidget(self.le) self._statusfilter = status.StatusFilterButton( statustext='MASC', text=_('Status')) self._toolbar.addWidget(self._statusfilter) self._fileactions = filectxactions.FilectxActions(self._repo, self, rev=self._rev) self._fileactions.linkActivated.connect(self.linkActivated) self._fileactions.filterRequested.connect(self.revsetFilterRequested) self._fileactions.runCustomCommandRequested.connect( self.runCustomCommandRequested) self.addActions(self._fileactions.actions()) def showEvent(self, event): QWidget.showEvent(self, event) if self._selectedrev != self._rev: # If the selected revision is not the same as the current revision # we must "reload" the manifest contents with the selected revision self.setRev(self._selectedrev) #@pyqtSlot(QModelIndex) def onDoubleClick(self, index): itemissubrepo = (self._treemodel.fileStatus(index) == 'S') if itemissubrepo: self._fileactions.opensubrepo() elif not self._treemodel.isDir(index): if self._treemodel.fileStatus(index) in 'C?': self._fileactions.editfile() else: self._fileactions.vdiff() def menuRequest(self, point): selmodel = self._treeview.selectionModel() if not selmodel.selectedRows(): return point = self._treeview.viewport().mapToGlobal(point) contextmenu = self._fileactions.menu() if contextmenu: contextmenu.exec_(point) #@pyqtSlot(QModelIndex) def _updateItemFileActions(self, index): itemissubrepo = (self._treemodel.fileStatus(index) == 'S') itemisdir = self._treemodel.isDir(index) self._fileactions.setPaths([self.path], itemissubrepo=itemissubrepo, itemisdir=itemisdir) @property def toolbar(self): """Return toolbar for manifest widget""" return self._toolbar @pyqtSlot(unicode, bool, bool, bool) def find(self, pattern, icase=False, wrap=False, forward=True): return self._fileview.find(pattern, icase, wrap, forward) @pyqtSlot(unicode, bool) def highlightText(self, pattern, icase=False): self._fileview.highlightText(pattern, icase) def _setupmodel(self): self._treemodel = ManifestModel(self._repo, self._rev, statusfilter=self._statusfilter.status(), parent=self) self._treemodel.setNameFilter(self.le.text()) oldmodel = self._treeview.model() oldselmodel = self._treeview.selectionModel() self._treeview.setModel(self._treemodel) if oldmodel: oldmodel.deleteLater() if oldselmodel: oldselmodel.deleteLater() selmodel = self._treeview.selectionModel() selmodel.currentChanged.connect(self._updatecontent) selmodel.currentChanged.connect(self._updateItemFileActions) selmodel.currentChanged.connect(self._emitPathChanged) self._statusfilter.statusChanged.connect(self._treemodel.setStatusFilter) self._statusfilter.statusChanged.connect(self._autoexpandtree) self._autoexpandtree() def _setupfilterupdater(self): self._filterupdatetimer = QTimer(self, interval=200, singleShot=True) self.le.returnPressed.connect(self._treeview.expandAll) self.le.textChanged.connect(self._filterupdatetimer.start) self._filterupdatetimer.timeout.connect(self._applyFilter) @pyqtSlot() def _applyFilter(self): filtertext = self.le.text() self._treemodel.setNameFilter(filtertext) self._treeview.enablefilterpalette(filtertext) @pyqtSlot() def _autoexpandtree(self): """expand file tree if the number of the items isn't large""" if 'C' not in self._statusfilter.status(): self._treeview.expandAll() def reload(self): # TODO pass def setRepo(self, repo): self._repo = repo #self._fileview.setRepo(repo) self._fileview.repo = repo if len(repo) <= self._rev: self._rev = len(repo)-1 self._setupmodel() self._fileactions.setRepo(repo) @property def rev(self): """Return current revision""" return self._rev def selectRev(self, rev): """ Select the revision that must be set when the dialog is shown again """ self._selectedrev = rev @pyqtSlot(int) @pyqtSlot(object) def setRev(self, rev): """Change revision to show""" self._selectedrev = rev self.revpanel.set_revision(rev) self.revpanel.update(repo = self._repo) if rev == self._rev: return self._rev = rev path = self.path self.revChanged.emit(rev) self._setupmodel() ctx = self._repo[rev] if path and hglib.fromunicode(path) in ctx: # recover file selection after reloading the model self.setPath(path) self._fileview.setContext(ctx) self._fileview.displayFile(self.path, self.status) self._fileactions.setRev(rev) @pyqtSlot(unicode, object) @pyqtSlot(unicode, object, int) def setSource(self, path, rev, line=None): """Change path and revision to show at once""" if self._rev != rev: self._rev = rev self._setupmodel() self._fileactions.setRev(rev) self.revChanged.emit(rev) if path != self.path: self.setPath(path) ctx = self._repo[rev] if hglib.fromunicode(self.path) in ctx: self._fileview.displayFile(path, self.status) else: self._fileview.clearDisplay() return if line: self._fileview.showLine(line - 1) @property def path(self): """Return currently selected path [unicode]""" return self._treemodel.filePath(self._treeview.currentIndex()) @property def status(self): """Return currently selected path""" return self._treemodel.fileStatus(self._treeview.currentIndex()) @pyqtSlot(unicode) def setPath(self, path): """Change path to show""" self._treeview.setCurrentIndex(self._treemodel.indexFromPath(path)) def displayFile(self): ctx, path = self._treemodel.fileSubrepoCtxFromPath(self.path) if ctx is None: ctx = self._repo[self._rev] else: ctx._repo.tabwidth = self._repo.tabwidth ctx._repo.maxdiff = self._repo.maxdiff self._fileview.setContext(ctx) self._fileview.displayFile(path, self.status) @pyqtSlot() def _updatecontent(self): self.displayFile() def setFileViewMode(self, mode): self._fileview.setMode(mode) @pyqtSlot() def _emitPathChanged(self): self.pathChanged.emit(self.path)
def test_rowcount(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(3, m.rowCount())
class ManifestWidget(QWidget, qtlib.TaskWidget): """Display file tree and contents at the specified revision""" revChanged = pyqtSignal(object) """Emitted (rev) when the current revision changed""" pathChanged = pyqtSignal(unicode) """Emitted (path) when the current file path changed""" showMessage = pyqtSignal(unicode) """Emitted when to show revision summary as a hint""" grepRequested = pyqtSignal(unicode, dict) """Emitted (pattern, opts) when user request to search changelog""" linkActivated = pyqtSignal(QString) """Emitted (path) when user clicks on link""" filecontextmenu = None subrepocontextmenu = None def canswitch(self): return False def __init__(self, repo, rev=None, parent=None): super(ManifestWidget, self).__init__(parent) self._repo = repo self._rev = rev self._selectedrev = rev self._diff_dialogs = {} self._nav_dialogs = {} self._initwidget() self._initactions() self._setupmodel() self._treeview.setCurrentIndex(self._treemodel.index(0, 0)) self.setRev(self._rev) def _initwidget(self): self.setLayout(QVBoxLayout()) self._splitter = QSplitter() self.layout().addWidget(self._splitter) self.layout().setContentsMargins(2, 2, 2, 2) navlayout = QVBoxLayout(spacing=0) navlayout.setContentsMargins(0, 0, 0, 0) self._toolbar = QToolBar() self._toolbar.setIconSize(QSize(16, 16)) self._toolbar.setStyleSheet(qtlib.tbstylesheet) self._treeview = QTreeView(self, headerHidden=True, dragEnabled=True) self._treeview.setContextMenuPolicy(Qt.CustomContextMenu) self._treeview.customContextMenuRequested.connect(self.menuRequest) self._treeview.doubleClicked.connect(self.onDoubleClick) navlayout.addWidget(self._toolbar) navlayout.addWidget(self._treeview) navlayoutw = QWidget() navlayoutw.setLayout(navlayout) self._splitter.addWidget(navlayoutw) self._splitter.setStretchFactor(0, 1) vbox = QVBoxLayout(spacing=0) vbox.setMargin(0) self.revpanel = revpanel.RevPanelWidget(self._repo) self.revpanel.linkActivated.connect(self.linkActivated) vbox.addWidget(self.revpanel, 0) self._fileview = fileview.HgFileView(self._repo, self) vbox.addWidget(self._fileview, 0) w = QWidget() w.setLayout(vbox) self._splitter.addWidget(w) self._splitter.setStretchFactor(1, 3) self._fileview.revisionSelected.connect(self.setRev) self._fileview.linkActivated.connect(self.linkActivated) for name in ("showMessage", "grepRequested"): getattr(self._fileview, name).connect(getattr(self, name)) def loadSettings(self, qs, prefix): prefix += "/manifest" self._fileview.loadSettings(qs, prefix + "/fileview") self._splitter.restoreState(qs.value(prefix + "/splitter").toByteArray()) expanded = qs.value(prefix + "/revpanel.expanded", False).toBool() self.revpanel.set_expanded(expanded) def saveSettings(self, qs, prefix): prefix += "/manifest" self._fileview.saveSettings(qs, prefix + "/fileview") qs.setValue(prefix + "/splitter", self._splitter.saveState()) qs.setValue(prefix + "/revpanel.expanded", self.revpanel.is_expanded()) def _initactions(self): self._statusfilter = status.StatusFilterButton(statustext="MASC", text=_("Status")) self._toolbar.addWidget(self._statusfilter) self._actions = {} for name, desc, icon, key, tip, cb in [ ( "navigate", _("File history"), "hg-log", "Shift+Return", _("Show the history of the selected file"), self.navigate, ), ( "diffnavigate", _("Compare file revisions"), "compare-files", None, _("Compare revisions of the selected file"), self.diffNavigate, ), ( "diff", _("Visual Diff"), "visualdiff", "Ctrl+D", _("View file changes in external diff tool"), self.vdiff, ), ( "ldiff", _("Visual Diff to Local"), "ldiff", "Shift+Ctrl+D", _("View changes to current in external diff tool"), self.vdifflocal, ), ( "edit", _("View at Revision"), "view-at-revision", "Alt+Ctrl+E", _("View file as it appeared at this revision"), self.editfile, ), ( "ledit", _("Edit Local"), "edit-file", "Shift+Ctrl+E", _("Edit current file in working copy"), self.editlocal, ), ( "revert", _("Revert to Revision"), "hg-revert", "Alt+Ctrl+T", _("Revert file(s) to contents at this revision"), self.revertfile, ), ( "opensubrepo", _("Open subrepository"), "thg-repository-open", "Alt+Ctrl+O", _("Open the selected subrepository"), self.opensubrepo, ), ( "explore", _("Explore subrepository"), "system-file-manager", "Alt+Ctrl+E", _("Open the selected subrepository in a file browser"), self.explore, ), ( "terminal", _("Open terminal in subrepository"), "utilities-terminal", "Alt+Ctrl+T", _("Open a shell terminal in the selected subrepository root"), self.terminal, ), ]: act = QAction(desc, self) if icon: act.setIcon(qtlib.getmenuicon(icon)) if key: act.setShortcut(key) if tip: act.setStatusTip(tip) if cb: act.triggered.connect(cb) self._actions[name] = act self.addAction(act) def navigate(self, filename=None): self._navigate(filename, FileLogDialog, self._nav_dialogs) def diffNavigate(self, filename=None): self._navigate(filename, FileDiffDialog, self._diff_dialogs) def vdiff(self): if self.path is None: return pats = [hglib.fromunicode(self.path)] opts = {"change": self.rev} dlg = visdiff.visualdiff(self._repo.ui, self._repo, pats, opts) if dlg: dlg.exec_() def vdifflocal(self): if self.path is None: return pats = [hglib.fromunicode(self.path)] assert type(self.rev) is int opts = {"rev": ["rev(%d)" % self.rev]} dlg = visdiff.visualdiff(self._repo.ui, self._repo, pats, opts) if dlg: dlg.exec_() def editfile(self): if self.path is None: return if self.rev is None: qtlib.editfiles(self._repo, [hglib.fromunicode(self.path)], parent=self) else: base, _ = visdiff.snapshot(self._repo, [hglib.fromunicode(self.path)], self._repo[self.rev]) files = [os.path.join(base, hglib.fromunicode(self.path))] qtlib.editfiles(self._repo, files, parent=self) def editlocal(self): if self.path is None: return qtlib.editfiles(self._repo, [hglib.fromunicode(self.path)], parent=self) def revertfile(self): if self.path is None: return rev = self.rev if rev is None: rev = self._repo["."].rev() dlg = revert.RevertDialog(self._repo, [hglib.fromunicode(self.path)], rev, self) dlg.exec_() def _navigate(self, filename, dlgclass, dlgdict): if not filename: filename = hglib.fromunicode(self.path) if filename not in dlgdict: repoviewer = self.window() if not isinstance(repoviewer, workbench.Workbench): repoviewer = None dlg = dlgclass(self._repo, filename, repoviewer) dlgdict[filename] = dlg ufname = hglib.tounicode(filename) dlg.setWindowTitle(_("Hg file log viewer - %s") % ufname) dlg = dlgdict[filename] dlg.goto(self.rev) dlg.show() dlg.raise_() dlg.activateWindow() def opensubrepo(self): path = self._repo.wjoin(hglib.fromunicode(self.path)) if os.path.isdir(path): self.linkActivated.emit(u"subrepo:" + hglib.tounicode(path)) else: QMessageBox.warning( self, _("Cannot open subrepository"), _("The selected subrepository does not exist on the working directory"), ) def explore(self): root = self._repo.wjoin(hglib.fromunicode(self.path)) if os.path.isdir(root): qtlib.openlocalurl(root) def terminal(self): root = self._repo.wjoin(hglib.fromunicode(self.path)) if os.path.isdir(root): qtlib.openshell(root, hglib.fromunicode(self.path)) def showEvent(self, event): QWidget.showEvent(self, event) if self._selectedrev != self._rev: # If the selected revision is not the same as the current revision # we must "reload" the manifest contents with the selected revision self.setRev(self._selectedrev) # @pyqtSlot(QModelIndex) def onDoubleClick(self, index): itemissubrepo = self._treemodel.fileStatus(index) == "S" if itemissubrepo: self.opensubrepo() else: self.vdiff() def menuRequest(self, point): selmodel = self._treeview.selectionModel() if not selmodel.selectedRows(): return point = self._treeview.viewport().mapToGlobal(point) currentindex = self._treeview.currentIndex() itemissubrepo = self._treemodel.fileStatus(currentindex) == "S" # Subrepos and regular items have different context menus if itemissubrepo: contextmenu = self.subrepocontextmenu actionlist = ["opensubrepo", "explore", "terminal"] else: contextmenu = self.filecontextmenu actionlist = ["diff", "ldiff", "edit", "ledit", "revert", "navigate", "diffnavigate"] if not contextmenu: contextmenu = QMenu(self) for act in actionlist: if act: contextmenu.addAction(self._actions[act]) else: contextmenu.addSeparator() if itemissubrepo: self.subrepocontextmenu = contextmenu else: self.filecontextmenu = contextmenu if actionlist: contextmenu.exec_(point) @property def toolbar(self): """Return toolbar for manifest widget""" return self._toolbar @pyqtSlot(unicode, bool, bool, bool) def find(self, pattern, icase=False, wrap=False, forward=True): return self._fileview.find(pattern, icase, wrap, forward) @pyqtSlot(unicode, bool) def highlightText(self, pattern, icase=False): self._fileview.highlightText(pattern, icase) def _setupmodel(self): self._treemodel = ManifestModel(self._repo, self._rev, statusfilter=self._statusfilter.status(), parent=self) oldmodel = self._treeview.model() oldselmodel = self._treeview.selectionModel() self._treeview.setModel(self._treemodel) if oldmodel: oldmodel.deleteLater() if oldselmodel: oldselmodel.deleteLater() selmodel = self._treeview.selectionModel() selmodel.currentChanged.connect(self._updatecontent) selmodel.currentChanged.connect(self._emitPathChanged) self._statusfilter.statusChanged.connect(self._treemodel.setStatusFilter) self._statusfilter.statusChanged.connect(self._autoexpandtree) self._autoexpandtree() @pyqtSlot() def _autoexpandtree(self): """expand file tree if the number of the items isn't large""" if "C" not in self._statusfilter.status(): self._treeview.expandAll() def reload(self): # TODO pass def setRepo(self, repo): self._repo = repo # self._fileview.setRepo(repo) self._fileview.repo = repo if len(repo) <= self._rev: self._rev = len(repo) - 1 self._setupmodel() @property def rev(self): """Return current revision""" return self._rev def selectRev(self, rev): """ Select the revision that must be set when the dialog is shown again """ self._selectedrev = rev @pyqtSlot(int) @pyqtSlot(object) def setRev(self, rev): """Change revision to show""" self._selectedrev = rev self.revpanel.set_revision(rev) self.revpanel.update(repo=self._repo) if rev == self._rev: return self._rev = rev path = self.path self.revChanged.emit(rev) self._setupmodel() ctx = self._repo[rev] if path and hglib.fromunicode(path) in ctx: # recover file selection after reloading the model self.setPath(path) self._fileview.setContext(ctx) self._fileview.displayFile(self.path, self.status) # update sensitivity of actions real = type(rev) is int self._actions["ldiff"].setEnabled(real) for act in ["diff", "edit"]: self._actions[act].setEnabled(real or rev is None) self._actions["revert"].setEnabled(real) @pyqtSlot(unicode, object) @pyqtSlot(unicode, object, int) def setSource(self, path, rev, line=None): """Change path and revision to show at once""" if self._rev != rev: self._rev = rev self._setupmodel() self.revChanged.emit(rev) if path != self.path: self.setPath(path) ctx = self._repo[rev] if hglib.fromunicode(self.path) in ctx: self._fileview.displayFile(path, self.status) if line: self._fileview.showLine(int(line) - 1) else: self._fileview.clearDisplay() @property def path(self): """Return currently selected path [unicode]""" return self._treemodel.filePath(self._treeview.currentIndex()) @property def status(self): """Return currently selected path""" return self._treemodel.fileStatus(self._treeview.currentIndex()) @pyqtSlot(unicode) def setPath(self, path): """Change path to show""" self._treeview.setCurrentIndex(self._treemodel.indexFromPath(path)) def displayFile(self): ctx, path = self._treemodel.fileSubrepoCtxFromPath(self.path) if ctx is None: ctx = self._repo[self._rev] else: ctx._repo.tabwidth = self._repo.tabwidth ctx._repo.maxdiff = self._repo.maxdiff self._fileview.setContext(ctx) self._fileview.displayFile(path, self.status) @pyqtSlot() def _updatecontent(self): self.displayFile() @pyqtSlot() def _emitPathChanged(self): self.pathChanged.emit(self.path)
def test_status_role_invalid(self): m = ManifestModel(self.repo, rev=0) self.assertEqual(None, m.data(QModelIndex(), role=ManifestModel.StatusRole))