def test_name_filter_glob(self):
     m = ManifestModel(self.repo, rev=0)
     m.setNameFilter('b*x')
     self.assertFalse(m.indexFromPath('bar').isValid())
     self.assertTrue(m.indexFromPath('baz/bax').isValid())
     self.assertTrue(m.indexFromPath('baz/box').isValid())
     self.assertFalse(m.indexFromPath('foo').isValid())
示例#2
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)
示例#3
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)