Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 1.9.2, 1.9.3, and 2.0

qqueue: add dialog for hg qqueue

Changeset c3b54ffe86ee

Parent b33c3ccf837b

by Johan Samyn

Changes to 3 files · Browse files at c3b54ffe86ee Showing diff from parent b33c3ccf837b Diff from another changeset...

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