Kiln » TortoiseHg » TortoiseHg
Clone URL:  
Pushed to one repository · View In Graph Contained in 2.1, 2.1.1, and 2.1.2

Merge with stable

Changeset 474a14363dee

Parents dbe7b9b55bf1

Parents ef48dc901ed8

by Adrian Buehlmann

Changes to 49 files · Browse files at 474a14363dee Showing diff from parent dbe7b9b55bf1 ef48dc901ed8 Diff from another changeset...

Change 1 of 1 Show Entire File .hgtags Stacked
 
51
52
53
 
 
 
51
52
53
54
55
@@ -51,3 +51,5 @@
 dc23fbf831944fdf894cade250161131c11c9ce4 1.9.2  36a2bd08a405438ec776ad506c71fe581b8ce0b5 1.9.3  81a60ce2dba7178e834ac46b0c996bee38380e77 2.0 +ee531052f78b11dafe1d8c7ab9cfb95b4854be38 2.0.1 +5333ccf1184864720c1d3690df94fa47df80bf02 2.0.2
Show Entire File i18n/​tortoisehg/​ca.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​cs.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​da.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​de.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​en_AU.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​en_GB.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​es.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​et.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​fa.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​fr.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​he.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​hr.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​hu.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​it.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​ja.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​ko.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​nb.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​nl.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​nn.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​oc.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​pl.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​pt.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​pt_BR.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​ru.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​sv.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​tr.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​uk.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​zh_CN.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
Show Entire File i18n/​tortoisehg/​zh_TW.po Stacked
This file's diff was not loaded because this changeset is very large. Load changes
 
304
305
306
 
307
308
309
 
313
314
315
 
 
316
317
318
319
 
320
321
322
 
330
331
332
333
 
334
335
336
 
304
305
306
307
308
309
310
 
314
315
316
317
318
319
320
321
 
322
323
324
325
 
333
334
335
 
336
337
338
339
@@ -304,6 +304,7 @@
  'Background thread for annotating a file at a revision'   def __init__(self, parent=None):   super(_AnnotateThread, self).__init__(parent) + self._threadid = None     @pyqtSlot(object)   def start(self, fctx): @@ -313,10 +314,12 @@
    @pyqtSlot()   def abort(self): + if self._threadid is None: + return   try:   thread2._async_raise(self._threadid, KeyboardInterrupt)   self.wait() - except (AttributeError, ValueError): + except ValueError:   pass     def run(self): @@ -330,7 +333,7 @@
  except KeyboardInterrupt:   pass   finally: - del self._threadid + self._threadid = None   del self._fctx    class AnnotateDialog(QMainWindow):
 
20
21
22
23
 
 
24
25
 
 
 
 
 
26
27
 
28
29
30
 
35
36
37
 
38
39
40
41
42
43
 
 
44
45
46
47
 
48
49
50
51
52
53
 
62
63
64
 
 
 
65
66
67
 
20
21
22
 
23
24
25
 
26
27
28
29
30
31
32
33
34
35
36
 
41
42
43
44
45
46
47
 
48
 
49
50
51
52
53
54
55
56
 
 
57
58
59
 
68
69
70
71
72
73
74
75
76
@@ -20,11 +20,17 @@
  def __init__(self, opts, parent=None):   super(BugReport, self).__init__(parent)   - self.text = self.gettext(opts) + layout = QVBoxLayout() + self.setLayout(layout)   - layout = QVBoxLayout() + lbl = QLabel(_('Please report this bug to our %s bug tracker %s') % + (u'<a href="http://bitbucket.org/tortoisehg/thg/wiki/BugReport">', + u'</a>')) + lbl.setOpenExternalLinks(True) + self.layout().addWidget(lbl)     tb = QTextBrowser() + self.text = self.gettext(opts)   tb.setHtml('<pre>' + Qt.escape(self.text) + '</pre>')   tb.setWordWrapMode(QTextOption.NoWrap)   layout.addWidget(tb) @@ -35,19 +41,19 @@
  bb.accepted.connect(self.accept)   bb.button(BB.Save).clicked.connect(self.save)   bb.button(BB.Ok).setDefault(True) + bb.addButton(_('Copy'), BB.HelpRole).clicked.connect(self.copyText)   bb.addButton(_('Quit'), BB.DestructiveRole).clicked.connect(qApp.quit)   layout.addWidget(bb)   - self.setLayout(layout)   self.setWindowTitle(_('TortoiseHg Bug Report')) - self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) + self.setWindowFlags(self.windowFlags() & \ + ~Qt.WindowContextHelpButtonHint)   self.resize(650, 400)   self._readsettings()     def gettext(self, opts): + # TODO: make this more uniformly unicode safe   text = '{{{\n#!python\n' # Wrap in Bitbucket wiki preformat markers - text += _('** Please report this bug to ' - 'http://bitbucket.org/tortoisehg/thg/issues\n')   text += '** Mercurial version (%s). TortoiseHg version (%s)\n' % (   hglib.hgversion, version.version())   text += '** Command: %s\n' % (hglib.tounicode(opts.get('cmd', 'N/A'))) @@ -62,6 +68,9 @@
  text += '\n}}}'   return text   + def copyText(self): + QApplication.clipboard().setText(self.text) +   def getarch(self):   text = '** Windows version: %s\n' % str(sys.getwindowsversion())   arch = 'unknown (failed to import win32api)'
 
8
9
10
11
 
12
13
14
 
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
 
128
129
130
131
 
132
133
134
 
8
9
10
 
11
12
13
14
 
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
 
133
134
135
 
136
137
138
139
@@ -8,7 +8,7 @@
 import cStringIO  import os   -from mercurial import hg, util, patch, commands +from mercurial import hg, util, patch, commands, cmdutil  from mercurial import match as matchmod, ui as uimod  from hgext import record   @@ -100,27 +100,32 @@
  self.refresh()     def runPatcher(self, fp, wfile, updatestate): - class warncapt(uimod.ui): + ui = self.repo.ui.copy() + class warncapt(ui.__class__):   def warn(self, msg, *args, **opts):   self.write(msg) + ui.__class__ = warncapt + + ok = True   repo = self.repo - ui = warncapt()   ui.pushbuffer() - strip, pfiles = 1, {} - ok = True + pfiles = {} + curdir = os.getcwd()   try: - patch.internalpatch(fp, ui, strip, repo.root, files=pfiles, - eolmode=None) + os.chdir(repo.root) + if patch.applydiff(ui, fp, pfiles) < 0: + ok = False + self.showMessage.emit(_('Patch failed to apply'))   except patch.PatchError, err:   ok = False   self.showMessage.emit(hglib.tounicode(str(err))) + os.chdir(curdir)   for line in ui.popbuffer().splitlines():   if line.endswith(wfile + '.rej'):   if qtlib.QuestionMsgBox(_('Manually resolve rejected chunks?'),   hglib.tounicode(line) + u'<br><br>' +   _('Edit patched file and rejects?'),   parent=self): - #wctxactions.edit(self, repo.ui, repo, [wfile, wfile+'.rej'])   from tortoisehg.hgqt import rejects   dlg = rejects.RejectsDialog(repo.wjoin(wfile), self)   if dlg.exec_() == QDialog.Accepted: @@ -128,7 +133,7 @@
  break   if updatestate and ok:   # Apply operations specified in git diff headers - hglib.updatedir(repo.ui, repo, pfiles) + cmdutil.updatedir(repo.ui, repo, pfiles)   return ok     def editCurrentFile(self):
 
332
333
334
335
 
336
337
 
 
 
 
338
339
340
 
332
333
334
 
335
336
337
338
339
340
341
342
343
344
@@ -332,9 +332,13 @@
    def _initfont(self):   tf = qtlib.getfont('fontoutputlog') - tf.changed.connect(lambda f: self.setFont(f)) + tf.changed.connect(self.forwardFont)   self.setFont(tf.font())   + @pyqtSlot(QFont) + def forwardFont(self, font): + self.setFont(font) +   def _initmarkers(self):   self._markers = {}   for l in ('ui.error', 'control'):
 
459
460
461
 
462
463
464
 
459
460
461
462
463
464
465
@@ -459,6 +459,7 @@
  self.updateRecentMessages()     def addUsernameToHistory(self, user): + user = hglib.tounicode(user)   if user in self.userhist:   self.userhist.remove(user)   self.userhist.insert(0, user)
 
8
9
10
11
 
12
13
14
 
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
 
 
 
 
 
 
 
 
 
 
 
 
337
338
339
 
353
354
355
 
356
357
358
 
405
406
407
 
408
409
410
 
421
422
423
 
424
425
426
427
428
429
430
431
432
433
434
435
436
 
 
 
 
 
 
 
 
 
437
 
438
439
440
 
441
442
 
443
444
445
446
447
448
449
 
450
451
452
453
454
455
456
 
 
457
458
459
460
 
 
 
461
462
463
464
465
466
 
 
 
 
 
 
 
 
 
467
468
469
 
589
590
591
 
 
 
 
 
 
 
 
 
592
593
594
595
 
596
597
598
 
8
9
10
 
11
12
13
14
 
322
323
324
 
 
 
 
 
 
 
 
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
 
357
358
359
360
361
362
363
 
410
411
412
413
414
415
416
 
427
428
429
430
431
432
433
434
435
 
 
 
 
436
437
438
 
439
440
441
442
443
444
445
446
447
448
449
450
451
 
452
453
 
454
455
456
457
458
459
460
 
461
462
463
464
465
466
 
 
467
468
469
470
 
 
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
 
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
 
626
627
628
629
@@ -8,7 +8,7 @@
 import os  import re   -from mercurial import ui, hg, error, commands, match, util +from mercurial import ui, hg, error, commands, match, util, subrepo    from tortoisehg.hgqt import htmlui, visdiff, qtlib, htmldelegate, thgrepo, cmdui  from tortoisehg.util import paths, hglib, thread2 @@ -322,18 +322,22 @@
  pass     def searchfinished(self): - count = self.tv.model().rowCount(None) - if not count: - self.showMessage.emit(_('No matches found')) - else: - self.showMessage.emit(_('%d matches found') % count) - for col in xrange(COL_TEXT): - self.tv.resizeColumnToContents(col) - self.tv.setSortingEnabled(True)   self.cancelbutton.setEnabled(False)   self.searchbutton.setEnabled(True)   self.regexple.setEnabled(True)   self.regexple.setFocus() + count = self.tv.model().rowCount(None) + if count: + for col in xrange(COL_TEXT): + self.tv.resizeColumnToContents(col) + self.tv.setSortingEnabled(True) + if self.thread.completed == False: + # do not overwrite error message on failure + pass + elif count: + self.showMessage.emit(_('%d matches found') % count) + else: + self.showMessage.emit(_('No matches found'))    class DataWrapper(object):   def __init__(self, data): @@ -353,6 +357,7 @@
  self.inc = inc   self.exc = exc   self.follow = follow + self.completed = False     def cancel(self):   if self.isRunning() and hasattr(self, 'thread_id'): @@ -405,6 +410,7 @@
  self.showMessage.emit(_('Interrupted'))   self.progress.emit(*cmdui.stopProgress(_('Searching')))   os.chdir(cwd) + self.completed = True    class CtxSearchThread(QThread):   '''Background thread for searching a changectx''' @@ -421,49 +427,65 @@
  self.exc = exc   self.once = once   self.canceled = False + self.completed = False     def cancel(self):   self.canceled = True     def run(self): - hu = htmlui.htmlui() - rev = self.ctx.rev() - # generate match function relative to repo root - matchfn = match.match(self.repo.root, '', [], self.inc, self.exc)   def badfn(f, msg):   e = hglib.tounicode("%s: %s" % (matchfn.rel(f), msg))   self.showMessage.emit(e) - matchfn.bad = badfn + self.hu = htmlui.htmlui() + try: + # generate match function relative to repo root + matchfn = match.match(self.repo.root, '', [], self.inc, self.exc) + matchfn.bad = badfn + self.searchRepo(self.ctx, '', matchfn) + self.completed = True + except Exception, e: + self.showMessage.emit(hglib.tounicode(str(e)))   + def searchRepo(self, ctx, prefix, matchfn):   topic = _('Searching')   unit = _('files') - total = len(self.ctx.manifest()) + total = len(ctx.manifest())   count = 0 - for wfile in self.ctx: # walk manifest + for wfile in ctx: # walk manifest   if self.canceled:   break   self.progress.emit(topic, count, wfile, unit, total)   count += 1   if not matchfn(wfile):   continue - data = self.ctx[wfile].data() # load file data + data = ctx[wfile].data() # load file data   if util.binary(data):   continue   for i, line in enumerate(data.splitlines()):   pos = 0   for m in self.regexp.finditer(line): # perform regexp - hu.write(line[pos:m.start()], label='ui.status') - hu.write(line[m.start():m.end()], label='grep.match') + self.hu.write(line[pos:m.start()], label='ui.status') + self.hu.write(line[m.start():m.end()], label='grep.match')   pos = m.end()   if pos: - hu.write(line[pos:], label='ui.status') - row = [wfile, i + 1, rev, None, hu.getdata()[0]] + self.hu.write(line[pos:], label='ui.status') + path = os.path.join(prefix, wfile) + row = [path, i + 1, ctx.rev(), None, self.hu.getdata()[0]]   w = DataWrapper(row)   self.matchedRow.emit(w)   if self.once:   break   self.progress.emit(topic, None, '', '', None)   + if ctx.rev() is None: + for s in ctx.substate: + if not matchfn(s): + continue + sub = ctx.sub(s) + if isinstance(sub, subrepo.hgsubrepo): + newprefix = os.path.join(prefix, s) + self.searchRepo(sub._repo[None], newprefix, lambda x: True) +    COL_PATH = 0  COL_LINE = 1 @@ -589,10 +611,19 @@
  continue   else:   seen.add(path) + if rev is None and path not in repo[None]: + abs = repo.wjoin(path) + root = paths.find_root(abs) + if root and abs.startswith(root): + path = abs[len(root)+1:] + else: + continue + else: + root = repo.root   dlg = annotate.AnnotateDialog(path, rev=rev, line=line,   pattern=pattern, parent=self,   searchwidget=self.parent(), - root=repo.root) + root=root)   dlg.show()     def onViewChangeset(self):
 
283
284
285
286
 
287
288
289
 
290
291
292
 
293
294
295
296
297
 
 
 
 
298
299
300
 
283
284
285
 
286
287
 
 
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
@@ -283,18 +283,22 @@
  w.setUtf8(True)   w.setReadOnly(True)   w.setMarginWidth(1, 0) # hide area for line numbers - lex = lexers.get_diff_lexer(self) + self.lexer = lex = lexers.get_diff_lexer(self)   fh = qtlib.getfont('fontdiff') - fh.changed.connect(lambda f: lex.setFont(f)) - # TODO: why cannot we connect directly, without lambda? + fh.changed.connect(self.forwardFont)   lex.setFont(fh.font())   w.setLexer(lex)   # TODO: better way to setup diff lexer +   initqsci(self._qui.preview_edit)     self._qui.main_tabs.currentChanged.connect(self._refreshpreviewtab)   self._refreshpreviewtab(self._qui.main_tabs.currentIndex())   + def forwardFont(self, font): + if self.lexer: + self.lexer.setFont(font) +   @pyqtSlot(int)   def _refreshpreviewtab(self, index):   """Generate preview text if current tab is preview"""
 
224
225
226
227
 
228
229
230
 
224
225
226
 
227
228
229
230
@@ -224,7 +224,7 @@
  self.repo.thginvalidate()   wctx = self.repo[None]   wctx.status(unknown=True) - except error.RepoError, e: + except (EnvironmentError, error.RepoError), e:   qtlib.WarningMsgBox(_('Unable to read repository status'),   uni(str(e)), parent=self)   except util.Abort, e:
 
176
177
178
179
 
 
180
181
182
 
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
 
176
177
178
 
179
180
181
182
183
 
406
407
408
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
410
411
@@ -176,7 +176,8 @@
  qs.setValue(prefix+'/splitter', self._splitter.saveState())     def _initactions(self): - self._statusfilter = _StatusFilterButton(statustext='MAC') + self._statusfilter = status.StatusFilterButton( + statustext='MAC', text=_('Status'))   self._toolbar.addWidget(self._statusfilter)     self._action_annotate_mode = QAction(_('Annotate'), self, checkable=True) @@ -405,51 +406,6 @@
  def _emitPathChanged(self):   self.pathChanged.emit(self.path)   -# TODO: share this menu with status widget? -class _StatusFilterButton(QToolButton): - """Button with drop-down menu for status filter""" - statusChanged = pyqtSignal(str) - - _TYPES = 'MARC' - - def __init__(self, statustext=_TYPES, parent=None, **kwargs): - if 'text' not in kwargs: - kwargs['text'] = _('Status') - super(_StatusFilterButton, self).__init__( - parent, popupMode=QToolButton.InstantPopup, - icon=qtlib.geticon('hg-status'), - toolButtonStyle=Qt.ToolButtonTextBesideIcon, **kwargs) - - self._initactions(statustext) - - def _initactions(self, text): - self._actions = {} - menu = QMenu(self) - for c in self._TYPES: - st = status.statusTypes[c] - a = menu.addAction('%s %s' % (c, st.name)) - a.setCheckable(True) - a.setChecked(c in text) - a.toggled.connect(self._update) - self._actions[c] = a - self.setMenu(menu) - - @pyqtSlot() - def _update(self): - self.statusChanged.emit(self.status()) - - def status(self): - """Return the text for status filter""" - return ''.join(c for c in self._TYPES - if self._actions[c].isChecked()) - - @pyqtSlot(str) - def setStatus(self, text): - """Set the status text""" - assert util.all(c in self._TYPES for c in text) - for c in self._TYPES: - self._actions[c].setChecked(c in text) -  class ManifestTaskWidget(ManifestWidget):   """Manifest widget designed for task tab"""  
 
172
173
174
175
 
176
177
178
 
172
173
174
 
175
176
177
178
@@ -172,7 +172,7 @@
  def _rootentry(self):   try:   return self.__rootentry - except AttributeError: + except (AttributeError, TypeError):   self._buildrootentry()   return self.__rootentry  
 
223
224
225
 
226
227
228
 
231
232
233
234
235
 
 
236
237
238
 
223
224
225
226
227
228
229
 
232
233
234
 
 
235
236
237
238
239
@@ -223,6 +223,7 @@
  return (fullsrc.upper() == fulldest.upper() and sys.platform == 'win32')     def compose_command(self, src, dest): + 'src and dest are expected to be in local encoding'   if self.copy_chk.isChecked():   cmdline = ['copy']   else: @@ -231,8 +232,8 @@
  cmdline.append('-v')   if self.isCaseFoldingOnWin():   cmdline.append('-A') - cmdline.append(hglib.fromunicode(src)) - cmdline.append(hglib.fromunicode(dest)) + cmdline.append(src) + cmdline.append(dest)   vcmdline = ' '.join(['hg'] + cmdline)   return (cmdline, vcmdline)  
 
509
510
511
 
512
513
514
 
515
516
 
 
 
 
 
 
 
 
517
518
519
 
521
522
523
 
524
525
526
 
509
510
511
512
513
514
 
515
516
 
517
518
519
520
521
522
523
524
525
526
527
 
529
530
531
532
533
534
535
@@ -509,11 +509,19 @@
    def getlog(self, ctx, gnode):   if ctx.rev() is None: + msg = None   if self.unicodestar:   # The Unicode symbol is a black star: - return u'\u2605 ' + _('Working Directory') + u' \u2605' + msg = u'\u2605 ' + _('Working Directory') + u' \u2605'   else: - return '*** ' + _('Working Directory') + ' ***' + msg = '*** ' + _('Working Directory') + ' ***' + + for pctx in ctx.parents(): + if pctx.node() not in self.repo._branchheads: + text = _('Not a head revision!') + msg += " " + qtlib.markup(text, fg='red', weight='bold') + + return msg     msg = ctx.longsummary()   @@ -521,6 +529,7 @@
  effects = qtlib.geteffect('log.unapplied_patch')   text = qtlib.applyeffects(' %s ' % ctx._patchname, effects)   # qtlib.markup(msg, fg=UNAPPLIED_PATCH_COLOR) + msg = qtlib.markup(msg)   return hglib.tounicode(text + ' ' + msg)     parts = []
 
72
73
74
 
 
 
 
 
75
76
77
 
72
73
74
75
76
77
78
79
80
81
82
@@ -72,6 +72,11 @@
  self.workbench.showMessage(self.msg)   super(RepoTreeView, self).mouseMoveEvent(event)   + def keyPressEvent(self, event): + if event.key() in (Qt.Key_Enter, Qt.Key_Return): + self.showFirstTabOrOpen() + else: + super(RepoTreeView, self).keyPressEvent(event)   def leaveEvent(self, event):   if self.msg != '':   self.workbench.showMessage('')
 
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
 
244
245
246
 
247
248
249
250
 
 
 
 
 
251
252
253
254
 
 
 
 
255
256
257
258
259
260
261
262
 
 
 
 
 
 
 
 
 
 
 
263
264
265
266
267
@@ -244,33 +244,24 @@
    key = '%s/column_widths/%s' % (self.cfgname, str(self.repo[0]))   s.setValue(key, col_widths) - s.setValue('%s/widget_width' % self.cfgname, self.viewport().width())     def resizeEvent(self, e):   # re-size columns the smart way: the column holding Description   # is re-sized according to the total widget size. - if e.oldSize().width() != e.size().width(): - key = '%s/widget_width' % self.cfgname - widget_width, ok = QSettings().value(key).toInt() - if not ok: - widget_width = 0 + if self.resized and e.oldSize().width() != e.size().width(): + model = self.model() + total_width = stretch_col = 0   - if self.resized: - model = self.model() - vp_width = self.viewport().width() - total_width = stretch_col = 0 + for c in range(model.columnCount(QModelIndex())): + if model._columns[c] in model._stretchs: + #save the description column + stretch_col = c + else: + #total the other widths + total_width += self.columnWidth(c)   - if vp_width != widget_width: - for c in range(model.columnCount(QModelIndex())): - if model._columns[c] in model._stretchs: - #save the description column - stretch_col = c - else: - #total the other widths - total_width += self.columnWidth(c) - - width = max(vp_width - total_width, 100) - self.setColumnWidth(stretch_col, width) + width = max(self.viewport().width() - total_width, 100) + self.setColumnWidth(stretch_col, width)     super(HgRepoView, self).resizeEvent(e)  
 
148
149
150
151
 
152
153
154
 
169
170
171
 
 
 
172
173
174
 
148
149
150
 
151
152
153
154
 
169
170
171
172
173
174
175
176
177
@@ -148,7 +148,7 @@
  self.message.setMinimumSize(QSize(0, 0))   f = getfont('fontcomment')   self.message.setFont(f.font()) - f.changed.connect(lambda font: self.message.setFont(font)) + f.changed.connect(self.forwardFont)     self.fileview = HgFileView(self.repo, self.message_splitter)   sp = SP(SP.Expanding, SP.Expanding) @@ -169,6 +169,9 @@
  self.filelist.fileRevSelected.connect(self.onFileRevSelected)   self.filelist.clearDisplay.connect(self.fileview.clearDisplay)   + def forwardFont(self, font): + self.message.setFont(font) +   def createActions(self):   def fileActivated():   idx = self.filelist.currentIndex()
 
104
105
106
107
108
109
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
112
113
 
146
147
148
149
150
151
 
 
 
 
 
152
153
154
155
156
157
158
159
160
 
 
161
162
163
 
728
729
730
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
731
732
733
 
104
105
106
 
 
 
 
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
 
159
160
161
 
 
 
162
163
164
165
166
167
 
 
 
 
 
 
 
 
168
169
170
171
172
 
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
@@ -104,10 +104,23 @@
  else:   lbl = QLabel(_('Filter:'))   hbox.addWidget(lbl) - pb = QPushButton(_('Status')) - hbox.addWidget(le) - hbox.addWidget(pb) - hbox.addWidget(tb) + + st = '' + for s in statusTypes: + val = statusTypes[s] + if self.opts[val.name]: + st = st + s + self.statusfilter = StatusFilterButton( + statustext=st, types=StatusType.preferredOrder) + + self.filelistToolbar = QToolBar(_('Status File List Toolbar')) + self.filelistToolbar.setIconSize(QSize(16,16)) + hbox.addWidget(self.filelistToolbar) + self.filelistToolbar.addWidget(le) + self.filelistToolbar.addSeparator() + self.filelistToolbar.addWidget(self.statusfilter) + self.filelistToolbar.addSeparator() + self.filelistToolbar.addWidget(self.refreshBtn)   tv = WctxFileTree(self.repo)   vbox.addLayout(hbox)   vbox.addWidget(tv) @@ -146,18 +159,14 @@
  tv.clicked.connect(self.onRowClicked)   le.textEdited.connect(self.setFilter)   - def statusTypeTrigger(isChecked): - txt = hglib.fromunicode(self.sender().text()) - self.opts[txt[2:]] = isChecked + def statusTypeTrigger(status): + status = str(status) + for s in statusTypes: + val = statusTypes[s] + self.opts[val.name] = s in status   self.refreshWctx() - menu = QMenu(self) - for stat in StatusType.preferredOrder: - val = statusTypes[stat] - a = menu.addAction('%s %s' % (stat, val.name)) - a.setCheckable(True) - a.setChecked(self.opts[val.name]) - a.triggered.connect(statusTypeTrigger) - pb.setMenu(menu) + self.statusfilter.statusChanged.connect(statusTypeTrigger) +   self.tv = tv   self.le = le   @@ -728,6 +737,52 @@
  'status.subrepo'),  }   + +class StatusFilterButton(QToolButton): + """Button with drop-down menu for status filter""" + statusChanged = pyqtSignal(str) + + def __init__(self, statustext, types=None, parent=None, **kwargs): + self._TYPES = 'MARC' + if types is not None: + self._TYPES = types + #if 'text' not in kwargs: + # kwargs['text'] = _('Status') + super(StatusFilterButton, self).__init__( + parent, popupMode=QToolButton.InstantPopup, + icon=qtlib.geticon('hg-status'), + toolButtonStyle=Qt.ToolButtonTextBesideIcon, **kwargs) + + self._initactions(statustext) + + def _initactions(self, text): + self._actions = {} + menu = QMenu(self) + for c in self._TYPES: + st = statusTypes[c] + a = menu.addAction('%s %s' % (c, st.name)) + a.setCheckable(True) + a.setChecked(c in text) + a.toggled.connect(self._update) + self._actions[c] = a + self.setMenu(menu) + + @pyqtSlot() + def _update(self): + self.statusChanged.emit(self.status()) + + def status(self): + """Return the text for status filter""" + return ''.join(c for c in self._TYPES + if self._actions[c].isChecked()) + + @pyqtSlot(str) + def setStatus(self, text): + """Set the status text""" + assert util.all(c in self._TYPES for c in text) + for c in self._TYPES: + self._actions[c].setChecked(c in text) +  class StatusDialog(QDialog):   'Standalone status browser'   def __init__(self, pats, opts, root=None, parent=None):
 
258
259
260
 
261
262
263
 
728
729
730
731
 
 
732
733
734
 
258
259
260
261
262
263
264
 
729
730
731
 
732
733
734
735
736
@@ -258,6 +258,7 @@
  self.setUrl(self.paths['default'])   self.curalias = 'default'   else: + self.setUrl('')   self.curalias = None     def refreshStatusTips(self): @@ -728,7 +729,8 @@
  def outputnodes(ret, data):   if ret == 0:   nodes = [n for n in data.splitlines() if len(n) == 40] - self.outgoingNodes.emit(nodes) + if nodes: + self.outgoingNodes.emit(nodes)   self.showMessage.emit(_('%d outgoing changesets to %s') %   (len(nodes), urlu))   elif ret == 1:
 
26
27
28
 
 
 
 
 
 
 
 
29
30
31
 
74
75
76
 
77
78
79
 
448
449
450
451
452
453
454
 
456
457
458
459
460
461
462
 
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 
82
83
84
85
86
87
88
 
457
458
459
 
460
461
462
 
464
465
466
 
467
468
469
@@ -26,6 +26,14 @@
 from PyQt4.QtCore import *  from PyQt4.QtGui import *   +class ThgTabBar(QTabBar): + def mouseReleaseEvent(self, event): + + if event.button() == Qt.MidButton: + self.tabCloseRequested.emit(self.tabAt(event.pos())) + + super(QTabBar, self).mouseReleaseEvent(event) +  class Workbench(QMainWindow):   """hg repository viewer/browser application"""   finished = pyqtSignal(int) @@ -74,6 +82,7 @@
  self.setWindowIcon(qtlib.geticon('hg-log'))     self.repoTabsWidget = tw = QTabWidget() + tw.setTabBar(ThgTabBar())   tw.setDocumentMode(True)   tw.setTabsClosable(True)   tw.setMovable(True) @@ -448,7 +457,6 @@
  index = self.repoTabsWidget.currentIndex()   if widget.closeRepoWidget():   self.repoTabsWidget.removeTab(index) - widget.deleteLater()   self.updateMenu()     def repoTabCloseRequested(self, index): @@ -456,7 +464,6 @@
  w = tw.widget(index)   if w and w.closeRepoWidget():   tw.removeTab(index) - w.deleteLater()   self.updateMenu()     def repoTabChanged(self, index=0):
 
194
195
196
197
 
198
199
200
 
194
195
196
 
197
198
199
200
@@ -194,7 +194,7 @@
  fp.seek(0)   return newini(fp)   else: - fp = open(path, 'rb') + fp = util.posixfile(path, 'rb')   try:   return newini(fp)   finally: