PageRenderTime 57ms CodeModel.GetById 18ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 0ms

/tortoisehg/hgqt/qqueue.py

https://bitbucket.org/tortoisehg/hgtk/
Python | 356 lines | 337 code | 10 blank | 9 comment | 0 complexity | cd1fff9e7c671bb88069d413202e1711 MD5 | raw file
  1# qqueue.py - TortoiseHg dialog for managing multiple MQ patch queues
  2#
  3# Copyright 2011 Johan Samyn <johan.samyn@gmail.com>
  4#
  5# This software may be used and distributed according to the terms of the
  6# GNU General Public License version 2, incorporated herein by reference.
  7
  8import os
  9
 10from mercurial import ui as uimod
 11from mercurial import util
 12from hgext import mq
 13
 14from tortoisehg.hgqt.i18n import _
 15from tortoisehg.hgqt import thgrepo, qtlib, cmdui
 16from tortoisehg.util import paths, hglib
 17
 18from PyQt4.QtCore import *
 19from PyQt4.QtGui import *
 20
 21# TODO:
 22# - Renaming a non-active queue ? (Why is it hg doesn't allow this ?)
 23
 24class QQueueDialog(QDialog):
 25    """Dialog for managing multiple MQ patch queues"""
 26
 27    output = pyqtSignal(QString, QString)
 28    makeLogVisible = pyqtSignal(bool)
 29
 30    def __init__(self, repo, parent=None):
 31        super(QQueueDialog, self).__init__(parent)
 32
 33        self.setWindowIcon(qtlib.geticon('thg_logo'))
 34        self.setWindowTitle(_('Manage MQ patch queues'))
 35        self.setWindowFlags(self.windowFlags()
 36                            & ~Qt.WindowContextHelpButtonHint)
 37
 38        self.activequeue = ''
 39        self.repo = repo
 40        repo.repositoryChanged.connect(self.reload)
 41
 42        layout = QVBoxLayout()
 43        layout.setMargin(4)
 44        self.setLayout(layout)
 45
 46        hbr = QHBoxLayout()
 47        hbr.setMargin(2)
 48        layout.addLayout(hbr)
 49        rlbl = QLabel(_('Repository:'))
 50        hbr.addWidget(rlbl)
 51        rle = QLineEdit()
 52        hbr.addWidget(rle)
 53        rle.setFont(qtlib.getfont('fontlist').font())
 54        rle.setText(repo.displayname)
 55        rle.setReadOnly(True)
 56        rle.setFocusPolicy(Qt.NoFocus)
 57
 58        topsep = qtlib.LabeledSeparator('')
 59        layout.addWidget(topsep)
 60
 61        hbl = QHBoxLayout()
 62        hbl.setMargin(2)
 63        layout.addLayout(hbl)
 64
 65        qvb = QVBoxLayout()
 66        hbl.addLayout(qvb)
 67
 68        qlbl = QLabel(_('Patch queues:'))
 69        qvb.addWidget(qlbl)
 70        ql = QListWidget(self)
 71        qvb.addWidget(ql)
 72        ql.currentRowChanged.connect(self.updateUI)
 73
 74        vbb = QVBoxLayout()
 75        vbb.setMargin(2)
 76        qvb.addLayout(vbb)
 77
 78        hqbtntop = QHBoxLayout()
 79        vbb.addLayout(hqbtntop)
 80        hqbtnmid = QHBoxLayout()
 81        vbb.addLayout(hqbtnmid)
 82        hqbtnbot = QHBoxLayout()
 83        vbb.addLayout(hqbtnbot)
 84
 85        btrel = QPushButton(_('Reload'))
 86        btrel.clicked.connect(self.reload)
 87        hqbtntop.addWidget(btrel)
 88        btact = QPushButton(_('Activate'))
 89        btact.clicked.connect(self.qqueueActivate)
 90        hqbtntop.addWidget(btact)
 91        btadd = QPushButton(_('Add'))
 92        btadd.clicked.connect(self.qqueueAdd)
 93        hqbtnmid.addWidget(btadd)
 94        btren = QPushButton(_('Rename'))
 95        btren.clicked.connect(self.qqueueRename)
 96        hqbtnmid.addWidget(btren)
 97        btdel = QPushButton(_('Delete'))
 98        btdel.clicked.connect(self.qqueueDelete)
 99        hqbtnbot.addWidget(btdel)
100        btpur = QPushButton(_('Purge'))
101        btpur.clicked.connect(self.qqueuePurge)
102        hqbtnbot.addWidget(btpur)
103
104        pvb = QVBoxLayout()
105        hbl.addLayout(pvb)
106
107        plbl = QLabel(_('Patches:'))
108        pvb.addWidget(plbl)
109        pl = QListWidget(self)
110        pvb.addWidget(pl)
111
112        botsep = qtlib.LabeledSeparator('')
113        layout.addWidget(botsep)
114
115        cmdlist = cmdui.Runner()
116        cmdlist.output.connect(self.output)
117        cmdlist.makeLogVisible.connect(self.makeLogVisible)
118        cmd = cmdui.Runner()
119        cmd.output.connect(self.output)
120        cmd.makeLogVisible.connect(self.makeLogVisible)
121
122        BB = QDialogButtonBox
123        bb = QDialogButtonBox(BB.Close)
124        bb.button(BB.Close).clicked.connect(self.close)
125        layout.addWidget(bb)
126
127        self.setLayout(layout)
128        self.ql = ql
129        self.pl = pl
130        self.btrel = btrel
131        self.btact = btact
132        self.btadd = btadd
133        self.btren = btren
134        self.btdel = btdel
135        self.btpur = btpur
136        self.bb = bb
137        self.cmdlist = cmdlist
138        self.cmd = cmd
139
140        self.itemfont = None
141        self.itemfontbold = None
142        self._readsettings()
143        self.reload()
144        self.ql.setFocus()
145
146    def setButtonState(self, state):
147        if state:
148            if self.ql.currentRow() != -1:
149                q = hglib.fromunicode(self.ql.item(self.ql.currentRow()).text())
150                self.btact.setEnabled(q != self.activequeue)
151                self.btren.setEnabled(q == self.activequeue and q != 'patches')
152                self.btdel.setEnabled(q != 'patches')
153                self.btpur.setEnabled(q != 'patches')
154            else:
155                self.btact.setEnabled(False)
156                self.btren.setEnabled(False)
157                self.btdel.setEnabled(False)
158                self.btpur.setEnabled(False)
159            self.btrel.setEnabled(True)
160            self.btadd.setEnabled(True)
161            self.bb.setEnabled(True)
162        else:
163            self.btrel.setEnabled(False)
164            self.btact.setEnabled(False)
165            self.btadd.setEnabled(False)
166            self.btren.setEnabled(False)
167            self.btdel.setEnabled(False)
168            self.btpur.setEnabled(False)
169            self.bb.setEnabled(False)
170
171    @pyqtSlot()
172    def updateUI(self):
173        if self.ql.currentRow() != -1:
174            self.showPatchesForQueue()
175        self.setButtonState(True)
176        self.bb.setEnabled(True)
177
178    @pyqtSlot()
179    def reload(self):
180        def reloadFinished():
181            self.repo.decrementBusyCount()
182            output = self.cmdlist.core.rawoutput()
183            self.showQueues(output)
184            self.updateUI()
185        cmdline = ['qqueue', '--repository', self.repo.root, '--list']
186        self.cmdlist.commandFinished.connect(reloadFinished)
187        self.repo.incrementBusyCount()
188        self.cmdlist.run(cmdline)
189
190    # This seems to return the cached data as it was just before the last
191    # issued command. So I used the threaded method again.
192    # def reload(self):
193        # _ui = uimod.ui()
194        # _ui.pushbuffer()
195        # try:
196            # opts = {'list': True}
197            # mq.qqueue(_ui, self.repo, None, **opts)
198        # except (util.Abort, EnvironmentError), e:
199            # print e
200        # output = _ui.popbuffer()
201        # qtlib.InfoMsgBox('test', '<p>reload - output = %s</p>' % output)
202        # self.showQueues(output)
203
204    def showQueues(self, output):
205        queues = output.rstrip('\n').split('\n')
206        self.ql.clear()
207        self.pl.clear()
208        row_activeq = 0
209        for i, q in enumerate(queues):
210            item = QListWidgetItem(q)
211            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled
212                          | Qt.ItemIsDragEnabled)
213            self.ql.addItem(item)
214            if self.itemfont == None:
215                self.itemfont = item.font()
216                self.itemfontbold = self.itemfont
217                self.itemfontbold.setBold(True)
218            if 'active' in q:
219                row_activeq = i
220                self.activequeue = q[:-9]
221                item.setText(self.activequeue)
222                item.setFont(self.itemfontbold)
223        self.ql.setCurrentRow(row_activeq)
224
225    def showPatchesForQueue(self):
226        currow = self.ql.currentRow()
227        if currow == -1:
228            return
229        while currow > self.ql.count() - 1:
230            currow -= 1
231        q = hglib.fromunicode(self.ql.item(currow).text())
232        self.pl.clear()
233        patches = []
234        if q == self.activequeue:
235            patches = self.repo.mq.full_series
236        else:
237            if q == 'patches':
238                sf = '/.hg/patches/series'
239            else:
240                sf = '/.hg/patches-%s/series' % q
241            sf = self.repo.root + sf
242            if os.path.exists(sf):
243                f = open(sf, 'r')
244                try:
245                    patches = f.read().splitlines()
246                finally:
247                    f.close()
248        for p in patches:
249            item = QListWidgetItem(hglib.tounicode(p))
250            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
251            self.pl.addItem(item)
252        self.ql.setFocus()
253
254    @pyqtSlot()
255    def qqueueActivate(self):
256        q = hglib.fromunicode(self.ql.item(self.ql.currentRow()).text())
257        if q == self.activequeue:
258            return
259        if qtlib.QuestionMsgBox(_('Confirm patch queue switch'),
260                _('Do you really want to activate patch queue \'%s\' ?' % q),
261                parent=self, defaultbutton=QMessageBox.No):
262            opts = [q]
263            self.qqueueCommand(opts)
264
265    @pyqtSlot()
266    def qqueueAdd(self):
267        title = _('TortoiseHg Prompt')
268        # this is the only way I found to make that dialog wide enough :(
269        label = QString(_('New patch queue name') + (' ' * 30))
270        # WindowContextHelpButton still there :( after this ?
271        dlg = QInputDialog(self, Qt.WindowFlags()
272                              & ~Qt.WindowContextHelpButtonHint)
273        qname, ok = dlg.getText(self, title, label)
274        if qname and ok:
275            opts = ['--create', hglib.fromunicode(qname)]
276            self.qqueueCommand(opts)
277
278    @pyqtSlot()
279    def qqueueRename(self):
280        q = hglib.fromunicode(self.ql.item(self.ql.currentRow()).text())
281        if q == 'patches':
282            return
283        title = _('TortoiseHg Prompt')
284        # this is the only way I found to make that dialog wide enough :(
285        label = QString(_('Rename patch queue \'%s\' to' % q) + (' ' * 30))
286        # WindowContextHelpButton still there :( after this ?
287        dlg = QInputDialog(self, Qt.WindowFlags()
288                              & ~Qt.WindowContextHelpButtonHint)
289        newqname, ok = dlg.getText(self, title, label)
290        if newqname:
291            newqname = hglib.fromunicode(newqname)
292        if newqname and ok:
293            opts = ['--rename', newqname]
294            self.qqueueCommand(opts)
295
296    @pyqtSlot()
297    def qqueueDelete(self):
298        q = hglib.fromunicode(self.ql.item(self.ql.currentRow()).text())
299        if q == 'patches':
300            return
301        if qtlib.QuestionMsgBox(_('Confirm patch queue delete'),
302              _('Do you really want to delete patch queue \'%s\' ?'
303              % q), parent=self, defaultbutton=QMessageBox.No):
304            opts = ['--delete', q]
305            self.qqueueCommand(opts)
306
307    @pyqtSlot()
308    def qqueuePurge(self):
309        q = hglib.fromunicode(self.ql.item(self.ql.currentRow()).text())
310        if q == 'patches':
311            return
312        if qtlib.QuestionMsgBox(_('Confirm patch queue purge'),
313              _('<p>This will also erase de patchfiles on disk!</p>'
314                '<p>Do you really want to purge patch queue \'%s\' ?</p>'
315                % q), parent=self, defaultbutton=QMessageBox.No):
316            opts = ['--purge', q]
317            self.qqueueCommand(opts)
318
319    def qqueueCommand(self, opts):
320        self.setButtonState(False)
321        def qqcmdFinished():
322            self.repo.decrementBusyCount()
323            # This seems to cause excessive refreshes ?!
324            # See when using 'thgdbg qqueue' from the commandline.
325            # But when not used, the data are not reshown after a command.
326            # Is it ok to have 2 cmd threads in the same dialog ?
327            self.reload()
328            # self.updateUI()
329        self.cmd.commandFinished.connect(qqcmdFinished)
330        cmdline = ['qqueue', '--repository', self.repo.root] + opts
331        self.repo.incrementBusyCount()
332        self.cmd.run(cmdline)
333
334    def accept(self):
335        self._writesettings()
336        QDialog.accept(self)
337
338    def close(self, event):
339        self._writesettings()
340        QDialog.close(self)
341
342    def _readsettings(self):
343        s = QSettings()
344        self.restoreGeometry(s.value('qqueue/geom').toByteArray())
345
346    def _writesettings(self):
347        s = QSettings()
348        s.setValue('qqueue/geom', self.saveGeometry())
349
350def run(ui, *pats, **opts):
351    repo = thgrepo.repository(None, paths.find_root())
352    if hasattr(repo, 'mq'):
353        return QQueueDialog(repo)
354    else:
355        qtlib.ErrorMsgBox(_('TortoiseHg Error'),
356            _('Please enable the MQ extension first.'))