[qt4] use new signal coding-style

The new API use the more intuitive signal.emit(arg) instead of emit(signal, arg) (also apply to other signal related method like connect.

related to:#137588
authorAlain Leufroy <alain@leufroy.fr>
changesetb375bb029652
branchdefault
phasedraft
hiddenyes
parent revision#e54a7775ff7b [qt4] use functools.partial instead Curry
child revision#33d70620c6da [qt4] change some toolbar names (closes #103375), #78c6daac5927 [qt] add open in editor action
files modified by this revision
hgviewlib/qt4/__init__.py
hgviewlib/qt4/blockmatcher.py
hgviewlib/qt4/helpviewer.py
hgviewlib/qt4/hgdialogmixin.py
hgviewlib/qt4/hgfiledialog.py
hgviewlib/qt4/hgfileview.py
hgviewlib/qt4/hgmanifestdialog.py
hgviewlib/qt4/hgrepomodel.py
hgviewlib/qt4/hgrepoview.py
hgviewlib/qt4/hgrepoviewer.py
hgviewlib/qt4/quickbar.py
# HG changeset patch
# User Alain Leufroy <alain@leufroy.fr>
# Date 1369786110 -7200
# Wed May 29 02:08:30 2013 +0200
# Node ID b375bb029652436c4e2a026d128ec4b1a6a31757
# Parent e54a7775ff7b775ca7166eb9af66defc14570914
[qt4] use new signal coding-style

The new API use the more intuitive ``signal.emit(arg)`` instead of
``emit(signal, arg)`` (also apply to other signal related method like
``connect``.

:related to: #137588

diff --git a/hgviewlib/qt4/__init__.py b/hgviewlib/qt4/__init__.py
@@ -50,13 +50,10 @@
1 
2  # load icons from resource and store them in a dict, no matter their
3  # extension (.svg or .png)
4  from PyQt4 import QtCore
5  from PyQt4 import QtGui, uic
6 -connect = QtCore.QObject.connect
7 -SIGNAL = QtCore.SIGNAL
8 -Qt = QtCore.Qt
9  import hgqv_rc
10 
11 
12  _icons = {}
13  def _load_icons():
diff --git a/hgviewlib/qt4/blockmatcher.py b/hgviewlib/qt4/blockmatcher.py
@@ -16,23 +16,29 @@
14 
15  """
16  Qt4 widgets to display diffs as blocks
17  """
18  import sys, os
19 +from functools import partial
20 
21  from PyQt4 import QtGui, QtCore
22 -from PyQt4.QtCore import Qt, SIGNAL
23 +from PyQt4.QtCore import Qt, pyqtSignal
24 
25  class BlockList(QtGui.QWidget):
26      """
27      A simple widget to be 'linked' to the scrollbar of a diff text
28      view.
29 
30      It represents diff blocks with colored rectangles, showing
31      currently viewed area by a semi-transparant rectangle sliding
32      above them.
33      """
34 +
35 +    value_changed = pyqtSignal(int)
36 +    range_changed = pyqtSignal(int, int)
37 +    page_step_changed = pyqtSignal(int)
38 +
39      def __init__(self, *args):
40          QtGui.QWidget.__init__(self, *args)
41          self._blocks = set()
42          self._minimum = 0
43          self._maximum = 100
@@ -55,37 +61,34 @@
44          self._blocks.add((typ, alo, ahi))
45 
46      def setMaximum(self, maximum):
47          self._maximum = maximum
48          self.update()
49 -        self.emit(SIGNAL('rangeChanged(int, int)'),
50 -                  self._minimum, self._maximum)
51 +        self.range_changed[int, int].emit(self._minimum, self._maximum)
52 
53      def setMinimum(self, minimum):
54          self._minimum = minimum
55          self.update()
56 -        self.emit(SIGNAL('rangeChanged(int, int)'),
57 -                  self._minimum, self._maximum)
58 +        self.range_changed[int, int].emit(self._minimum, self._maximum)
59 
60      def setRange(self, minimum, maximum):
61          self._minimum = minimum
62          self._maximum = maximum
63          self.update()
64 -        self.emit(SIGNAL('rangeChanged(int, int)'),
65 -                  self._minimum, self._maximum)
66 +        self.range_changed[int, int].emit(self._minimum, self._maximum)
67 
68      def setValue(self, val):
69          if val != self._value:
70              self._value = val
71              self.update()
72 -            self.emit(SIGNAL('valueChanged(int)'), val)
73 +            self.value_changed[int].emit(val)
74 
75      def setPageStep(self, pagestep):
76          if pagestep != self._pagestep:
77              self._pagestep = pagestep
78              self.update()
79 -            self.emit(SIGNAL('pageStepChanged(int)'), pagestep)
80 +            self.page_step_changed[int].emit(pagestep)
81 
82      def linkScrollBar(self, sbar):
83          """
84          Make the block list displayer be linked to the scrollbar
85          """
@@ -94,15 +97,17 @@
86          self.setMaximum(sbar.maximum())
87          self.setMinimum(sbar.minimum())
88          self.setPageStep(sbar.pageStep())
89          self.setValue(sbar.value())
90          self.setUpdatesEnabled(True)
91 -        self.connect(sbar, SIGNAL('valueChanged(int)'), self.setValue)
92 -        self.connect(sbar, SIGNAL('rangeChanged(int, int)'), self.setRange)
93 -        self.connect(self, SIGNAL('valueChanged(int)'), sbar.setValue)
94 -        self.connect(self, SIGNAL('rangeChanged(int, int)'), sbar.setRange)
95 -        self.connect(self, SIGNAL('pageStepChanged(int)'), sbar.setPageStep)
96 +        sbar.valueChanged[int].connect(self.setValue)
97 +        sbar.rangeChanged[int, int].connect(self.setRange)
98 +        # use partial to bypass the slot overload checking that fails with
99 +        # pyqt4 4.10.1 on debian
100 +        self.value_changed[int].connect(partial(sbar.setValue))
101 +        self.range_changed[int, int].connect(partial(sbar.setRange))
102 +        self.page_step_changed[int].connect(partial(sbar.setPageStep))
103 
104      def syncPageStep(self):
105          self.setPageStep(self._sbar.pageStep())
106 
107      def paintEvent(self, event):
@@ -129,10 +134,15 @@
108      displaying 2 versions of a same file (diff).
109 
110      It will show graphically matching diff blocks between the 2 text
111      areas.
112      """
113 +
114 +    value_changed = pyqtSignal([int], [int, str])
115 +    range_changed = pyqtSignal([int, int], [int, int, str])
116 +    page_step_changed = pyqtSignal([int], [int, str])
117 +
118      def __init__(self, *args):
119          QtGui.QWidget.__init__(self, *args)
120          self._blocks = set()
121          self._minimum = {'left': 0, 'right': 0}
122          self._maximum = {'left': 100, 'right': 100}
@@ -238,17 +248,17 @@
123              p.restore()
124 
125      def setMaximum(self, maximum, side):
126          self._maximum[side] = maximum
127          self.update()
128 -        self.emit(SIGNAL('rangeChanged(int, int, const QString &)'),
129 +        self.range_changed[int, int, str].emit(
130                    self._minimum[side], self._maximum[side], side)
131 
132      def setMinimum(self, minimum, side):
133          self._minimum[side] = minimum
134          self.update()
135 -        self.emit(SIGNAL('rangeChanged(int, int, const QString &)'),
136 +        self.range_changed[int, int, str].emit(
137                    self._minimum[side], self._maximum[side], side)
138 
139      def setRange(self, minimum, maximum, side=None):
140          if side is None:
141              if self.sender() == self._sbar['left']:
@@ -256,11 +266,11 @@
142              else:
143                  side = 'right'
144          self._minimum[side] = minimum
145          self._maximum[side] = maximum
146          self.update()
147 -        self.emit(SIGNAL('rangeChanged(int, int, const QString &)'),
148 +        self.range_changed[int, int, str].emit(
149                    self._minimum[side], self._maximum[side], side)
150 
151      def setValue(self, val, side=None):
152          if side is None:
153              if self.sender() == self._sbar['left']:
@@ -268,18 +278,17 @@
154              else:
155                  side = 'right'
156          if val != self._value[side]:
157              self._value[side] = val
158              self.update()
159 -            self.emit(SIGNAL('valueChanged(int, const QString &)'), val, side)
160 +            self.value_changed[int, str].emit(val, side)
161 
162      def setPageStep(self, pagestep, side):
163          if pagestep != self._pagestep[side]:
164              self._pagestep[side] = pagestep
165              self.update()
166 -            self.emit(SIGNAL('pageStepChanged(int, const QString &)'),
167 -                      pagestep, side)
168 +            self.page_step_changed[int, str].emit(pagestep, side)
169 
170      def syncPageStep(self):
171          for side in ['left', 'right']:
172              self.setPageStep(self._sbar[side].pageStep(), side)
173 
@@ -297,14 +306,14 @@
174          self.setMaximum(sb.maximum(), side)
175          self.setMinimum(sb.minimum(), side)
176          self.setPageStep(sb.pageStep(), side)
177          self.setValue(sb.value(), side)
178          self.setUpdatesEnabled(True)
179 -        self.connect(sb, SIGNAL('valueChanged(int)'), self.setValue)
180 -        self.connect(sb, SIGNAL('rangeChanged(int, int)'), self.setRange)
181 +        sb.valueChanged[int].connect(self.setValue)
182 +        sb.rangeChanged[int, int].connect(self.setRange)
183 
184 -        self.connect(self, SIGNAL('valueChanged(int, const QString &)'),
185 +        self.value_changed[int, str].connect(
186                       lambda v, s: side==s and sb.setValue(v))
187 -        self.connect(self, SIGNAL('rangeChanged(int, int, const QString )'),
188 +        self.range_changed[int, int, str].connect(
189                       lambda v1, v2, s: side==s and sb.setRange(v1, v2))
190 -        self.connect(self, SIGNAL('pageStepChanged(int, const QString )'),
191 +        self.page_step_changed[int, str].connect(
192                       lambda v, s: side==s and sb.setPageStep(v))
diff --git a/hgviewlib/qt4/helpviewer.py b/hgviewlib/qt4/helpviewer.py
@@ -17,12 +17,10 @@
193  from hgviewlib.qt4.hgdialogmixin import HgDialogMixin
194  from hgviewlib.hgviewhelp import help_msg, get_options_helpmsg
195 
196  Qt = QtCore.Qt
197  bold = QtGui.QFont.Bold
198 -connect = QtCore.QObject.connect
199 -SIGNAL = QtCore.SIGNAL
200 
201  try:
202      from docutils.core import publish_string
203  except:
204      def publish_string(s, *args, **kwargs):
diff --git a/hgviewlib/qt4/hgdialogmixin.py b/hgviewlib/qt4/hgdialogmixin.py
@@ -21,12 +21,10 @@
205  import os.path as osp
206  import sys
207 
208  from PyQt4 import QtCore
209  from PyQt4 import QtGui, uic
210 -connect = QtCore.QObject.connect
211 -SIGNAL = QtCore.SIGNAL
212  Qt = QtCore.Qt
213 
214  from hgviewlib.config import HgConfig
215  from hgviewlib.qt4 import should_rebuild
216 
@@ -74,22 +72,19 @@
217          # we explicitly create a QShortcut so we can disable it
218          # when a "helper context toolbar" is activated (which can be
219          # closed hitting the Esc shortcut)
220          self.esc_shortcut = QtGui.QShortcut(self)
221          self.esc_shortcut.setKey(Qt.Key_Escape)
222 -        connect(self.esc_shortcut, SIGNAL('activated()'),
223 -                self.maybeClose)
224 +        self.esc_shortcut.activated.connect(self.maybeClose)
225          self._quickbars = []
226 
227      def attachQuickBar(self, qbar):
228          qbar.setParent(self)
229          self._quickbars.append(qbar)
230 -        connect(qbar, SIGNAL('escShortcutDisabled(bool)'),
231 -                self.setShortcutsEnabled)
232 +        qbar.esc_shortcut_disabled[bool].connect(self.setShortcutsEnabled)
233          self.addToolBar(Qt.BottomToolBarArea, qbar)
234 -        connect(qbar, SIGNAL('visible'),
235 -                self.ensureOneQuickBar)
236 +        qbar.unhidden.connect(self.ensureOneQuickBar)
237 
238      def setShortcutsEnabled(self, enabled=True):
239          for sh in self.disab_shortcuts:
240              sh.setEnabled(enabled)
241 
diff --git a/hgviewlib/qt4/hgfiledialog.py b/hgviewlib/qt4/hgfiledialog.py
@@ -23,11 +23,11 @@
242  import difflib
243 
244  from mercurial import ui, hg, util
245 
246  from PyQt4 import QtGui, QtCore, Qsci
247 -from PyQt4.QtCore import Qt
248 +from PyQt4.QtCore import Qt, pyqtSignal
249 
250  from hgviewlib.application import (FileViewer as _FileViewer,
251                                     FileDiffViewer as _FileDiffViewer)
252  from hgviewlib.util import tounicode, rootpath
253 
@@ -36,13 +36,10 @@
254  from hgviewlib.qt4.hgrepomodel import FileRevModel
255  from hgviewlib.qt4.blockmatcher import BlockList, BlockMatch
256  from hgviewlib.qt4.lexers import get_lexer
257  from hgviewlib.qt4.quickbar import FindInGraphlogQuickBar
258 
259 -connect = QtCore.QObject.connect
260 -disconnect = QtCore.QObject.disconnect
261 -SIGNAL = QtCore.SIGNAL
262 
263  sides = ('left', 'right')
264  otherside = {'left': 'right', 'right': 'left'}
265 
266 
@@ -65,12 +62,11 @@
267          self.setupModels()
268 
269      def setRepoViewer(self, repoviewer=None):
270          self.repoviewer = repoviewer
271          if repoviewer:
272 -            connect(repoviewer, SIGNAL('finished(int)'),
273 -                    lambda x: self.setRepoViewer())
274 +            repoviewer.destroyed.connect(lambda x: self.setRepoViewer())
275 
276      def reload(self):
277          self.repo = util.build_repo(self.repo.ui, self.repo.root)
278          self.setupModels()
279 
@@ -87,12 +83,11 @@
280              lexer.setDefaultFont(self._font)
281              lexer.setFont(self._font)
282          self.lexer = lexer
283 
284      def modelFilled(self):
285 -        disconnect(self.filerevmodel, SIGNAL('filled'),
286 -                   self.modelFilled)
287 +        self.filerevmodel.filled.disconnect(self.modelFilled)
288          if isinstance(self._show_rev, int):
289              index = self.filerevmodel.indexFromRev(self._show_rev)
290              self._show_rev = None
291          else:
292              index = self.filerevmodel.index(0,0)
@@ -118,20 +113,20 @@
293      _uifile = 'fileviewer.ui'
294 
295      def setupViews(self):
296          self.textView.setFont(self._font)
297          self.setWindowTitle('hgview filelog: %s' % os.path.abspath(self.filename))
298 -        connect(self.textView, SIGNAL('showMessage'),
299 -                self.statusBar().showMessage)
300 +        self.textView.message_logged.connect(self.statusBar().showMessage)
301 
302      def setupToolbars(self):
303          self.find_toolbar = FindInGraphlogQuickBar(self)
304          self.find_toolbar.attachFileView(self.textView)
305 -        connect(self.find_toolbar, SIGNAL('revisionSelected'),
306 -                self.tableView_revisions.goto)
307 -        connect(self.find_toolbar, SIGNAL('showMessage'),
308 -                self.statusBar().showMessage)
309 +        self.find_toolbar.revision_selected.connect(
310 +            self.tableView_revisions.goto)
311 +        self.find_toolbar.revision_selected[int].connect(
312 +            self.tableView_revisions.goto)
313 +        self.find_toolbar.message_logged.connect(self.statusBar().showMessage)
314          self.attachQuickBar(self.find_toolbar)
315 
316          self.toolBar_edit.addSeparator()
317          self.toolBar_edit.addAction(self.tableView_revisions._actions['back'])
318          self.toolBar_edit.addAction(self.tableView_revisions._actions['forward'])
@@ -144,63 +139,58 @@
319          self.attachQuickBar(self.tableView_revisions.goto_toolbar)
320 
321      def setupModels(self):
322          self.filerevmodel = FileRevModel(self.repo)
323          self.tableView_revisions.setModel(self.filerevmodel)
324 -        connect(self.tableView_revisions,
325 -                SIGNAL('revisionSelected'),
326 -                self.revisionSelected)
327 -        connect(self.tableView_revisions,
328 -                SIGNAL('revisionActivated'),
329 +        self.tableView_revisions.revision_selected.connect(
330 +            self.revisionSelected)
331 +        self.tableView_revisions.revision_selected[int].connect(
332 +            self.revisionSelected)
333 +        self.tableView_revisions.revision_activated.connect(
334                  self.revisionActivated)
335 -        connect(self.filerevmodel, SIGNAL('showMessage'),
336 -                self.statusBar().showMessage,
337 -                Qt.QueuedConnection)
338 -        connect(self.filerevmodel, QtCore.SIGNAL('filled'),
339 -                self.modelFilled)
340 +        self.tableView_revisions.revision_activated[int].connect(
341 +                self.revisionActivated)
342 +        self.filerevmodel.message_logged.connect(
343 +            self.statusBar().showMessage,
344 +            Qt.QueuedConnection)
345 +        self.filerevmodel.filled.connect(self.modelFilled)
346          self.textView.setMode('file')
347          self.textView.setModel(self.filerevmodel)
348          self.find_toolbar.setModel(self.filerevmodel)
349          self.find_toolbar.setFilterFiles([self.filename])
350          self.find_toolbar.setMode('file')
351          self.filerevmodel.setFilename(self.filename)
352 
353      def createActions(self):
354 -        connect(self.actionClose, SIGNAL('triggered()'),
355 -                self.close)
356 -        connect(self.actionReload, SIGNAL('triggered()'),
357 -                self.reload)
358 +        self.actionClose.triggered.connect(self.close)
359 +        self.actionReload.triggered.connect(self.reload)
360          self.actionClose.setIcon(geticon('quit'))
361          self.actionReload.setIcon(geticon('reload'))
362 
363          self.actionDiffMode = QtGui.QAction('Diff mode', self)
364          self.actionDiffMode.setCheckable(True)
365 -        connect(self.actionDiffMode, SIGNAL('toggled(bool)'),
366 -                self.setMode)
367 +        self.actionDiffMode.toggled[bool].connect(self.setMode)
368 
369          self.actionAnnMode = QtGui.QAction('Annotate', self)
370          self.actionAnnMode.setCheckable(True)
371 -        connect(self.actionAnnMode, SIGNAL('toggled(bool)'),
372 -                self.textView.setAnnotate)
373 +        self.actionAnnMode.toggled[bool].connect(self.textView.setAnnotate)
374 
375          self.actionNextDiff = QtGui.QAction(geticon('down'), 'Next diff', self)
376          self.actionNextDiff.setShortcut('Alt+Down')
377          self.actionPrevDiff = QtGui.QAction(geticon('up'), 'Previous diff', self)
378          self.actionPrevDiff.setShortcut('Alt+Up')
379 -        connect(self.actionNextDiff, SIGNAL('triggered()'),
380 -                self.nextDiff)
381 -        connect(self.actionPrevDiff, SIGNAL('triggered()'),
382 -                self.prevDiff)
383 +        self.actionNextDifftriggered.connect(self.nextDiff)
384 +        self.actionPrevDiff.triggered.connect(self.prevDiff)
385 
386      def revisionSelected(self, rev):
387          pos = self.textView.verticalScrollBar().value()
388          ctx = self.filerevmodel.repo.changectx(rev)
389          self.textView.setContext(ctx)
390          self.textView.displayFile(self.filerevmodel.graph.filename(rev))
391          self.textView.verticalScrollBar().setValue(pos)
392          self.actionPrevDiff.setEnabled(False)
393 -        connect(self.textView, SIGNAL('filled'),
394 +        self.textView.filled.connect(
395                  lambda self=self: self.actionNextDiff.setEnabled(self.textView.fileMode() and self.textView.nDiffs()))
396 
397      def goto(self, rev):
398          index = self.filerevmodel.indexFromRev(rev)
399          if index is not None:
@@ -229,10 +219,12 @@
400      """
401      Qt4 dialog to display diffs between different mercurial revisions of a file.
402      """
403      _uifile = 'filediffviewer.ui'
404 
405 +    diff_filled = pyqtSignal()
406 +
407      def setupViews(self):
408          self.tableView_revisions = self.tableView_revisions_left
409          self.tableViews = {'left': self.tableView_revisions_left,
410                             'right': self.tableView_revisions_right}
411          # viewers are Scintilla editors
@@ -287,64 +279,58 @@
412 
413          for side in sides:
414              table = getattr(self, 'tableView_revisions_%s' % side)
415              table.setTabKeyNavigation(False)
416              #table.installEventFilter(self)
417 -            connect(table, SIGNAL('revisionSelected'), self.revisionSelected)
418 -            connect(table, SIGNAL('revisionActivated'), self.revisionActivated)
419 +            table.revision_selected.connect(self.revisionSelected)
420 +            table.revision_selected[int].connect(self.revisionSelected)
421 +            table.revision_activated.connect(self.revisionActivated)
422 +            table.revision_activated[int].connect(self.revisionActivated)
423 
424 -            connect(self.viewers[side].verticalScrollBar(),
425 -                    QtCore.SIGNAL('valueChanged(int)'),
426 +            self.viewers[side].verticalScrollBar().valueChanged[int].connect(
427                      lambda value, side=side: self.vbar_changed(value, side))
428              self.attachQuickBar(table.goto_toolbar)
429 
430          self.setTabOrder(table, self.viewers['left'])
431          self.setTabOrder(self.viewers['left'], self.viewers['right'])
432          self.setWindowTitle('hgview diff: %s' % os.path.abspath(self.filename))
433 
434          # timer used to fill viewers with diff block markers during GUI idle time
435          self.timer = QtCore.QTimer()
436          self.timer.setSingleShot(False)
437 -        connect(self.timer, SIGNAL("timeout()"),
438 -                self.idle_fill_files)
439 +        self.timer.timeout.connect(self.idle_fill_files)
440 
441      def setupModels(self):
442          self.filedata = {'left': None, 'right': None}
443          self._invbarchanged = False
444          self.filerevmodel = FileRevModel(self.repo, self.filename)
445 -        connect(self.filerevmodel, QtCore.SIGNAL('filled'),
446 -                self.modelFilled)
447 +        self.filerevmodel.filled.connect(self.modelFilled)
448          self.tableView_revisions_left.setModel(self.filerevmodel)
449          self.tableView_revisions_right.setModel(self.filerevmodel)
450 
451      def createActions(self):
452 -        connect(self.actionClose, SIGNAL('triggered()'),
453 -                self.close)
454 -        connect(self.actionReload, SIGNAL('triggered()'),
455 -                self.reload)
456 +        self.actionClose.triggered.connect(self.close)
457 +        self.actionReload.triggered.connect(self.reload)
458          self.actionClose.setIcon(geticon('quit'))
459          self.actionReload.setIcon(geticon('reload'))
460 
461          self.actionNextDiff = QtGui.QAction(geticon('down'), 'Next diff', self)
462          self.actionNextDiff.setShortcut('Alt+Down')
463          self.actionPrevDiff = QtGui.QAction(geticon('up'), 'Previous diff', self)
464          self.actionPrevDiff.setShortcut('Alt+Up')
465 -        connect(self.actionNextDiff, SIGNAL('triggered()'),
466 -                self.nextDiff)
467 -        connect(self.actionPrevDiff, SIGNAL('triggered()'),
468 -                self.prevDiff)
469 +        self.actionNextDiff.triggered.connect(self.nextDiff)
470 +        self.actionPrevDiff.triggered.connect(self.prevDiff)
471          self.actionNextDiff.setEnabled(False)
472          self.actionPrevDiff.setEnabled(False)
473 
474      def setupToolbars(self):
475          self.toolBar_edit.addSeparator()
476          self.toolBar_edit.addAction(self.actionNextDiff)
477          self.toolBar_edit.addAction(self.actionPrevDiff)
478 
479      def modelFilled(self):
480 -        disconnect(self.filerevmodel, SIGNAL('filled'),
481 -                   self.modelFilled)
482 +        self.filerevmodel.filled.disconnect(self.modelFilled)
483          if self._show_rev is not None:
484              rev = self._show_rev
485              self._show_rev = None
486          else:
487              rev = self.filerevmodel.graph[0].rev
@@ -402,11 +388,11 @@
488          for n in range(30): # burst pool
489              if self._diff is None or not self._diff.get_opcodes():
490                  self._diff = None
491                  self.timer.stop()
492                  self.setDiffNavActions(-1)
493 -                self.emit(SIGNAL('diffFilled'))
494 +                self.diff_filled.emit()
495                  break
496 
497              tag, alo, ahi, blo, bhi = self._diff.get_opcodes().pop(0)
498 
499              w = self.viewers['left']
diff --git a/hgviewlib/qt4/hgfileview.py b/hgviewlib/qt4/hgfileview.py
@@ -24,13 +24,12 @@
500      from mercurial.error import LookupError
501  except ImportError:
502      from mercurial.revlog import LookupError
503 
504  from PyQt4 import QtCore, QtGui, Qsci
505 +from PyQt4.QtCore import pyqtSignal
506  Qt = QtCore.Qt
507 -connect = QtCore.QObject.connect
508 -SIGNAL = QtCore.SIGNAL
509 
510  from hgviewlib.decorators import timeit
511  from hgviewlib.util import exec_flag_changed, isbfile, bfilepath, tounicode
512  from hgviewlib.config import HgConfig
513 
@@ -73,12 +72,11 @@
514              marker = self.markerDefine(qsci.Background)
515              color = 0x7FFF00 + (i-N/2)*256/N*256*256 - i*256/N*256 + i*256/N
516              self.SendScintilla(qsci.SCI_MARKERSETBACK, marker, color)
517              self.markers.append(marker)
518 
519 -        connect(textarea.verticalScrollBar(),
520 -                SIGNAL('valueChanged(int)'),
521 +        textarea.verticalScrollBar().valueChanged[int].connect(
522                  self.verticalScrollBar().setValue)
523 
524      def setFilectx(self, fctx):
525          self.fctx = fctx
526          ann = [f.rev() for f, line in fctx.annotate(follow=True)]
@@ -123,11 +121,11 @@
527              if tip:
528                  act.setStatusTip(tip)
529              if key:
530                  act.setShortcut(key)
531              if cb:
532 -                connect(act, SIGNAL('triggered()'), cb)
533 +                act.triggered.connect(cb)
534              self._actions[name] = act
535              self.addAction(act)
536          self._actions['diffmode'].setCheckable(True)
537          self._actions['annmode'].setCheckable(True)
538          self._actions['show-big-file'].setCheckable(True)
@@ -142,10 +140,14 @@
539                  menu.addSeparator()
540          menu.exec_(event.globalPos())
541 
542 
543  class HgFileView(QtGui.QFrame):
544 +
545 +    message_logged = pyqtSignal(str, int)
546 +    rev_for_diff_changed = pyqtSignal(int)
547 +
548      def __init__(self, parent=None):
549          QtGui.QFrame.__init__(self, parent)
550          framelayout = QtGui.QVBoxLayout(self)
551          framelayout.setContentsMargins(0,0,0,0)
552          framelayout.setSpacing(0)
@@ -231,22 +233,16 @@
553          self._mode = "diff" # can be 'diff' or 'file'
554          self.filedata = None
555 
556          self.timer = QtCore.QTimer()
557          self.timer.setSingleShot(False)
558 -        self.connect(self.timer, QtCore.SIGNAL("timeout()"),
559 -                     self.idle_fill_files)
560 -        self.connect(self.sci._actions['diffmode'], SIGNAL('toggled(bool)'),
561 -                     self.setMode)
562 -        self.connect(self.sci._actions['annmode'], SIGNAL('toggled(bool)'),
563 -                     self.setAnnotate)
564 -        self.connect(self.sci._actions['prev'], SIGNAL('triggered()'),
565 -                     self.prevDiff)
566 -        self.connect(self.sci._actions['next'], SIGNAL('triggered()'),
567 -                     self.nextDiff)
568 -        self.connect(self.sci._actions['show-big-file'], SIGNAL('toggled(bool)'),
569 -                     self.showBigFile)
570 +        self.timer.timeout.connect(self.idle_fill_files)
571 +        self.sci._actions['diffmode'].toggled[bool].connect(self.setMode)
572 +        self.sci._actions['annmode'].toggled[bool].connect(self.setAnnotate)
573 +        self.sci._actions['prev'].triggered.connect(self.prevDiff)
574 +        self.sci._actions['next'].triggered.connect(self.nextDiff)
575 +        self.sci._actions['show-big-file'].toggled[bool].connect(self.showBigFile)
576          self.sci._actions['diffmode'].setChecked(True)
577 
578      def resizeEvent(self, event):
579          QtGui.QFrame.resizeEvent(self, event)
580          h = self.sci.horizontalScrollBar().height()
@@ -321,11 +317,11 @@
581          else:
582              self._filename = filename
583 
584          if rev is not None:
585              self._p_rev = rev
586 -            self.emit(SIGNAL('revForDiffChanged'), rev)
587 +            self.rev_for_diff_changed.emit(rev)
588          self.sci.clear()
589          self.ann.clear()
590          self.filenamelabel.setText(" ")
591          self.execflaglabel.clear()
592          if filename is None:
@@ -510,13 +506,13 @@
593          n = len(text)
594          while pos[-1] > -1:
595              self.sci.SendScintilla(qsci.SCI_INDICATORFILLRANGE, pos[-1], n)
596              pos.append(data.find(text, pos[-1]+1))
597          pos = [x for x in pos if x > -1]
598 -        self.emit(SIGNAL('showMessage'),
599 -                  "Found %d occurrences of '%s' in current file or diff" % (len(pos), text),
600 -                  2000)
601 +        self.message_logged.emit(
602 +            u"Found %d occurrences of '%s' in current file or diff" % (len(pos), tounicode(text)),
603 +            2000)
604          return pos
605 
606      def highlightCurrentSearchString(self, pos, text):
607          line = self.sci.SendScintilla(qsci.SCI_LINEFROMPOSITION, pos)
608          #line, idx = w.lineIndexFromPosition(nextpos)
@@ -576,10 +572,13 @@
609 
610  class HgFileListView(QtGui.QTableView):
611      """
612      A QTableView for displaying a HgFileListModel
613      """
614 +
615 +    file_selected = pyqtSignal([str, int], [str])
616 +
617      def __init__(self, parent=None):
618          QtGui.QTableView.__init__(self, parent)
619          self.setShowGrid(False)
620          self.verticalHeader().hide()
621          self.verticalHeader().setDefaultSectionSize(20)
@@ -590,28 +589,23 @@
622 
623          self.horizontalHeader().setToolTip('Double click to toggle merge mode')
624 
625          self.createActions()
626 
627 -        connect(self.horizontalHeader(), SIGNAL('sectionDoubleClicked(int)'),
628 +        self.horizontalHeader().sectionDoubleClicked[int].connect(
629                  self.toggleFullFileList)
630 -        connect(self,
631 -                SIGNAL('doubleClicked (const QModelIndex &)'),
632 -                self.fileActivated)
633 +        self.doubleClicked.connect(self.fileActivated)
634 
635 -        connect(self.horizontalHeader(),
636 -                SIGNAL('sectionResized(int, int, int)'),
637 +        self.horizontalHeader().sectionResized[int, int, int].connect(
638                  self.sectionResized)
639          self._diff_dialogs = {}
640          self._nav_dialogs = {}
641 
642      def setModel(self, model):
643          QtGui.QTableView.setModel(self, model)
644 -        connect(model, SIGNAL('layoutChanged()'),
645 -                self.fileSelected)
646 -        connect(self.selectionModel(),
647 -                SIGNAL('currentRowChanged (const QModelIndex & , const QModelIndex & )'),
648 +        model.layoutChanged.connect(self.fileSelected)
649 +        self.selectionModel().currentRowChanged.connect(
650                  self.fileSelected)
651          self.horizontalHeader().setResizeMode(1, QtGui.QHeaderView.Stretch)
652 
653      def currentFile(self):
654          index = self.currentIndex()
@@ -620,11 +614,15 @@
655      def fileSelected(self, index=None, *args):
656          if index is None:
657              index = self.currentIndex()
658          sel_file = self.model().fileFromIndex(index)
659          from_rev = self.model().revFromIndex(index)
660 -        self.emit(SIGNAL('fileSelected'), sel_file, from_rev)
661 +        # signal get unicode as input
662 +        if from_rev is None:
663 +            self.file_selected[str].emit(tounicode(sel_file))
664 +        else:
665 +            self.file_selected[str, int].emit(to_unicode(sel_file), from_rev)
666 
667      def selectFile(self, filename):
668          self.setCurrentIndex(self.model().indexFromFile(filename))
669 
670      def fileActivated(self, index, alternate=False):
@@ -679,11 +677,11 @@
671              if tip:
672                  act.setStatusTip(tip)
673              if key:
674                  act.setShortcut(key)
675              if cb:
676 -                connect(act, SIGNAL('triggered()'), cb)
677 +                act.triggered.connect(cb)
678              self._actions[name] = act
679              self.addAction(act)
680 
681      def contextMenuEvent(self, event):
682          menu = QtGui.QMenu(self)
diff --git a/hgviewlib/qt4/hgmanifestdialog.py b/hgviewlib/qt4/hgmanifestdialog.py
@@ -32,14 +32,10 @@
683  from hgviewlib.qt4 import icon as geticon
684  from hgviewlib.qt4.hgdialogmixin import HgDialogMixin
685  from hgviewlib.qt4.hgrepomodel import ManifestModel
686  from hgviewlib.qt4.lexers import get_lexer
687 
688 -connect = QtCore.QObject.connect
689 -disconnect = QtCore.QObject.disconnect
690 -SIGNAL = QtCore.SIGNAL
691 -
692 
693  class ManifestViewer(QtGui.QMainWindow, HgDialogMixin, _ManifestViewer):
694      """
695      Qt4 dialog to display all files of a repo at a given revision
696      """
@@ -63,17 +59,15 @@
697          self.max_file_size = cfg.getMaxFileSize()
698 
699      def setupModels(self):
700          self.treemodel = ManifestModel(self.repo, self.rev)
701          self.treeView.setModel(self.treemodel)
702 -        connect(self.treeView.selectionModel(),
703 -                SIGNAL('currentChanged(const QModelIndex &, const QModelIndex &)'),
704 -                self.fileSelected)
705 +        self.treeView.selectionModel().currentChanged.connect(
706 +            self.fileSelected)
707 
708      def createActions(self):
709 -        connect(self.actionClose, SIGNAL('triggered()'),
710 -                self.close)
711 +        self.actionClose.triggered.connect(self.close)
712          self.actionClose.setIcon(geticon('quit'))
713 
714      def setupTextview(self):
715          lay = QtGui.QHBoxLayout(self.mainFrame)
716          lay.setSpacing(0)
diff --git a/hgviewlib/qt4/hgrepomodel.py b/hgviewlib/qt4/hgrepomodel.py
@@ -32,14 +32,12 @@
717  from hgviewlib.qt4 import icon as geticon
718  from hgviewlib.decorators import timeit
719  from hgviewlib.hgpatches import phases
720 
721  from PyQt4.QtGui import QColor, QPixmap, QPainter, QPen, QFont
722 -from PyQt4.QtCore import Qt, SIGNAL, QAbstractItemModel, QAbstractTableModel, \
723 -     QObject, QDateTime, QTimer, QModelIndex, QPointF
724 -connect = QObject.connect
725 -SIGNAL = SIGNAL
726 +from PyQt4.QtCore import Qt, pyqtSignal, QAbstractItemModel, QAbstractTableModel, \
727 +     QObject, QDateTime, QTimer, QModelIndex, QVariant, QPointF
728 
729  # XXX make this better than a poor hard written list...
730  COLORS = [ "blue", "darkgreen", "red", "green", "darkblue", "purple",
731             "cyan", Qt.darkYellow, "magenta", "darkred", "darkmagenta",
732             "darkcyan", "gray", ]
@@ -240,10 +238,14 @@
733 
734  class HgRepoListModel(QAbstractTableModel, HgRepoListWalker, GraphCtxMixin):
735      """
736      Model used for displaying the revisions of a Hg *local* repository
737      """
738 +
739 +    message_logged = pyqtSignal(str, int)
740 +    filled = pyqtSignal()
741 +
742      _allcolumns = ('ID', 'Branch', 'Log', 'Author', 'Date', 'Tags',)
743      _columns = ('ID', 'Branch', 'Log', 'Author', 'Date', 'Tags',)
744      _stretchs = {'Log': 1, }
745      _getcolumns = "getChangelogColumns"
746 
@@ -256,36 +258,36 @@
747          HgRepoListWalker.__init__(self, repo, branch, fromhead, follow, closed=closed)
748          self.highlights = []
749 
750      def setRepo(self, repo, branch='', fromhead=None, follow=False, closed=False):
751          HgRepoListWalker.setRepo(self, repo, branch, fromhead, follow, closed=closed)
752 -        self.emit(SIGNAL('layoutChanged()'))
753 -        QTimer.singleShot(0, partial(self.emit, SIGNAL('filled')))
754 +        self.layoutChanged.emit()
755 +        QTimer.singleShot(0, self.filled.emit)
756          self._fill_timer = self.startTimer(50)
757 
758      def highlight_rows(self, rows):
759          """mark ``rows`` to be highlighted."""
760          self.highlights[:] = rows
761          self._datacache.clear()
762 
763      def timerEvent(self, event):
764          if event.timerId() == self._fill_timer:
765 -            self.emit(SIGNAL('showMessage'),
766 +            self.message_logged.emit(
767                        'filling (%s)' % (len(self.graph)),
768                        -1)
769              if self.graph.isfilled():
770                  self.killTimer(self._fill_timer)
771                  self._fill_timer = None
772 -                self.emit(SIGNAL('showMessage'), '', -1)
773 +                self.message_logged.emit('', -1)
774              # we fill the graph data structures without telling
775              # views until we are done - this gives
776              # maximal GUI responsiveness
777              elif not self.graph.build_nodes(nnodes=self.fill_step):
778                  self.killTimer(self._fill_timer)
779                  self._fill_timer = None
780                  self.updateRowCount()
781 -                self.emit(SIGNAL('showMessage'), '', -1)
782 +                self.message_logged.emit('', -1)
783 
784      def updateRowCount(self):
785          currentlen = self.rowcount
786          newlen = len(self.graph)
787          if newlen > self.rowcount:
@@ -400,11 +402,11 @@
788          self.graph = None
789          self._datacache = {}
790          self.notify_data_changed()
791 
792      def notify_data_changed(self):
793 -        self.emit(SIGNAL("layoutChanged()"))
794 +        self.layoutChanged.emit()
795 
796      def indexFromRev(self, rev):
797          self.ensureBuilt(rev=rev)
798          row = self.rowFromRev(rev)
799          if row is not None:
@@ -448,11 +450,11 @@
800              # we use fl.index here (instead of linkrev) cause
801              # linkrev API changed between 1.0 and 1.?. So this
802              # works with both versions.
803              self.heads = [fl.index[fl.rev(x)][4] for x in fl.heads()]
804              self.ensureBuilt(row=self.fill_step/2)
805 -            QTimer.singleShot(0, partial(self.emit, SIGNAL('filled')))
806 +            QTimer.singleShot(0, self.filled.emit)
807              self._fill_timer = self.startTimer(500)
808          else:
809              self.graph = None
810              self.heads = []
811 
@@ -484,11 +486,11 @@
812          self._fill_iter = None
813 
814      def toggleFullFileList(self):
815          self._fulllist = not self._fulllist
816          self.loadFiles()
817 -        self.emit(SIGNAL('layoutChanged()'))
818 +        self.layoutChanged.emit()
819 
820      def load_config(self):
821          cfg = HgConfig(self.repo.ui)
822          self._flagcolor = {}
823          self._flagcolor['='] = cfg.getFileModifiedColor()
@@ -501,11 +503,11 @@
824 
825      def setDiffWidth(self, w):
826          if w != self.diffwidth:
827              self.diffwidth = w
828              self._datacache = {}
829 -            self.emit(SIGNAL('dataChanged(const QModelIndex &, const QModelIndex & )'),
830 +            self.dataChanged.emit(
831                        self.index(1, 0),
832                        self.index(1, self.rowCount()))
833 
834      def __len__(self):
835          return len(self._files)
@@ -592,11 +594,11 @@
836      def setSelectedRev(self, ctx):
837          if ctx != self.current_ctx:
838              self.current_ctx = ctx
839              self._datacache = {}
840              self.loadFiles()
841 -            self.emit(SIGNAL("layoutChanged()"))
842 +            self.layoutChanged.emit()
843 
844      def fillFileStats(self):
845          """
846          Method called to start the background process of computing
847          file stats, which are to be displayed in the 'Stats' column
@@ -610,12 +612,11 @@
848          try:
849              nextfill = self._fill_iter.next()
850              if nextfill is not None:
851                  row, col = nextfill
852                  idx = self.index(row, col)
853 -                self.emit(SIGNAL('dataChanged(const QModelIndex &, const QModelIndex &)'),
854 -                          idx, idx)
855 +                self.dataChanged.emit(idx, idx)
856              QTimer.singleShot(10, lambda self=self: self._fill_one_step())
857 
858          except StopIteration:
859              self._fill_iter = None
860 
diff --git a/hgviewlib/qt4/hgrepoview.py b/hgviewlib/qt4/hgrepoview.py
@@ -24,14 +24,12 @@
861 
862  from mercurial.error import (RepoError, ParseError, LookupError,
863                               RepoLookupError, Abort)
864 
865  from PyQt4 import QtCore, QtGui
866 +from PyQt4.QtCore import pyqtSignal
867  Qt = QtCore.Qt
868 -connect = QtCore.QObject.connect
869 -disconnect = QtCore.QObject.disconnect
870 -SIGNAL = QtCore.SIGNAL
871 
872  from hgviewlib.decorators import timeit
873  from hgviewlib.config import HgConfig
874  from hgviewlib.hgpatches.scmutil import revrange
875  from hgviewlib.util import format_desc, xml_escape, tounicode
@@ -65,10 +63,14 @@
876 
877 
878  class GotoQuery(QtCore.QThread):
879      """A dedicated thread that queries a revset to the repo related to
880      the model"""
881 +
882 +    failed_revset = pyqtSignal(str)
883 +    new_revset = pyqtSignal(tuple, str)
884 +
885      def __init__(self):
886          super(GotoQuery, self).__init__()
887          self.rows = None
888          self.revexp = None
889          self.model = None
@@ -80,21 +82,21 @@
890          revset = None
891          try:
892              revset = revrange(self.model.repo, [self.revexp.encode('utf-8')])
893          except (RepoError, ParseError, LookupError, RepoLookupError, Abort), err:
894              self.rows = None
895 -            self.emit(SIGNAL('failed_revset'), err)
896 +            self.failed_revset.emit(tounicode(err))
897              return
898          if revset is None:
899              self.rows = ()
900 -            self.emit(SIGNAL('new_revset'), self.rows, self.revexp)
901 +            self.new_revset.emit(self.rows, self.revexp)
902              return
903          rows = (idx.row() for idx in
904                  (self.model.indexFromRev(rev) for rev in revset)
905                  if idx is not None)
906          self.rows = tuple(sorted(rows))
907 -        self.emit(SIGNAL('new_revset'), self.rows, self.revexp)
908 +        self.new_revset.emit(self.rows, self.revexp)
909 
910      def perform(self, revexp, model):
911          self.terminate()
912          self.revexp = revexp
913          self.model = model
@@ -117,15 +119,20 @@
914          self.setStringList(strings)
915 
916 
917  class QueryLineEdit(QtGui.QLineEdit):
918      """Special LineEdit class with visual marks for the revset query status"""
919 +
920 +    text_edited_no_blank = pyqtSignal(str)
921 +
922      FORGROUNDS = {'normal':Qt.color1,
923                    'valid':Qt.color1,
924                    'failed':Qt.darkRed,
925                    'query':Qt.darkGray}
926 +
927      ICONS = {'valid':'valid', 'query':'loading'}
928 +
929      def __init__(self, parent):
930          self._parent = parent
931          self._status = None # one of the keys of self.FORGROUNDS and self.ICONS
932          QtGui.QLineEdit.__init__(self, parent)
933          self.setTextMargins(0,0,-16,0)
@@ -155,14 +162,20 @@
934      def on_text_edited(self):
935          current_text = self.text().strip()
936          if  current_text == self.previous_text:
937              return
938          self.previous_text = current_text
939 -        self.emit(SIGNAL('text_edited_no_blank'), current_text)
940 +        self.text_edited_no_blank.emit( current_text)
941 
942 
943  class GotoQuickBar(QuickBar):
944 +
945 +    goto_strict_next_from = pyqtSignal(tuple)
946 +    goto_strict_prev_from = pyqtSignal(tuple)
947 +    new_set = pyqtSignal([], [tuple])
948 +    goto_next_from = pyqtSignal(tuple)
949 +
950      def __init__(self, parent):
951          self._parent = parent
952          self._goto_query = None
953          self.compl_model = None
954          self.completer = None
@@ -201,20 +214,20 @@
955          # entry
956          self.entry = QueryLineEdit(self)
957          self.entry.setCompleter(self.completer)
958          self.entry.setStatusTip("Enter a 'revset' to query a set of revisions")
959          self.addWidget(self.entry)
960 -        connect(self.entry, SIGNAL('text_edited_no_blank'), self.auto_search)
961 +        self.entry.text_edited_no_blank.connect(self.auto_search)
962          self.entry.returnPressed.connect(lambda: self.goto(True))
963          # actions
964          self.addAction(self._actions['prev'])
965          self.addAction(self._actions['next'])
966          self.addAction(self._actions['help'])
967          # querier (threaded)
968          self._goto_query = GotoQuery()
969 -        connect(self._goto_query, SIGNAL('failed_revset'), self.on_failed)
970 -        connect(self._goto_query, SIGNAL('new_revset'), self.on_queried)
971 +        self._goto_query.failed_revset.connect(self.on_failed)
972 +        self._goto_query.new_revset.connect(self.on_queried)
973 
974      def setVisible(self, visible=True):
975          QuickBar.setVisible(self, visible)
976          if visible:
977              self.entry.setFocus()
@@ -265,14 +278,14 @@
978          rows = self._goto_query.get_last_results()
979          if rows is None:
980              self.entry.status = 'failed'
981              return
982          if forward:
983 -            signal = 'goto_strict_next_from'
984 +            signal = self.goto_strict_next_from[tuple]
985          else:
986 -            signal = 'goto_strict_prev_from'
987 -        self.emit(SIGNAL(signal), rows)
988 +            signal = self.goto_strict_prev_from[tuple]
989 +        signal.emit(rows)
990          # usecase: enter a nodeid and hit enter to go on,
991          #          so the goto tool bar is no more required and may be
992          #          annoying
993          if rows and len(rows) == 1:
994              self.setVisible(False)
@@ -280,12 +293,12 @@
995      def search(self, revexp, threaded=True):
996          if revexp is None:
997              revexp = self._standby_revexp
998          self._standby_revexp = None
999          if not revexp:
1000 -            self.emit(SIGNAL('new_set'), None)
1001 -            self.emit(SIGNAL('goto_next_from'), (self.row_before,))
1002 +            self.new_set.emit()
1003 +            self.goto_next_from[tuple].emit((self.row_before,))
1004              return
1005          self.show_message("Querying ... (edit the entry to cancel)")
1006          self._actions['next'].setEnabled(False)
1007          self._actions['prev'].setEnabled(False)
1008          self.entry.status = 'query'
@@ -298,12 +311,12 @@
1009          self.parent().statusBar().showMessage(message, delay)
1010 
1011      def on_queried(self, rows=None, revexp=u''):
1012          """Slot to handle new revset."""
1013          self.entry.status = 'valid'
1014 -        self.emit(SIGNAL('new_set'), rows)
1015 -        self.emit(SIGNAL('goto_next_from'), rows)
1016 +        self.new_set[tuple].emit(rows)
1017 +        self.goto_next_from[tuple].emit(rows)
1018          self._actions['next'].setEnabled(True)
1019          self._actions['prev'].setEnabled(True)
1020          if rows and revexp:
1021              self.compl_model.add_to_string_list(revexp)
1022 
@@ -317,10 +330,16 @@
1023  class HgRepoView(QtGui.QTableView):
1024      """
1025      A QTableView for displaying a FileRevModel or a HgRepoListModel,
1026      with actions, shortcuts, etc.
1027      """
1028 +
1029 +    start_from_rev = pyqtSignal([], [int, bool], [str, bool])
1030 +    revision_activated = pyqtSignal([], [int])
1031 +    revision_selected = pyqtSignal([], [int])
1032 +    message_logged = pyqtSignal(str, int)
1033 +
1034      def __init__(self, parent=None):
1035          QtGui.QTableView.__init__(self, parent)
1036          self.init_variables()
1037          self.setShowGrid(False)
1038          self.verticalHeader().hide()
@@ -329,18 +348,15 @@
1039          self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
1040          self.setAlternatingRowColors(True)
1041 
1042          self.createActions()
1043          self.createToolbars()
1044 -        connect(self,
1045 -                SIGNAL('doubleClicked (const QModelIndex &)'),
1046 -                self.revisionActivated)
1047 +        self.doubleClicked.connect(self.revisionActivated)
1048 
1049          self._autoresize = True
1050 -        connect(self.horizontalHeader(),
1051 -                SIGNAL('sectionResized(int, int, int)'),
1052 -                self.disableAutoResize)
1053 +        self.horizontalHeader().sectionResized[int, int, int].connect(
1054 +            self.disableAutoResize)
1055 
1056      def mousePressEvent(self, event):
1057          index = self.indexAt(event.pos())
1058          if not index.isValid():
1059              return
@@ -349,19 +365,18 @@
1060              return
1061          QtGui.QTableView.mousePressEvent(self, event)
1062 
1063      def createToolbars(self):
1064          self.goto_toolbar = GotoQuickBar(self)
1065 -        goto = self.goto_next_from
1066 -        connect(self.goto_toolbar, SIGNAL('goto_strict_next_from'),
1067 +        goto = self.on_goto_next_from
1068 +        self.goto_toolbar.goto_strict_next_from[tuple].connect(
1069                  lambda revs: goto(revs, strict=True, forward=True))
1070 -        connect(self.goto_toolbar, SIGNAL('goto_strict_prev_from'),
1071 +        self.goto_toolbar.goto_strict_prev_from[tuple].connect(
1072                  lambda revs: goto(revs, strict=True, forward=False))
1073 -        connect(self.goto_toolbar, SIGNAL('goto_next_from'),
1074 -                lambda revs: goto(revs))
1075 -        connect(self.goto_toolbar, SIGNAL('new_set'),
1076 -                self.highlight_rows)
1077 +        self.goto_toolbar.goto_next_from[tuple].connect(goto)
1078 +        self.goto_toolbar.new_set.connect(self.highlight_rows)
1079 +        self.goto_toolbar.new_set[tuple].connect(self.highlight_rows)
1080 
1081      def _action_defs(self):
1082          class ActDef(object):
1083              def __init__(self, name, desc, icon, tip, keys, cb):
1084                  self.name = name
@@ -440,14 +455,16 @@
1085              if tip:
1086                  act.setStatusTip(tip)
1087              if keys:
1088                  act.setShortcuts(keys)
1089              if cb:
1090 -                connect(act, SIGNAL('triggered()'), cb)
1091 +                act.triggered.connect(cb)
1092              self.addAction(act)
1093          self._actions['unfilter'].setEnabled(False)
1094 -        connect(self, SIGNAL('startFromRev'), self.update_filter_action)
1095 +        self.start_from_rev.connect(self.update_filter_action)
1096 +        self.start_from_rev[int, bool].connect(self.update_filter_action)
1097 +        self.start_from_rev[str, bool].connect(self.update_filter_action)
1098 
1099      def update_filter_action(self, rev=None, follow=None):
1100          self._actions['unfilter'].setEnabled(rev is not None)
1101 
1102      def copy_cs_to_clipboard(self):
@@ -459,20 +476,29 @@
1103          u.pushbuffer()
1104          cmdutil.show_changeset(u, repo, {'template':template}, False).show(ctx)
1105          QtGui.QApplication.clipboard().setText(u.popbuffer())
1106 
1107      def showAtRev(self):
1108 -        self.emit(SIGNAL('revisionActivated'), self.current_rev)
1109 +        if self.current_rev is None:
1110 +            self.revision_activated.emit()
1111 +        else:
1112 +            self.revision_activated[int].emit(self.current_rev)
1113 
1114      def startFromRev(self):
1115 -        self.emit(SIGNAL('startFromRev'), self.current_rev, False)
1116 +        if self.current_rev is None:
1117 +            self.start_from_rev.emit()
1118 +        else:
1119 +            self.start_from_rev[int, bool].emit(self.current_rev, False)
1120 
1121      def followFromRev(self):
1122 -        self.emit(SIGNAL('startFromRev'), self.current_rev, True)
1123 +        if self.current_rev is None:
1124 +            self.start_from_rev.emit()
1125 +        else:
1126 +            self.start_from_rev[int, bool].emit(self.current_rev, True)
1127 
1128      def removeFilter(self):
1129 -        self.emit(SIGNAL('startFromRev'))
1130 +        self.start_from_rev.emit()
1131 
1132      def contextMenuEvent(self, event):
1133          menu = QtGui.QMenu(self)
1134          for act in ['copycs', None,
1135                      'manifest', None,
@@ -496,13 +522,11 @@
1136          # history navigation or if we are navigating the history
1137 
1138      def setModel(self, model):
1139          self.init_variables()
1140          QtGui.QTableView.setModel(self, model)
1141 -        connect(self.selectionModel(),
1142 -                QtCore.SIGNAL('currentRowChanged (const QModelIndex & , const QModelIndex & )'),
1143 -                self.revisionSelected)
1144 +        self.selectionModel().currentRowChanged.connect(self.revisionSelected)
1145          tags = (tounicode(tag) for tag in model.repo.tags().keys())
1146          self.goto_toolbar.compl_model.add_to_string_list(*tags)
1147          revaliases = [tounicode(item[0]) for item in model.repo.ui.configitems("revsetalias")]
1148          self.goto_toolbar.compl_model.add_to_string_list(*revaliases)
1149          col = list(model._columns).index('Log')
@@ -558,11 +582,11 @@
1150              return gnode.rev
1151 
1152      def revisionActivated(self, index):
1153          rev = self.revFromindex(index)
1154          if rev is not None:
1155 -            self.emit(SIGNAL('revisionActivated'), rev)
1156 +            self.revision_activated[int].emit(rev)
1157 
1158      def revisionSelected(self, index, index_from):
1159          """
1160          Callback called when a revision is selected in the revisions table
1161          """
@@ -577,21 +601,24 @@
1162                  self._rev_pos = len(self._rev_history)-1
1163 
1164              self._in_history = False
1165              self.current_rev = rev
1166 
1167 -            self.emit(SIGNAL('revisionSelected'), rev)
1168 +            if rev is None:
1169 +                self.revision_selected.emit()
1170 +            else:
1171 +                self.revision_selected[int].emit(rev)
1172              self.set_navigation_button_state()
1173 
1174      def gotoAncestor(self, index):
1175          rev = self.revFromindex(index)
1176          if rev is not None and self.current_rev is not None:
1177              repo = self.model().repo
1178              ctx = repo[self.current_rev]
1179              ctx2 = repo[rev]
1180              ancestor = ctx.ancestor(ctx2)
1181 -            self.emit(SIGNAL('showMessage'),
1182 +            self.message_logged.emit(
1183                        "Goto ancestor of %s and %s"%(ctx.rev(), ctx2.rev()),
1184                        5000)
1185              self.goto(ancestor.rev())
1186 
1187      def set_navigation_button_state(self):
@@ -620,11 +647,11 @@
1188              if idx is not None:
1189                  self._in_history = True
1190                  self.setCurrentIndex(idx)
1191          self.set_navigation_button_state()
1192 
1193 -    def goto(self, rev):
1194 +    def goto(self, rev=None):
1195          """
1196          Select revision 'rev'.
1197          It can be anything understood by repo.changectx():
1198            revision number, node or tag for instance.
1199          """
@@ -632,19 +659,19 @@
1200              rev = rev.split(':')[1]
1201          repo = self.model().repo
1202          try:
1203              rev = repo.changectx(rev).rev()
1204          except RepoError:
1205 -            self.emit(SIGNAL('showMessage'),
1206 +            self.message_logged.emit(
1207                        "Can't find revision '%s'" % rev, 2000)
1208          else:
1209              idx = self.model().indexFromRev(rev)
1210              if idx is not None:
1211                  self.goto_toolbar.setVisible(False)
1212                  self.setCurrentIndex(idx)
1213 
1214 -    def goto_next_from(self, rows, strict=False, forward=True):
1215 +    def on_goto_next_from(self, rows, strict=False, forward=True):
1216          """Select the next row available in rows."""
1217          if not rows:
1218              return
1219          currow = self.currentIndex().row()
1220          if strict:
@@ -660,11 +687,11 @@
1221          except StopIteration:
1222              self.visual_bell()
1223              row = rows[0 if forward else -1]
1224          self.setCurrentIndex(self.model().index(row, 0))
1225          pos = rows.index(row) + 1
1226 -        self.emit(SIGNAL('showMessage'),
1227 +        self.message_logged.emit(
1228                    "revision #%i of %i" % (pos, len(rows)),
1229                    -1)
1230 
1231      def nextRev(self):
1232          row = self.currentIndex().row()
@@ -672,16 +699,16 @@
1233                               self.model().rowCount() - 1), 0))
1234      def prevRev(self):
1235          row = self.currentIndex().row()
1236          self.setCurrentIndex(self.model().index(max(row - 1, 0), 0))
1237 
1238 -    def highlight_rows(self, rows):
1239 +    def highlight_rows(self, rows=None):
1240          if rows is None:
1241              self.visual_bell()
1242 -            self.emit(SIGNAL('showMessage'), 'Revision set cleared.', 2000)
1243 +            self.message_logged.emit('Revision set cleared.', 2000)
1244          else:
1245 -            self.emit(SIGNAL('showMessage'),
1246 +            self.message_logged.emit(
1247                        '%i revisions found.' % len(rows),
1248                        2000)
1249          self.model().highlight_rows(rows or ())
1250          self.refresh_display()
1251 
@@ -707,10 +734,14 @@
1252  TROUBLE_EXPLANATIONS['conflicting'] = TROUBLE_EXPLANATIONS['divergent']
1253  class RevDisplay(QtGui.QTextBrowser):
1254      """
1255      Display metadata for one revision (rev, author, description, etc.)
1256      """
1257 +
1258 +    parent_revision_selected = pyqtSignal([], [int])
1259 +    revision_selected = pyqtSignal([], [int])
1260 +
1261      def __init__(self, parent=None):
1262          QtGui.QTextBrowser.__init__(self, parent)
1263          self.excluded = ()
1264          self.descwidth = 60 # number of chars displayed for parent/child descriptions
1265 
@@ -719,19 +750,16 @@
1266              self.rst_action.setCheckable(True)
1267              self.rst_action.setChecked(True)
1268              self.rst_action.setToolTip(self.tr('Interpret ReST comments'))
1269              self.rst_action.setStatusTip(self.tr('Interpret ReST comments'))
1270 
1271 -            connect(self.rst_action, SIGNAL('triggered()'),
1272 -                    self.refreshDisplay)
1273 +            self.rst_action.triggered.connect(self.refreshDisplay)
1274          else:
1275              self.rst_action = None
1276 -        connect(self,
1277 -                SIGNAL('anchorClicked(const QUrl &)'),
1278 -                self.anchorClicked)
1279 +        self.anchorClicked.connect(self.on_anchor_clicked)
1280 
1281 -    def anchorClicked(self, qurl):
1282 +    def on_anchor_clicked(self, qurl):
1283          """
1284          Callback called when a link is clicked in the text browser
1285          """
1286          rev = qurl.toString()
1287          diff = False
@@ -747,13 +775,19 @@
1288 
1289          if diff:
1290              self.diffrev = rev
1291              self.refreshDisplay()
1292              # TODO: emit a signal to recompute the diff
1293 -            self.emit(SIGNAL('parentRevisionSelected'), self.diffrev)
1294 +            if self.diffrev is None:
1295 +                self.parent_revision_selected.emit()
1296 +            else:
1297 +                self.parent_revision_selected[int].emit(self.diffrev)
1298          else:
1299 -            self.emit(SIGNAL('revisionSelected'), rev)
1300 +            if rev is None:
1301 +                self.revision_selected.emit()
1302 +            else:
1303 +                self.revision_selected[int].emit(rev)
1304 
1305      def setDiffRevision(self, rev):
1306          if rev != self.diffrev:
1307              self.diffrev = rev
1308              self.refreshDisplay()
diff --git a/hgviewlib/qt4/hgrepoviewer.py b/hgviewlib/qt4/hgrepoviewer.py
@@ -6,15 +6,17 @@
1309  # This software may be used and distributed according to the terms
1310  # of the GNU General Public License, incorporated herein by reference.
1311  """
1312  Main Qt4 application for hgview
1313  """
1314 +
1315  import sys, os
1316  import os.path as osp
1317  import re
1318  import errno
1319  from functools import partial
1320 +from operator import methodcaller
1321 
1322  from PyQt4 import QtCore, QtGui, Qsci
1323 
1324  from mercurial import ui, hg
1325  from mercurial import util
@@ -40,12 +42,10 @@
1326 
1327  from mercurial.error import RepoError
1328 
1329  Qt = QtCore.Qt
1330  bold = QtGui.QFont.Bold
1331 -connect = QtCore.QObject.connect
1332 -SIGNAL = QtCore.SIGNAL
1333 
1334  NESTED_PREFIX = u'\N{RIGHTWARDS ARROW} '
1335 
1336  class HgRepoViewer(QtGui.QMainWindow, HgDialogMixin, _HgRepoViewer):
1337      """hg repository viewer/browser application"""
@@ -76,31 +76,32 @@
1338 
1339          self.createActions()
1340          self.createToolbars()
1341 
1342          self.textview_status.setFont(self._font)
1343 -        connect(self.textview_status, SIGNAL('showMessage'),
1344 +        self.textview_status.message_logged.connect(
1345                  self.statusBar().showMessage)
1346 -        connect(self.tableView_revisions, SIGNAL('showMessage'),
1347 +        self.tableView_revisions.message_logged.connect(
1348                  self.statusBar().showMessage)
1349 
1350          # setup tables and views
1351          if self.repo.root is not None:
1352 
1353              self.setupHeaderTextview()
1354              self.setupBranchCombo()
1355              self.setupModels(fromhead)
1356              self._setupQuickOpen()
1357 
1358 +        to_utf8 = methodcaller('encode', 'utf-8')
1359          if self.cfg.getFileDescriptionView() == 'asfile':
1360 -            fileselcallback = self.displaySelectedFile
1361 +            fileselcallback = compose(self.displaySelectedFile, to_utf8)
1362          else:
1363 -            fileselcallback = self.textview_status.displayFile
1364 -        connect(self.tableView_filelist, SIGNAL('fileSelected'),
1365 -                fileselcallback)
1366 +            fileselcallback = compose(self.textview_status.displayFile, to_utf8)
1367 +        self.tableView_filelist.file_selected[str].connect(fileselcallback)
1368 +        self.tableView_filelist.file_selected[str, int].connect(fileselcallback)
1369 
1370 -        connect(self.textview_status, SIGNAL('revForDiffChanged'),
1371 +        self.textview_status.rev_for_diff_changed.connect(
1372                  self.textview_header.setDiffRevision)
1373 
1374          if fromhead:
1375              self.startrev_entry.setText(str(fromhead))
1376          self.setupRevisionTable()
@@ -185,15 +186,17 @@
1377      def createToolbars(self):
1378          # find quickbar
1379          self.find_toolbar = FindInGraphlogQuickBar(self)
1380          self.find_toolbar.attachFileView(self.textview_status)
1381          self.find_toolbar.attachHeaderView(self.textview_header)
1382 -        connect(self.find_toolbar, SIGNAL('revisionSelected'),
1383 +        self.find_toolbar.revision_selected.connect(
1384                  self.tableView_revisions.goto)
1385 -        connect(self.find_toolbar, SIGNAL('fileSelected'),
1386 +        self.find_toolbar.revision_selected[int].connect(
1387 +                self.tableView_revisions.goto)
1388 +        self.find_toolbar.file_selected.connect(
1389                  self.tableView_filelist.selectFile)
1390 -        connect(self.find_toolbar, SIGNAL('showMessage'),
1391 +        self.find_toolbar.message_logged.connect(
1392                  self.statusBar().showMessage,
1393                  Qt.QueuedConnection)
1394 
1395          self.attachQuickBar(self.find_toolbar)
1396 
@@ -225,14 +228,12 @@
1397          cbranch_action = self.branch_menu.addAction("Display closed branches")
1398          cbranch_action.setCheckable(True)
1399          self.branch_checkBox_action = cbranch_action
1400          self.branch_label.setMenu(self.branch_menu)
1401          self.branch_comboBox = QtGui.QComboBox()
1402 -        connect(self.branch_comboBox, SIGNAL('activated(const QString &)'),
1403 -                self.refreshRevisionTable)
1404 -        connect(cbranch_action, SIGNAL('toggled(bool)'),
1405 -                self.setupBranchComboAndReload)
1406 +        self.branch_comboBox.activated[str].connect(self.refreshRevisionTable)
1407 +        cbranch_action.toggled[bool].connect(self.setupBranchComboAndReload)
1408 
1409          self.toolBar_treefilters.layout().setSpacing(3)
1410 
1411          self.branch_label_action = self.toolBar_treefilters.addWidget(self.branch_label)
1412          self.branch_comboBox_action = self.toolBar_treefilters.addWidget(self.branch_comboBox)
@@ -249,15 +250,14 @@
1413          follow_action = self.startrev_menu.addAction("Follow mode")
1414          follow_action.setCheckable(True)
1415          follow_action.setStatusTip("Follow changeset history from start revision")
1416          self.startrev_follow_action = follow_action
1417          self.startrev_label.setMenu(self.startrev_menu)
1418 -        callback = lambda *a: self.tableView_revisions.emit(
1419 -                    SIGNAL('startFromRev'),
1420 -                    self.startrev_entry, self.startrev_follow_action)
1421 -        connect(self.startrev_entry, SIGNAL('editingFinished()'), callback)
1422 -        connect(self.startrev_follow_action, SIGNAL('toggled(bool)'), callback)
1423 +        callback = lambda *a: self.tableView_revisions.start_from_rev[str, bool].emit(
1424 +            str(self.startrev_entry.text()), self.startrev_follow_action.isChecked())
1425 +        self.startrev_entry.editingFinished.connect(callback)
1426 +        self.startrev_follow_action.toggled[bool].connect(callback)
1427 
1428          self.revscompl_model = QtGui.QStringListModel(['tip'])
1429          self.revcompleter = QtGui.QCompleter(self.revscompl_model, self)
1430          self.startrev_entry.setCompleter(self.revcompleter)
1431 
@@ -309,111 +309,95 @@
1432              )
1433 
1434 
1435      def createActions(self):
1436          # main window actions (from .ui file)
1437 -        connect(self.actionOpen_repository, SIGNAL('triggered()'),
1438 -                self.open_repository)
1439 -        connect(self.actionRefresh, SIGNAL('triggered()'),
1440 -                self.reload)
1441 -        connect(self.actionAbout, SIGNAL('triggered()'),
1442 -                self.on_about)
1443 -        connect(self.actionQuit, SIGNAL('triggered()'),
1444 -                self.close)
1445 +        self.actionOpen_repository.triggered.connect(self.open_repository)
1446 +        self.actionRefresh.triggered.connect(self.reload)
1447 +        self.actionAbout.triggered.connect(self.on_about)
1448 +        self.actionQuit.triggered.connect(self.close)
1449          self.actionQuit.setIcon(geticon('quit'))
1450          self.actionRefresh.setIcon(geticon('reload'))
1451 
1452          self.actionHelp.setShortcut(Qt.Key_F1)
1453          self.actionHelp.setIcon(geticon('help'))
1454 -        connect(self.actionHelp, SIGNAL('triggered()'),
1455 -                self.on_help)
1456 +        self.actionHelp.triggered.connect(self.on_help)
1457 
1458          self.actionShowHidden = QtGui.QAction(self.tr('Show/Hide Hidden'), self)
1459          self.actionShowHidden.setIcon(geticon('showhide'))
1460          self.actionShowHidden.setCheckable(True)
1461          self.actionShowHidden.setChecked(self.cfg.getShowHidden())
1462          self.actionShowHidden.setToolTip(self.tr('Show/Hide hidden changeset'))
1463          self.actionShowHidden.setStatusTip(self.tr('Show/hide hidden changeset'))
1464 -        connect(self.actionShowHidden, SIGNAL('triggered()'),
1465 -                self.refreshRevisionTable)
1466 +        self.actionShowHidden.triggered.connect(self.refreshRevisionTable)
1467 
1468          self.actionShowMainContent = QtGui.QAction('Content', self)
1469          self.actionShowMainContent.setIcon(geticon('content'))
1470          self.actionShowMainContent.setCheckable(True)
1471          self.actionShowMainContent.setChecked(self.cfg.getContentAtStartUp())
1472          tip = self.tr('Show/Hide changeset content')
1473          self.actionShowMainContent.setToolTip(tip)
1474          self.actionShowMainContent.setStatusTip(tip)
1475          self.actionShowMainContent.setShortcut(Qt.Key_Space)
1476 -        connect(self.actionShowMainContent, SIGNAL('triggered()'),
1477 -                self.toggleMainContent)
1478 +        self.actionShowMainContent.triggered.connect(self.toggleMainContent)
1479 
1480          # Next/Prev file
1481          self.actionNextFile = QtGui.QAction('Next file', self)
1482          self.actionNextFile.setShortcut('Right')
1483 -        connect(self.actionNextFile, SIGNAL('triggered()'),
1484 -                self.tableView_filelist.nextFile)
1485 +        self.actionNextFile.triggered.connect(self.tableView_filelist.nextFile)
1486          self.actionPrevFile = QtGui.QAction('Prev file', self)
1487          self.actionPrevFile.setShortcut('Left')
1488 -        connect(self.actionPrevFile, SIGNAL('triggered()'),
1489 -                self.tableView_filelist.prevFile)
1490 +        self.actionPrevFile.triggered.connect(self.tableView_filelist.prevFile)
1491          self.addAction(self.actionNextFile)
1492          self.addAction(self.actionPrevFile)
1493          self.disab_shortcuts.append(self.actionNextFile)
1494          self.disab_shortcuts.append(self.actionPrevFile)
1495 
1496          # Next/Prev rev
1497          self.actionNextRev = QtGui.QAction('Next revision', self)
1498          self.actionNextRev.setShortcut('Down')
1499 -        connect(self.actionNextRev, SIGNAL('triggered()'),
1500 -                self.tableView_revisions.nextRev)
1501 +        self.actionNextRev.triggered.connect(self.tableView_revisions.nextRev)
1502          self.actionPrevRev = QtGui.QAction('Prev revision', self)
1503          self.actionPrevRev.setShortcut('Up')
1504 -        connect(self.actionPrevRev, SIGNAL('triggered()'),
1505 -                self.tableView_revisions.prevRev)
1506 +        self.actionPrevRev.triggered.connect(self.tableView_revisions.prevRev)
1507          self.addAction(self.actionNextRev)
1508          self.addAction(self.actionPrevRev)
1509          self.disab_shortcuts.append(self.actionNextRev)
1510          self.disab_shortcuts.append(self.actionPrevRev)
1511 
1512          # navigate in file viewer
1513          self.actionNextLine = QtGui.QAction('Next line', self)
1514          self.actionNextLine.setShortcut(Qt.SHIFT + Qt.Key_Down)
1515 -        connect(self.actionNextLine, SIGNAL('triggered()'),
1516 -                self.textview_status.nextLine)
1517 +        self.actionNextLine.triggered.connect(self.textview_status.nextLine)
1518          self.addAction(self.actionNextLine)
1519          self.actionPrevLine = QtGui.QAction('Prev line', self)
1520          self.actionPrevLine.setShortcut(Qt.SHIFT + Qt.Key_Up)
1521 -        connect(self.actionPrevLine, SIGNAL('triggered()'),
1522 -                self.textview_status.prevLine)
1523 +        self.actionPrevLine.triggered.connect(self.textview_status.prevLine)
1524          self.addAction(self.actionPrevLine)
1525          self.actionNextCol = QtGui.QAction('Next column', self)
1526          self.actionNextCol.setShortcut(Qt.SHIFT + Qt.Key_Right)
1527 -        connect(self.actionNextCol, SIGNAL('triggered()'),
1528 -                self.textview_status.nextCol)
1529 +        self.actionNextCol.triggered.connect(self.textview_status.nextCol)
1530          self.addAction(self.actionNextCol)
1531          self.actionPrevCol = QtGui.QAction('Prev column', self)
1532          self.actionPrevCol.setShortcut(Qt.SHIFT + Qt.Key_Left)
1533 -        connect(self.actionPrevCol, SIGNAL('triggered()'),
1534 -                self.textview_status.prevCol)
1535 +        self.actionPrevCol.triggered.connect(self.textview_status.prevCol)
1536          self.addAction(self.actionPrevCol)
1537 
1538          # Activate file (file diff navigator)
1539          self.actionActivateFile = QtGui.QAction('Activate file', self)
1540          self.actionActivateFile.setShortcuts([Qt.Key_Return, Qt.Key_Enter])
1541          def enterkeypressed():
1542              w = QtGui.QApplication.focusWidget()
1543              if not isinstance(w, QtGui.QLineEdit):
1544                  self.tableView_filelist.fileActivated(self.tableView_filelist.currentIndex(),)
1545              else:
1546 -                w.emit(SIGNAL('editingFinished()'))
1547 -        connect(self.actionActivateFile, SIGNAL('triggered()'),
1548 -                enterkeypressed)
1549 +                w.editingFinished.emit()
1550 +                self.actionActivateFile.triggered.connect(enterkeypressed)
1551 
1552          self.actionActivateFileAlt = QtGui.QAction('Activate alt. file', self)
1553          self.actionActivateFileAlt.setShortcuts([Qt.ALT+Qt.Key_Return, Qt.ALT+Qt.Key_Enter])
1554 -        connect(self.actionActivateFileAlt, SIGNAL('triggered()'),
1555 +        self.actionActivateFileAlt.triggered.connect(
1556                  lambda self=self:
1557                  self.tableView_filelist.fileActivated(self.tableView_filelist.currentIndex(),
1558                                                        alternate=True))
1559 
1560      def toggleMainContent(self, visible=None):
@@ -434,13 +418,12 @@
1561          cfg = HgDialogMixin.load_config(self)
1562          self.hidefinddelay = cfg.getHideFindDelay()
1563 
1564      def create_models(self, fromhead=None):
1565          self.repomodel = HgRepoListModel(self.repo, fromhead=fromhead)
1566 -        connect(self.repomodel, SIGNAL('filled'),
1567 -                self.on_filled)
1568 -        connect(self.repomodel, SIGNAL('showMessage'),
1569 +        self.repomodel.filled.connect(self.on_filled)
1570 +        self.repomodel.message_logged.connect(
1571                  self.statusBar().showMessage,
1572                  Qt.QueuedConnection)
1573 
1574          self.filelistmodel = HgFileListModel(self.repo)
1575 
@@ -462,32 +445,33 @@
1576              self.textview_status.displayFile(filename, rev)
1577 
1578      def setupRevisionTable(self):
1579          view = self.tableView_revisions
1580          view.installEventFilter(self)
1581 -        connect(view, SIGNAL('clicked (const QModelIndex &)'),
1582 -                self.toggleMainContent)
1583 -        connect(view, SIGNAL('revisionSelected'), self.revision_selected)
1584 -        connect(view, SIGNAL('revisionActivated'), self.revision_activated)
1585 -        connect(view, SIGNAL('startFromRev'), self.start_from_rev)
1586 -        connect(self.textview_header, SIGNAL('revisionSelected'), view.goto)
1587 -        connect(self.textview_header, SIGNAL('parentRevisionSelected'),
1588 +        view.clicked.connect(self.toggleMainContent)
1589 +        view.revision_selected.connect(self.revision_selected)
1590 +        view.revision_selected[int].connect(self.revision_selected)
1591 +        view.revision_activated.connect(self.revision_activated)
1592 +        view.revision_activated[int].connect(self.revision_activated)
1593 +        view.start_from_rev.connect(self.start_from_rev)
1594 +        view.start_from_rev[int, bool].connect(self.start_from_rev)
1595 +        view.start_from_rev[str, bool].connect(self.start_from_rev)
1596 +        self.textview_header.revision_selected.connect(view.goto)
1597 +        self.textview_header.revision_selected[int].connect(view.goto)
1598 +        self.textview_header.parent_revision_selected.connect(
1599 +                self.textview_status.displayDiff)
1600 +        self.textview_header.parent_revision_selected[int].connect(
1601                  self.textview_status.displayDiff)
1602          self.attachQuickBar(view.goto_toolbar)
1603          gotoaction = view.goto_toolbar.toggleViewAction()
1604          gotoaction.setIcon(geticon('goto'))
1605          self.toolBar_edit.addAction(gotoaction)
1606 
1607      def start_from_rev(self, rev=None, follow=False):
1608 -        if rev == self.startrev_entry:
1609 -            rev = str(self.startrev_entry.text()).strip() or None
1610 -        if follow == self.startrev_follow_action:
1611 -            follow = self.startrev_follow_action.isChecked()
1612 -
1613 +        rev = rev or None # '' => None
1614          self.startrev_entry.setText(str(rev or ''))
1615          self.startrev_follow_action.setChecked(follow)
1616 -
1617          self.refreshRevisionTable(rev=rev, follow=follow)
1618 
1619      def _setup_table(self, table):
1620          table.setTabKeyNavigation(False)
1621          table.verticalHeader().setDefaultSectionSize(self.rowheight)
@@ -537,11 +521,11 @@
1622              rev = self.tableView_revisions.current_rev
1623          self.toggleMainContent(True)
1624          self._manifestdlg = ManifestViewer(self.repo, rev)
1625          self._manifestdlg.show()
1626 
1627 -    def revision_selected(self, rev):
1628 +    def revision_selected(self, rev=None):
1629          """
1630          Callback called when a revision is selected in the revisions table
1631          if rev == -1: refresh the current selected revision
1632          """
1633          if not self.frame_maincontent.isVisible() or not self.repomodel.graph:
diff --git a/hgviewlib/qt4/quickbar.py b/hgviewlib/qt4/quickbar.py
@@ -20,20 +20,23 @@
1634  from functools import partial
1635 
1636  from mercurial import util
1637 
1638  from PyQt4 import QtCore, QtGui
1639 +from PyQt4.QtCore import pyqtSignal
1640 
1641  from hgviewlib.util import tounicode
1642  from hgviewlib.qt4 import icon as geticon
1643 
1644  Qt = QtCore.Qt
1645 -connect = QtCore.QObject.connect
1646 -SIGNAL = QtCore.SIGNAL
1647 
1648 
1649  class QuickBar(QtGui.QToolBar):
1650 +
1651 +    esc_shortcut_disabled = pyqtSignal(bool)
1652 +    unhidden = pyqtSignal()
1653 +
1654      def __init__(self, name, key, desc=None, parent=None):
1655          self.original_parent = parent
1656          # used to remember who had the focus before bar steel it
1657          self._focusw = None
1658          QtGui.QToolBar.__init__(self, name, parent)
@@ -57,27 +60,25 @@
1659              desc = "Open"
1660          openact = QtGui.QAction(desc, parent)
1661          openact.setCheckable(True)
1662          openact.setChecked(False)
1663          openact.setShortcut(QtGui.QKeySequence(openkey))
1664 -        connect(openact, SIGNAL('triggered()'),
1665 -                partial(self.setVisible, True))
1666 +        openact.triggered.connect(partial(self.setVisible, True))
1667 
1668          closeact = QtGui.QAction('Close', self)
1669          closeact.setIcon(geticon('close'))
1670 -        connect(closeact, SIGNAL('triggered()'),
1671 -                partial(self.setVisible, False))
1672 +        closeact.triggered.connect(partial(self.setVisible, False))
1673 
1674          self._actions = {'open': openact,
1675                           'close': closeact,}
1676 
1677      def setVisible(self, visible=True):
1678          if visible and not self.isVisible():
1679 -            self.emit(SIGNAL('visible'))
1680 +            self.unhidden.emit()
1681              self._focusw = QtGui.QApplication.focusWidget()
1682          QtGui.QToolBar.setVisible(self, visible)
1683 -        self.emit(SIGNAL('escShortcutDisabled(bool)'), not visible)
1684 +        self.esc_shortcut_disabled[bool].emit(not visible)
1685          if not visible and self._focusw:
1686              self._focusw.setFocus()
1687              self._focusw = None
1688 
1689      def createContent(self):
@@ -96,39 +97,45 @@
1690          shortcuts.append(key)
1691          act.setShortcuts(shortcuts)
1692 
1693 
1694  class FindQuickBar(QuickBar):
1695 +
1696 +    to_find = pyqtSignal(str)
1697 +    to_find_next = pyqtSignal(str)
1698 +    canceled = pyqtSignal()
1699 +    message_logged = pyqtSignal(str, int)
1700 +
1701      def __init__(self, parent):
1702          QuickBar.__init__(self, "Find", "/", "Find", parent)
1703          self.addShortcut('open', 'Ctrl+F')
1704          self.currenttext = ''
1705 
1706      def createActions(self, openkey, desc):
1707          QuickBar.createActions(self, openkey, desc)
1708          self._actions['findnext'] = QtGui.QAction("Find next", self)
1709          self._actions['findnext'].setShortcut(QtGui.QKeySequence("Ctrl+N"))
1710 -        connect(self._actions['findnext'], SIGNAL('triggered()'), self.find)
1711 +        self._actions['findnext'].triggered.connect(self.find)
1712          self._actions['cancel'] = QtGui.QAction("Cancel", self)
1713 -        connect(self._actions['cancel'], SIGNAL('triggered()'), self.cancel)
1714 +        self._actions['cancel'].triggered.connect(self.cancel)
1715 
1716      def find(self, *args):
1717          '''Scan the repository metadata and search for occurrences of the
1718          text in the entry.
1719          :note: do not scan if no text was provided'''
1720          text = self.entry.text()
1721          if not text: # do not strip() as user may want to find space sequences
1722 -            self.emit(SIGNAL('showMessage'), 'Nothing to look for.', 1000)
1723 +            self.message_logged.emit('Nothing to look for.', 1000)
1724              return
1725          if text == self.currenttext:
1726 -            self.emit(SIGNAL('findnext'), text)
1727 +            self.to_find_next.emit(text)
1728          else:
1729              self.currenttext = text
1730 -            self.emit(SIGNAL('find'), text)
1731 +            self.to_find.emit(text)
1732 
1733      def cancel(self):
1734 -        self.emit(SIGNAL('cancel'))
1735 +        self.canceled.emit()
1736 
1737      def setCancelEnabled(self, enabled=True):
1738          self._actions['cancel'].setEnabled(enabled)
1739          self._actions['findnext'].setEnabled(not enabled)
1740 
@@ -141,14 +148,12 @@
1741          self.addWidget(self.entry)
1742          self.addAction(self._actions['findnext'])
1743          self.addAction(self._actions['cancel'])
1744          self.setCancelEnabled(False)
1745 
1746 -        connect(self.entry, SIGNAL('returnPressed()'),
1747 -                self.find)
1748 -        connect(self.entry, SIGNAL('textEdited(const QString &)'),
1749 -                self.find)
1750 +        self.entry.returnPressed.connect(self.find)
1751 +        self.entry.textEdited[str].connect(self.find)
1752 
1753      def setVisible(self, visible=True):
1754          QuickBar.setVisible(self, visible)
1755          if visible:
1756              self.entry.setFocus()
@@ -162,25 +167,26 @@
1757          # prevent a warning in the console:
1758          # QObject::startTimer: QTimer can only be used with threads started with QThread
1759          self.entry.setCompleter(None)
1760 
1761  class FindInGraphlogQuickBar(FindQuickBar):
1762 +
1763 +    revision_selected = pyqtSignal([], [int])
1764 +    file_selected = pyqtSignal(str)
1765 +
1766      def __init__(self, parent):
1767          FindQuickBar.__init__(self, parent)
1768          self._findinfile_iter = None
1769          self._findinlog_iter = None
1770          self._findindesc_iter = None
1771          self._fileview = None
1772          self._headerview = None
1773          self._filter_files = None
1774          self._mode = 'diff'
1775 -        connect(self, SIGNAL('find'),
1776 -                self.on_find_text_changed)
1777 -        connect(self, SIGNAL('findnext'),
1778 -                self.on_findnext)
1779 -        connect(self, SIGNAL('cancel'),
1780 -                self.on_cancelsearch)
1781 +        self.to_find.connect(self.on_find_text_changed)
1782 +        self.to_find_next.connect(self.on_findnext)
1783 +        self.canceled.connect(self.on_cancelsearch)
1784 
1785      def setFilterFiles(self, files):
1786          self._filter_files = files
1787 
1788      def setModel(self, model):
@@ -228,18 +234,18 @@
1789                  else:
1790                      yield None
1791 
1792      def cancel(self):
1793          if self._actions['cancel'].isEnabled():
1794 -            self.emit(SIGNAL('cancel'))
1795 +            self.canceled.emit()
1796          else:
1797              self.hide()
1798 
1799      def on_cancelsearch(self, *args):
1800          self._findinlog_iter = None
1801          self.setCancelEnabled(False)
1802 -        self.emit(SIGNAL('showMessage'), 'Search cancelled!', 2000)
1803 +        self.message_logged.emit('Search cancelled!', 2000)
1804 
1805      def on_findnext(self):
1806          """
1807          callback called by 'Find' quicktoolbar (on findnext signal)
1808          """
@@ -278,32 +284,35 @@
1809              # when search has been cancelled
1810              return
1811          for next_find in self._findinlog_iter:
1812              if next_find is None: # not yet found, let's animate a bit the GUI
1813                  if (step % 20) == 0:
1814 -                    self.emit(SIGNAL("showMessage"),
1815 +                    self.message_logged.emit(
1816                                'Searching'+'.'*(step/20),
1817                                -1)
1818                  step += 1
1819                  QtCore.QTimer.singleShot(0, partial(self.find_next_in_log, (step % 80)))
1820              else:
1821 -                self.emit(SIGNAL("showMessage"), '', -1)
1822 +                self.message_logged.emit('', -1)
1823                  self.setCancelEnabled(False)
1824 
1825                  rev, filename = next_find
1826 -                self.emit(SIGNAL('revisionSelected'), rev)
1827 +                if rev is None:
1828 +                    self.revision_selected.emit()
1829 +                else:
1830 +                    self.revision_selected[int].emit(rev)
1831                  text = self.entry.text()
1832                  if filename is None and self._headerview:
1833                      self._findindesc_iter = self._headerview.searchString(text)
1834                      self.on_findnext()
1835                  else:
1836 -                    self.emit(SIGNAL('fileSelected'), filename)
1837 +                    self.file_selected.emit(tounicode(filename))
1838                      if self._fileview:
1839                          self._findinfile_iter = self._fileview.searchString(text)
1840                          self.on_findnext()
1841              return
1842 -        self.emit(SIGNAL('showMessage'),
1843 +        self.message_logged.emit(
1844                    'No more matches found in repository',
1845                    2000)
1846          self.setCancelEnabled(False)
1847          self._findinlog_iter = None
1848 
@@ -318,11 +327,11 @@
1849              self._findindesc_iter = self._headerview.searchString(newtext)
1850          if self._fileview:
1851              self._findinfile_iter = self._fileview.searchString(newtext)
1852          if newtext.strip():
1853              if self._findindesc_iter is None and self._findindesc_iter is None:
1854 -                self.emit(SIGNAL('showMessage'),
1855 +                self.message_logged.emit(
1856                            'Search string not found in current diff. '
1857                            'Hit "Find next" button to start searching '
1858                            'in the repository',
1859                            2000)
1860              else: