Commits

Yuya Nishihara  committed 40e2b8f

before fix setSource

  • Participants
  • Parent commits 1c952c2

Comments (0)

Files changed (25)

+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287216391 -32400
+# Node ID 2dda297ac420c5732479584384e747ffd0883f1a
+# Parent  a2d7e44377051f282682b9f1326282613f99bdf8
+manifestdialog: honor line number specified by annotate context menu
+
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -142,7 +142,7 @@ class ManifestWidget(QWidget):
+         self._contentview.addWidget(self._nullcontent)
+         self._fileview = annotate.AnnotateView(self._repo)
+         self._contentview.addWidget(self._fileview)
+-        self._fileview.revSelected.connect(lambda a: self.setSource(*a[:2]))
++        self._fileview.revSelected.connect(lambda a: self.setSource(*a[:3]))
+         for name in ('revisionHint', 'searchRequested', 'grepRequested'):
+             getattr(self._fileview, name).connect(getattr(self, name))
+         self._contentview.currentChanged.connect(self._updatecontent)
+@@ -199,14 +199,15 @@ class ManifestWidget(QWidget):
+         """Change revision to show"""
+         self.setSource(self.path, rev)
+ 
+-    @pyqtSlot(unicode, object)
+-    def setSource(self, path, rev):
++    @pyqtSlot(unicode, object, int)
++    def setSource(self, path, rev, line=None):
+         """Change path and revision to show at once"""
+         if self._rev != rev:
+             self._rev = rev
+             self._setupmodel()
+             self.revchanged.emit(rev)
+         self.setpath(path)
++        self._fileview.setSource(path, rev, line)
+ 
+     @property
+     def path(self):
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287216976 -32400
+# Node ID 4a3018edc250256ceb4a96936e1ab7f3ab0cb9ea
+# Parent  2dda297ac420c5732479584384e747ffd0883f1a
+annotate: make dialog visible as soon as possible
+
+diff --git a/tortoisehg/hgqt/annotate.py b/tortoisehg/hgqt/annotate.py
+--- a/tortoisehg/hgqt/annotate.py
++++ b/tortoisehg/hgqt/annotate.py
+@@ -499,11 +499,15 @@ class AnnotateDialog(QMainWindow):
+         if line and isinstance(line, str):
+             line = int(line)
+ 
+-        av.setSource(hglib.tounicode(pats[0]), opts.get('rev') or '.', line)
+         self.repo = repo
+ 
+         self.restoreSettings()
+ 
++        # run heavy operation after the dialog visible
++        path = hglib.tounicode(pats[0])
++        rev = opts.get('rev') or '.'
++        QTimer.singleShot(0, lambda: av.setSource(path, rev, line))
++
+     def closeEvent(self, event):
+         self.storeSettings()
+         super(AnnotateDialog, self).closeEvent(event)
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287222117 -32400
+# Node ID 30f624256d6245f2ec44608fcfdade52ee385639
+# Parent  4a3018edc250256ceb4a96936e1ab7f3ab0cb9ea
+manifestdialog: simplify switching of content views
+
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -83,15 +83,6 @@ class ManifestDialog(QMainWindow):
+         from tortoisehg.hgqt import run
+         run.grep(self._repo.ui, hglib.fromunicode(pattern), **opts)
+ 
+-class _NullView(QWidget):
+-    """empty widget for content view"""
+-    def __init__(self, parent=None):
+-        super(_NullView, self).__init__(parent)
+-
+-    @pyqtSlot(unicode, object)
+-    def setSource(self, path, rev):
+-        pass
+-
+ class ManifestWidget(QWidget):
+     """Display file tree and contents at the specified revision"""
+     revchanged = pyqtSignal(object)  # emit when curret revision changed
+@@ -138,14 +129,13 @@ class ManifestWidget(QWidget):
+         self._splitter.setStretchFactor(0, 1)
+         self._splitter.setStretchFactor(1, 3)
+ 
+-        self._nullcontent = _NullView()
++        self._nullcontent = QWidget()
+         self._contentview.addWidget(self._nullcontent)
+         self._fileview = annotate.AnnotateView(self._repo)
+         self._contentview.addWidget(self._fileview)
+         self._fileview.revSelected.connect(lambda a: self.setSource(*a[:3]))
+         for name in ('revisionHint', 'searchRequested', 'grepRequested'):
+             getattr(self._fileview, name).connect(getattr(self, name))
+-        self._contentview.currentChanged.connect(self._updatecontent)
+ 
+     def _initactions(self):
+         self._statusfilter = _StatusFilterButton(text='MAC')
+@@ -226,7 +216,7 @@ class ManifestWidget(QWidget):
+             return
+ 
+         self._contentview.setCurrentWidget(self._fileview)
+-        self._contentview.currentWidget().setSource(self.path, self._rev)
++        self._fileview.setSource(self.path, self._rev)
+ 
+ # TODO: share this menu with status widget?
+ class _StatusFilterButton(QToolButton):
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287224399 -32400
+# Node ID 82aaeaae1ee06eb6a1e1e2eaf14b3b3bbd4dc92a
+# Parent  30f624256d6245f2ec44608fcfdade52ee385639
+tests: move with_encoding() decorator to tests module
+
+diff --git a/tests/__init__.py b/tests/__init__.py
+--- a/tests/__init__.py
++++ b/tests/__init__.py
+@@ -1,5 +1,7 @@
+ import os, tempfile, shutil
++from nose.tools import *
+ from mercurial import ui, commands, error
++from tortoisehg.util import hglib
+ from tortoisehg.hgqt import thgrepo
+ 
+ FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixtures')
+@@ -29,3 +31,19 @@ def get_fixture_repo(name):
+         return thgrepo.repository(ui.ui(), path)
+     except error.RepoError:
+         return create_fixture_repo(name)
++
++
++def with_encoding(encoding, fallbackencoding=None):
++    """Change locale encoding temporarily"""
++    orig_encoding = hglib._encoding
++    orig_fallbackencoding = hglib._fallbackencoding
++
++    def setenc():
++        hglib._encoding = encoding
++        hglib._fallbackencoding = fallbackencoding or encoding
++
++    def restoreenc():
++        hglib._encoding = orig_encoding
++        hglib._fallbackencoding = orig_fallbackencoding
++
++    return with_setup(setenc, restoreenc)
+diff --git a/tests/hglib_encoding_test.py b/tests/hglib_encoding_test.py
+--- a/tests/hglib_encoding_test.py
++++ b/tests/hglib_encoding_test.py
+@@ -1,21 +1,7 @@
+ """Test for encoding helper functions of tortoisehg.util.hglib"""
+ from nose.tools import *
+ from tortoisehg.util import hglib
+-
+-def with_encoding(encoding, fallbackencoding=None):
+-    """Change locale encoding temporarily"""
+-    orig_encoding = hglib._encoding
+-    orig_fallbackencoding = hglib._fallbackencoding
+-
+-    def setenc():
+-        hglib._encoding = encoding
+-        hglib._fallbackencoding = fallbackencoding or encoding
+-
+-    def restoreenc():
+-        hglib._encoding = orig_encoding
+-        hglib._fallbackencoding = orig_fallbackencoding
+-
+-    return with_setup(setenc, restoreenc)
++from tests import with_encoding
+ 
+ JAPANESE_KANA_I = u'\u30a4'  # Japanese katakana "i"
+ 
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287225779 -32400
+# Node ID b2407fdfc5454b1f788abdd316d78d90d0170e88
+# Parent  82aaeaae1ee06eb6a1e1e2eaf14b3b3bbd4dc92a
+manifestmodel: handle non-ascii filenames
+
+diff --git a/tests/fixtures/euc-jp-path.diff b/tests/fixtures/euc-jp-path.diff
+new file mode 100644
+--- /dev/null
++++ b/tests/fixtures/euc-jp-path.diff
+@@ -0,0 +1,9 @@
++# HG changeset patch
++# User test
++# Date 0 0
++# Node ID b124d3c17d0d93e83d26c00d7a176c02c42d73d1
++# Parent  0000000000000000000000000000000000000000
++add aloha
++
++diff --git a/����ϡ� b/����ϡ�
++new file mode 100644
+diff --git a/tests/qt_manifestmodel_test.py b/tests/qt_manifestmodel_test.py
+--- a/tests/qt_manifestmodel_test.py
++++ b/tests/qt_manifestmodel_test.py
+@@ -1,14 +1,18 @@
+ from nose.tools import *
+ from PyQt4.QtCore import QModelIndex
+ from tortoisehg.hgqt.manifestmodel import ManifestModel
+-from tests import get_fixture_repo
++from tests import get_fixture_repo, with_encoding
++
++_aloha_ja = u'\u3042\u308d\u306f\u30fc'
+ 
+ def setup():
+-    global _repo
+-    _repo = get_fixture_repo('subdirs')
++    global _repos
++    _repos = {}
++    for name in ('subdirs', 'euc-jp-path'):
++        _repos[name] = get_fixture_repo(name)
+ 
+-def newmodel(rev=0):
+-    return ManifestModel(_repo, rev=rev)
++def newmodel(name='subdirs', rev=0):
++    return ManifestModel(_repos[name], rev=rev)
+ 
+ def test_data():
+     m = newmodel()
+@@ -26,6 +30,11 @@ def test_data_inexistent():
+     assert_equals(None, m.data(QModelIndex()))
+     assert_equals(None, m.data(m.index(0, 0, m.index(1, 0))))
+ 
++@with_encoding('euc-jp')
++def test_data_eucjp():
++    m = newmodel(name='euc-jp-path')
++    assert_equals(_aloha_ja, m.data(m.index(0, 0)))
++
+ def test_isdir():
+     m = newmodel()
+     assert m.isDir(m.indexFromPath(''))
+@@ -51,6 +60,11 @@ def test_pathfromindex():
+     assert_equals('baz', m.filePath(m.index(0, 0)))
+     assert_equals('baz/bax', m.filePath(m.index(0, 0, m.index(0, 0))))
+ 
++@with_encoding('euc-jp')
++def test_pathfromindex_eucjp():
++    m = newmodel(name='euc-jp-path')
++    assert_equals(_aloha_ja, m.filePath(m.index(0, 0)))
++
+ def test_indexfrompath():
+     m = newmodel()
+     assert_equals(QModelIndex(), m.indexFromPath(''))
+@@ -58,6 +72,11 @@ def test_indexfrompath():
+     assert_equals(m.index(0, 0), m.indexFromPath('baz'))
+     assert_equals(m.index(0, 0, m.index(0, 0)), m.indexFromPath('baz/bax'))
+ 
++@with_encoding('euc-jp')
++def test_indexfrompath_eucjp():
++    m = newmodel(name='euc-jp-path')
++    assert_equals(m.index(0, 0), m.indexFromPath(_aloha_ja))
++
+ def test_removed_should_be_listed():
+     m = newmodel(rev=1)
+     m.setStatusFilter('MARC')
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -211,7 +211,7 @@ class ManifestWidget(QWidget):
+ 
+     @pyqtSlot()
+     def _updatecontent(self):
+-        if self.path not in self._repo[self._rev]:
++        if hglib.fromunicode(self.path) not in self._repo[self._rev]:
+             self._contentview.setCurrentWidget(self._nullcontent)
+             return
+ 
+diff --git a/tortoisehg/hgqt/manifestmodel.py b/tortoisehg/hgqt/manifestmodel.py
+--- a/tortoisehg/hgqt/manifestmodel.py
++++ b/tortoisehg/hgqt/manifestmodel.py
+@@ -14,6 +14,7 @@ from PyQt4.QtCore import *
+ from PyQt4.QtGui import *
+ 
+ from mercurial import util
++from tortoisehg.util import hglib
+ from tortoisehg.hgqt import qtlib, status, visdiff
+ 
+ class ManifestModel(QAbstractItemModel):
+@@ -48,6 +49,7 @@ class ManifestModel(QAbstractItemModel):
+             return e.name
+ 
+     def filePath(self, index):
++        """Return path at the given index [unicode]"""
+         if not index.isValid():
+             return ''
+ 
+@@ -112,7 +114,10 @@ class ManifestModel(QAbstractItemModel):
+             return QModelIndex()
+ 
+     def indexFromPath(self, path, column=0):
+-        """Return index for the specified path if found; otherwise invalid index"""
++        """Return index for the specified path if found [unicode]
++
++        If not found, returns invalid index.
++        """
+         if not path:
+             return QModelIndex()
+ 
+@@ -194,7 +199,7 @@ class ManifestModel(QAbstractItemModel):
+                 continue
+ 
+             e = roote
+-            for p in path.split('/'):
++            for p in hglib.tounicode(path).split('/'):
+                 if not p in e:
+                     e.addchild(p)
+                 e = e[p]
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287226428 -32400
+# Node ID af058efb9e8220d35e6a074ed7c1ee2efeb2c0c0
+# Parent  b2407fdfc5454b1f788abdd316d78d90d0170e88
+manifestmodel: make indexFromPath accept QString
+
+diff --git a/tests/qt_manifestmodel_test.py b/tests/qt_manifestmodel_test.py
+--- a/tests/qt_manifestmodel_test.py
++++ b/tests/qt_manifestmodel_test.py
+@@ -1,5 +1,5 @@
+ from nose.tools import *
+-from PyQt4.QtCore import QModelIndex
++from PyQt4.QtCore import QModelIndex, QString
+ from tortoisehg.hgqt.manifestmodel import ManifestModel
+ from tests import get_fixture_repo, with_encoding
+ 
+@@ -72,6 +72,10 @@ def test_indexfrompath():
+     assert_equals(m.index(0, 0), m.indexFromPath('baz'))
+     assert_equals(m.index(0, 0, m.index(0, 0)), m.indexFromPath('baz/bax'))
+ 
++def test_indexfrompath_qstr():
++    m = newmodel()
++    assert_equals(m.index(1, 0), m.indexFromPath(QString('bar')))
++
+ @with_encoding('euc-jp')
+ def test_indexfrompath_eucjp():
+     m = newmodel(name='euc-jp-path')
+diff --git a/tortoisehg/hgqt/manifestmodel.py b/tortoisehg/hgqt/manifestmodel.py
+--- a/tortoisehg/hgqt/manifestmodel.py
++++ b/tortoisehg/hgqt/manifestmodel.py
+@@ -122,7 +122,7 @@ class ManifestModel(QAbstractItemModel):
+             return QModelIndex()
+ 
+         e = self._rootentry
+-        paths = path and path.split('/') or []
++        paths = path and unicode(path).split('/') or []
+         try:
+             for p in paths:
+                 e = e[p]
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287226748 -32400
+# Node ID 31757235c7e0505787106edd8d1d85283c98b05e
+# Parent  af058efb9e8220d35e6a074ed7c1ee2efeb2c0c0
+annotate: make AnnotateView handle switching revision by itself
+
+Since it knows the repo, it's simple enough to switch revision by itself.
+
+diff --git a/tortoisehg/hgqt/annotate.py b/tortoisehg/hgqt/annotate.py
+--- a/tortoisehg/hgqt/annotate.py
++++ b/tortoisehg/hgqt/annotate.py
+@@ -29,7 +29,6 @@ class AnnotateView(qscilib.Scintilla):
+     searchRequested = pyqtSignal(QString)
+     """Emitted (pattern) when user request to search content"""
+ 
+-    revSelected = pyqtSignal(object)
+     editSelected = pyqtSignal(object)
+ 
+     grepRequested = pyqtSignal(QString, dict)
+@@ -119,7 +118,7 @@ class AnnotateView(qscilib.Scintilla):
+                 add(name, func)
+ 
+         def annorig():
+-            self.revSelected.emit(data)
++            self.setSource(*data)
+         def editorig():
+             self.editSelected.emit(data)
+         menu.addSeparator()
+@@ -132,7 +131,7 @@ class AnnotateView(qscilib.Scintilla):
+         for pfctx in fctx.parents():
+             pdata = [pfctx.path(), pfctx.changectx().rev(), line]
+             def annparent(data):
+-                self.revSelected.emit(data)
++                self.setSource(*data)
+             def editparent(data):
+                 self.editSelected.emit(data)
+             for name, func in [(_('Annotate parent revision %d') % pdata[1],
+@@ -478,7 +477,6 @@ class AnnotateDialog(QMainWindow):
+         status = QStatusBar()
+         self.setStatusBar(status)
+         av.revisionHint.connect(status.showMessage)
+-        av.revSelected.connect(lambda data: self.av.setSource(*data))
+         av.editSelected.connect(self.editSelected)
+         av.grepRequested.connect(self._openSearchWidget)
+ 
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -132,8 +132,8 @@ class ManifestWidget(QWidget):
+         self._nullcontent = QWidget()
+         self._contentview.addWidget(self._nullcontent)
+         self._fileview = annotate.AnnotateView(self._repo)
++        self._fileview.sourceChanged.connect(self.setSource)
+         self._contentview.addWidget(self._fileview)
+-        self._fileview.revSelected.connect(lambda a: self.setSource(*a[:3]))
+         for name in ('revisionHint', 'searchRequested', 'grepRequested'):
+             getattr(self._fileview, name).connect(getattr(self, name))
+ 
+@@ -189,15 +189,18 @@ class ManifestWidget(QWidget):
+         """Change revision to show"""
+         self.setSource(self.path, rev)
+ 
++    @pyqtSlot(unicode, object)
+     @pyqtSlot(unicode, object, int)
+     def setSource(self, path, rev, line=None):
+         """Change path and revision to show at once"""
+-        if self._rev != rev:
++        revchanged = self._rev != rev
++        if revchanged:
+             self._rev = rev
+             self._setupmodel()
+-            self.revchanged.emit(rev)
+         self.setpath(path)
+         self._fileview.setSource(path, rev, line)
++        if revchanged:
++            self.revchanged.emit(rev)
+ 
+     @property
+     def path(self):
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287228238 -32400
+# Node ID 5b746687277cf5caf0669d723b378aebf0a972f4
+# Parent  31757235c7e0505787106edd8d1d85283c98b05e
+annotate: fix encoding of path emitted from context menu
+
+diff --git a/tortoisehg/hgqt/annotate.py b/tortoisehg/hgqt/annotate.py
+--- a/tortoisehg/hgqt/annotate.py
++++ b/tortoisehg/hgqt/annotate.py
+@@ -97,7 +97,7 @@ class AnnotateView(qscilib.Scintilla):
+             return menu.exec_(point)
+ 
+         fctx, line = self._links[line]
+-        data = [fctx.path(), fctx.linkrev(), line]
++        data = [hglib.tounicode(fctx.path()), fctx.linkrev(), line]
+ 
+         if self.hasSelectedText():
+             selection = self.selectedText()
+@@ -129,7 +129,8 @@ class AnnotateView(qscilib.Scintilla):
+                 action.triggered.connect(func)
+             add(name, func)
+         for pfctx in fctx.parents():
+-            pdata = [pfctx.path(), pfctx.changectx().rev(), line]
++            pdata = [hglib.tounicode(pfctx.path()), pfctx.changectx().rev(),
++                     line]
+             def annparent(data):
+                 self.setSource(*data)
+             def editparent(data):
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287229266 -32400
+# Node ID 11f8741a3d8ee1ea5f50326099ff127bd8cd6e02
+# Parent  5b746687277cf5caf0669d723b378aebf0a972f4
+annotate: extract arguments of editSelected signal
+
+diff --git a/tortoisehg/hgqt/annotate.py b/tortoisehg/hgqt/annotate.py
+--- a/tortoisehg/hgqt/annotate.py
++++ b/tortoisehg/hgqt/annotate.py
+@@ -29,7 +29,8 @@ class AnnotateView(qscilib.Scintilla):
+     searchRequested = pyqtSignal(QString)
+     """Emitted (pattern) when user request to search content"""
+ 
+-    editSelected = pyqtSignal(object)
++    editSelected = pyqtSignal(unicode, object, int)
++    """Emitted (path, rev, line) when user requests to open editor"""
+ 
+     grepRequested = pyqtSignal(QString, dict)
+     """Emitted (pattern, **opts) when user request to search changelog"""
+@@ -120,7 +121,7 @@ class AnnotateView(qscilib.Scintilla):
+         def annorig():
+             self.setSource(*data)
+         def editorig():
+-            self.editSelected.emit(data)
++            self.editSelected.emit(*data)
+         menu.addSeparator()
+         for name, func in [(_('Annotate originating revision'), annorig),
+                            (_('View originating revision'), editorig)]:
+@@ -134,7 +135,7 @@ class AnnotateView(qscilib.Scintilla):
+             def annparent(data):
+                 self.setSource(*data)
+             def editparent(data):
+-                self.editSelected.emit(data)
++                self.editSelected.emit(*data)
+             for name, func in [(_('Annotate parent revision %d') % pdata[1],
+                                   annparent),
+                                (_('View parent revision %d') % pdata[1],
+@@ -511,10 +512,10 @@ class AnnotateDialog(QMainWindow):
+         self.storeSettings()
+         super(AnnotateDialog, self).closeEvent(event)
+ 
+-    def editSelected(self, args):
++    def editSelected(self, wfile, rev, line):
+         pattern = hglib.fromunicode(self._searchbar._le.text()) or None
++        wfile = hglib.fromunicode(wfile)
+         repo = self.repo
+-        wfile, rev, line = args
+         try:
+             ctx = repo[rev]
+             fctx = ctx[wfile]
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287231363 -32400
+# Node ID 41a330a6c6dbadcaaf6c18502427be6e5358099c
+# Parent  11f8741a3d8ee1ea5f50326099ff127bd8cd6e02
+manifestdialog: implement menu action to open specified content in editor
+
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -17,6 +17,7 @@
+ """
+ Qt4 dialogs to display hg revisions of a file
+ """
++import os
+ 
+ from mercurial import util
+ 
+@@ -25,7 +26,7 @@ from PyQt4.QtGui import *
+ 
+ from tortoisehg.util import paths, hglib
+ 
+-from tortoisehg.hgqt import qtlib, annotate, status, thgrepo
++from tortoisehg.hgqt import qtlib, annotate, status, thgrepo, visdiff, wctxactions
+ from tortoisehg.hgqt.i18n import _
+ from tortoisehg.hgqt.manifestmodel import ManifestModel
+ 
+@@ -40,6 +41,7 @@ class ManifestDialog(QMainWindow):
+ 
+         self._manifest_widget = ManifestWidget(ui, repo, rev)
+         self._manifest_widget.revchanged.connect(self._updatewindowtitle)
++        self._manifest_widget.editSelected.connect(self._openInEditor)
+         self._manifest_widget.grepRequested.connect(self._openSearchWidget)
+         self.setCentralWidget(self._manifest_widget)
+         self.addToolBar(self._manifest_widget.toolbar)
+@@ -83,6 +85,12 @@ class ManifestDialog(QMainWindow):
+         from tortoisehg.hgqt import run
+         run.grep(self._repo.ui, hglib.fromunicode(pattern), **opts)
+ 
++    @pyqtSlot(unicode, object, int)
++    def _openInEditor(self, path, rev, line):
++        """Open editor to show the specified file"""
++        _openineditor(self._repo, path, rev, line,
++                      pattern=self._searchbar.pattern(), parent=self)
++
+ class ManifestWidget(QWidget):
+     """Display file tree and contents at the specified revision"""
+     revchanged = pyqtSignal(object)  # emit when curret revision changed
+@@ -93,6 +101,9 @@ class ManifestWidget(QWidget):
+     searchRequested = pyqtSignal(unicode)
+     """Emitted (pattern) when user request to search content"""
+ 
++    editSelected = pyqtSignal(unicode, object, int)
++    """Emitted (path, rev, line) when user requests to open editor"""
++
+     grepRequested = pyqtSignal(unicode, dict)
+     """Emitted (pattern, opts) when user request to search changelog"""
+ 
+@@ -134,7 +145,8 @@ class ManifestWidget(QWidget):
+         self._fileview = annotate.AnnotateView(self._repo)
+         self._fileview.sourceChanged.connect(self.setSource)
+         self._contentview.addWidget(self._fileview)
+-        for name in ('revisionHint', 'searchRequested', 'grepRequested'):
++        for name in ('revisionHint', 'searchRequested', 'editSelected',
++                     'grepRequested'):
+             getattr(self._fileview, name).connect(getattr(self, name))
+ 
+     def _initactions(self):
+@@ -273,6 +285,10 @@ class _StatusFilterButton(QToolButton):
+ class ManifestTaskWidget(ManifestWidget):
+     """Manifest widget designed for task tab"""
+ 
++    def __init__(self, ui, repo, rev=None, parent=None):
++        super(ManifestTaskWidget, self).__init__(ui, repo, rev, parent)
++        self.editSelected.connect(self._openInEditor)
++
+     @pyqtSlot()
+     def showSearchBar(self):
+         self._searchbar.show()
+@@ -286,12 +302,26 @@ class ManifestTaskWidget(ManifestWidget)
+         connectsearchbar(self, searchbar)
+         return searchbar
+ 
++    @pyqtSlot(unicode, object, int)
++    def _openInEditor(self, path, rev, line):
++        """Open editor to show the specified file"""
++        _openineditor(self._repo, path, rev, line,
++                      pattern=self._searchbar.pattern(), parent=self)
++
+ def connectsearchbar(manifestwidget, searchbar):
+     """Connect searchbar to manifest widget"""
+     searchbar.conditionChanged.connect(manifestwidget.highlightText)
+     searchbar.searchRequested.connect(manifestwidget.searchText)
+     manifestwidget.searchRequested.connect(searchbar.search)
+ 
++def _openineditor(repo, path, rev, line=None, pattern=None, parent=None):
++    """Open editor to show the specified file [unicode]"""
++    path = hglib.fromunicode(path)
++    pattern = hglib.fromunicode(pattern)
++    base = visdiff.snapshot(repo, [path], repo[rev])[0]
++    files = [os.path.join(base, path)]
++    wctxactions.edit(parent, repo.ui, repo, files, line, pattern)
++
+ 
+ def run(ui, *pats, **opts):
+     repo = opts.get('repo') or thgrepo.repository(ui, paths.find_root())
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287236702 -32400
+# Node ID 703fa6f5100fa67be50808b1995c7d6391631000
+# Parent  41a330a6c6dbadcaaf6c18502427be6e5358099c
+qscilib: implement QTextEdit-like find() method
+
+diff --git a/tortoisehg/hgqt/qscilib.py b/tortoisehg/hgqt/qscilib.py
+--- a/tortoisehg/hgqt/qscilib.py
++++ b/tortoisehg/hgqt/qscilib.py
+@@ -118,6 +118,8 @@ class Scintilla(QsciScintilla):
+     def __init__(self, parent=None):
+         super(Scintilla, self).__init__(parent)
+         self.setUtf8(True)
++        self.textChanged.connect(self._resetfindcond)
++        self._resetfindcond()
+ 
+     def inputMethodQuery(self, query):
+         if query == Qt.ImMicroFocus:
+@@ -176,3 +178,21 @@ class Scintilla(QsciScintilla):
+         menu.addAction(_('Select All'), self.selectAll, QKeySequence.SelectAll)
+ 
+         return menu
++
++    @pyqtSlot(unicode, bool, bool, bool)
++    def find(self, exp, icase=True, wrap=False, forward=True):
++        """Find the next/prev occurence; returns True if found
++
++        This method tries to imitate the behavior of QTextEdit.find(),
++        unlike combo of QsciScintilla.findFirst() and findNext().
++        """
++        cond = (exp, True, not icase, False, wrap, forward)
++        if cond == self.__findcond:
++            return self.findNext()
++        else:
++            self.__findcond = cond
++            return self.findFirst(*cond)
++
++    @pyqtSlot()
++    def _resetfindcond(self):
++        self.__findcond = ()
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287237250 -32400
+# Node ID 00da69bf6ce826e7a0e7e2f7f586426f77eab531
+# Parent  703fa6f5100fa67be50808b1995c7d6391631000
+annotate, manifestdialog: use wrapped Scintilla.find()
+
+diff --git a/tortoisehg/hgqt/annotate.py b/tortoisehg/hgqt/annotate.py
+--- a/tortoisehg/hgqt/annotate.py
++++ b/tortoisehg/hgqt/annotate.py
+@@ -299,11 +299,6 @@ class AnnotateView(qscilib.Scintilla):
+     def prevMatch(self):
+         pass # XXX
+ 
+-    @pyqtSlot(unicode, bool, bool)
+-    def searchText(self, match, icase=False, wrap=False):
+-        """Search text matching to the givne regexp pattern [unicode]"""
+-        self.findFirst(match, True, not icase, False, wrap)
+-
+     @pyqtSlot(unicode, bool)
+     def highlightText(self, match, icase=False):
+         """Highlight text matching to the given regexp pattern [unicode]
+@@ -392,8 +387,8 @@ class SearchToolBar(QToolBar):
+     conditionChanged = pyqtSignal(unicode, bool, bool)
+     """Emitted (pattern, icase, wrap) when search condition changed"""
+ 
+-    searchRequested = pyqtSignal(unicode, bool, bool)
+-    """Emitted (pattern, icase, wrap) when search button pressed"""
++    searchRequested = pyqtSignal(unicode, bool, bool, bool)
++    """Emitted (pattern, icase, wrap, forward) when requested"""
+ 
+     def __init__(self, parent=None, hidable=False):
+         super(SearchToolBar, self).__init__(_('Search'), parent,
+@@ -432,9 +427,9 @@ class SearchToolBar(QToolBar):
+                                    self.wrapAround())
+ 
+     @pyqtSlot()
+-    def _emitSearchRequested(self):
++    def _emitSearchRequested(self, forward=True):
+         self.searchRequested.emit(self.pattern(), self.caseInsensitive(),
+-                                  self.wrapAround())
++                                  self.wrapAround(), forward)
+ 
+     def pattern(self):
+         """Returns the current search pattern [unicode]"""
+@@ -485,7 +480,7 @@ class AnnotateDialog(QMainWindow):
+         self._searchbar = SearchToolBar()
+         self.addToolBar(self._searchbar)
+         self._searchbar.setPattern(hglib.tounicode(opts.get('pattern', '')))
+-        self._searchbar.searchRequested.connect(self.av.searchText)
++        self._searchbar.searchRequested.connect(self.av.find)
+         self._searchbar.conditionChanged.connect(self.av.highlightText)
+         av.searchRequested.connect(self._searchbar.search)
+ 
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -163,9 +163,9 @@ class ManifestWidget(QWidget):
+         """Return toolbar for manifest widget"""
+         return self._toolbar
+ 
+-    @pyqtSlot(unicode, bool, bool)
+-    def searchText(self, pattern, icase=False, wrap=False):
+-        self._fileview.searchText(pattern, icase, wrap)
++    @pyqtSlot(unicode, bool, bool, bool)
++    def find(self, pattern, icase=False, wrap=False, forward=True):
++        return self._fileview.find(pattern, icase, wrap, forward)
+ 
+     @pyqtSlot(unicode, bool)
+     def highlightText(self, pattern, icase=False):
+@@ -311,7 +311,7 @@ class ManifestTaskWidget(ManifestWidget)
+ def connectsearchbar(manifestwidget, searchbar):
+     """Connect searchbar to manifest widget"""
+     searchbar.conditionChanged.connect(manifestwidget.highlightText)
+-    searchbar.searchRequested.connect(manifestwidget.searchText)
++    searchbar.searchRequested.connect(manifestwidget.find)
+     manifestwidget.searchRequested.connect(searchbar.search)
+ 
+ def _openineditor(repo, path, rev, line=None, pattern=None, parent=None):
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287237842 -32400
+# Node ID 7767b41ae8046bae2432cb1cd189937bef69a018
+# Parent  00da69bf6ce826e7a0e7e2f7f586426f77eab531
+annotate: implement find prev with unifying events to SearchToolBar
+
+- move keyPressEvent and wheelEvent to SearchToolBar
+- emit searchRequested signal accordingly
+
+diff --git a/tortoisehg/hgqt/annotate.py b/tortoisehg/hgqt/annotate.py
+--- a/tortoisehg/hgqt/annotate.py
++++ b/tortoisehg/hgqt/annotate.py
+@@ -66,12 +66,6 @@ class AnnotateView(qscilib.Scintilla):
+         if event.key() == Qt.Key_Escape:
+             self._thread.abort()
+             return
+-        if event.matches(QKeySequence.FindNext):
+-            self.nextMatch()
+-            return
+-        if event.matches(QKeySequence.FindPrevious):
+-            self.prevMatch()
+-            return
+         return super(AnnotateView, self).keyPressEvent(event)
+ 
+     def mouseMoveEvent(self, event):
+@@ -293,12 +287,6 @@ class AnnotateView(qscilib.Scintilla):
+         else:
+             self.setMarginWidth(2, 0)
+ 
+-    def nextMatch(self):
+-        self.findNext()
+-
+-    def prevMatch(self):
+-        pass # XXX
+-
+     @pyqtSlot(unicode, bool)
+     def highlightText(self, match, icase=False):
+         """Highlight text matching to the given regexp pattern [unicode]
+@@ -421,6 +409,24 @@ class SearchToolBar(QToolBar):
+ 
+         self.setFocusProxy(self._le)
+ 
++    def keyPressEvent(self, event):
++        if event.matches(QKeySequence.FindNext):
++            self._emitSearchRequested(forward=True)
++            return
++        if event.matches(QKeySequence.FindPrevious):
++            self._emitSearchRequested(forward=False)
++            return
++        super(SearchToolBar, self).keyPressEvent(event)
++
++    def wheelEvent(self, event):
++        if event.delta() > 0:
++            self._emitSearchRequested(forward=False)
++            return
++        if event.delta() < 0:
++            self._emitSearchRequested(forward=True)
++            return
++        super(SearchToolBar, self).wheelEvent(event)
++
+     @pyqtSlot()
+     def _emitConditionChanged(self):
+         self.conditionChanged.emit(self.pattern(), self.caseInsensitive(),
+@@ -533,15 +539,6 @@ class AnnotateDialog(QMainWindow):
+             self.searchwidget.show()
+             self.searchwidget.raise_()
+ 
+-    def wheelEvent(self, event):
+-        if self.childAt(event.pos()) != self._searchbar._le:
+-            event.ignore()
+-            return
+-        if event.delta() > 0:
+-            self.av.prevMatch()
+-        elif event.delta() < 0:
+-            self.av.nextMatch()
+-
+     def storeSettings(self):
+         s = QSettings()
+         s.setValue('annotate/geom', self.saveGeometry())
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287239746 -32400
+# Node ID 567c07135006f8961672fea5e06ea1b0891e192c
+# Parent  7767b41ae8046bae2432cb1cd189937bef69a018
+qscilib: move SearchToolBar to qscilib
+
+It will be useful for widgets using qscilib.Scintilla.
+
+diff --git a/tortoisehg/hgqt/annotate.py b/tortoisehg/hgqt/annotate.py
+--- a/tortoisehg/hgqt/annotate.py
++++ b/tortoisehg/hgqt/annotate.py
+@@ -371,100 +371,6 @@ class _AnnotateThread(QThread):
+             del self._threadid
+             del self._fctx
+ 
+-class SearchToolBar(QToolBar):
+-    conditionChanged = pyqtSignal(unicode, bool, bool)
+-    """Emitted (pattern, icase, wrap) when search condition changed"""
+-
+-    searchRequested = pyqtSignal(unicode, bool, bool, bool)
+-    """Emitted (pattern, icase, wrap, forward) when requested"""
+-
+-    def __init__(self, parent=None, hidable=False):
+-        super(SearchToolBar, self).__init__(_('Search'), parent,
+-                                            objectName='search',
+-                                            iconSize=QSize(16, 16))
+-        if hidable:
+-            self._close_button = QToolButton(icon=qtlib.geticon('close'),
+-                                             shortcut=Qt.Key_Escape)
+-            self._close_button.clicked.connect(self.hide)
+-            self.addWidget(self._close_button)
+-
+-        self._lbl = QLabel(_('Regexp:'),
+-                           toolTip=_('Regular expression search pattern'))
+-        self.addWidget(self._lbl)
+-        self._le = QLineEdit()
+-        self._le.textChanged.connect(self._emitConditionChanged)
+-        self._le.returnPressed.connect(self._emitSearchRequested)
+-        self._lbl.setBuddy(self._le)
+-        self.addWidget(self._le)
+-        self._chk = QCheckBox(_('Ignore case'))
+-        self._chk.toggled.connect(self._emitConditionChanged)
+-        self.addWidget(self._chk)
+-        self._wrapchk = QCheckBox(_('Wrap search'))
+-        self._wrapchk.toggled.connect(self._emitConditionChanged)
+-        self.addWidget(self._wrapchk)
+-        self._bt = QPushButton(_('Search'), enabled=False)
+-        self._bt.clicked.connect(self._emitSearchRequested)
+-        self._le.textChanged.connect(lambda s: self._bt.setEnabled(bool(s)))
+-        self.addWidget(self._bt)
+-
+-        self.setFocusProxy(self._le)
+-
+-    def keyPressEvent(self, event):
+-        if event.matches(QKeySequence.FindNext):
+-            self._emitSearchRequested(forward=True)
+-            return
+-        if event.matches(QKeySequence.FindPrevious):
+-            self._emitSearchRequested(forward=False)
+-            return
+-        super(SearchToolBar, self).keyPressEvent(event)
+-
+-    def wheelEvent(self, event):
+-        if event.delta() > 0:
+-            self._emitSearchRequested(forward=False)
+-            return
+-        if event.delta() < 0:
+-            self._emitSearchRequested(forward=True)
+-            return
+-        super(SearchToolBar, self).wheelEvent(event)
+-
+-    @pyqtSlot()
+-    def _emitConditionChanged(self):
+-        self.conditionChanged.emit(self.pattern(), self.caseInsensitive(),
+-                                   self.wrapAround())
+-
+-    @pyqtSlot()
+-    def _emitSearchRequested(self, forward=True):
+-        self.searchRequested.emit(self.pattern(), self.caseInsensitive(),
+-                                  self.wrapAround(), forward)
+-
+-    def pattern(self):
+-        """Returns the current search pattern [unicode]"""
+-        return self._le.text()
+-
+-    def setPattern(self, text):
+-        """Set the search pattern [unicode]"""
+-        self._le.setText(text)
+-
+-    def caseInsensitive(self):
+-        """True if case-insensitive search is requested"""
+-        return self._chk.isChecked()
+-
+-    def setCaseInsensitive(self, icase):
+-        self._chk.setChecked(icase)
+-
+-    def wrapAround(self):
+-        """True if wrap search is requested"""
+-        return self._wrapchk.isChecked()
+-
+-    def setWrapAround(self, wrap):
+-        self._wrapchk.setChecked(wrap)
+-
+-    @pyqtSlot(unicode)
+-    def search(self, text):
+-        """Request search with the given pattern"""
+-        self.setPattern(text)
+-        self._emitSearchRequested()
+-
+ class AnnotateDialog(QMainWindow):
+     def __init__(self, *pats, **opts):
+         super(AnnotateDialog,self).__init__(opts.get('parent'), Qt.Window)
+@@ -483,7 +389,7 @@ class AnnotateDialog(QMainWindow):
+         av.editSelected.connect(self.editSelected)
+         av.grepRequested.connect(self._openSearchWidget)
+ 
+-        self._searchbar = SearchToolBar()
++        self._searchbar = qscilib.SearchToolBar()
+         self.addToolBar(self._searchbar)
+         self._searchbar.setPattern(hglib.tounicode(opts.get('pattern', '')))
+         self._searchbar.searchRequested.connect(self.av.find)
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -26,7 +26,8 @@ from PyQt4.QtGui import *
+ 
+ from tortoisehg.util import paths, hglib
+ 
+-from tortoisehg.hgqt import qtlib, annotate, status, thgrepo, visdiff, wctxactions
++from tortoisehg.hgqt import qtlib, qscilib, annotate, status, thgrepo, \
++                            visdiff, wctxactions
+ from tortoisehg.hgqt.i18n import _
+ from tortoisehg.hgqt.manifestmodel import ManifestModel
+ 
+@@ -46,7 +47,7 @@ class ManifestDialog(QMainWindow):
+         self.setCentralWidget(self._manifest_widget)
+         self.addToolBar(self._manifest_widget.toolbar)
+ 
+-        self._searchbar = annotate.SearchToolBar()
++        self._searchbar = qscilib.SearchToolBar()
+         connectsearchbar(self._manifest_widget, self._searchbar)
+         self.addToolBar(self._searchbar)
+ 
+@@ -296,7 +297,7 @@ class ManifestTaskWidget(ManifestWidget)
+ 
+     @util.propertycache
+     def _searchbar(self):
+-        searchbar = annotate.SearchToolBar(hidable=True)
++        searchbar = qscilib.SearchToolBar(hidable=True)
+         searchbar.hide()
+         self.layout().addWidget(searchbar)
+         connectsearchbar(self, searchbar)
+diff --git a/tortoisehg/hgqt/qscilib.py b/tortoisehg/hgqt/qscilib.py
+--- a/tortoisehg/hgqt/qscilib.py
++++ b/tortoisehg/hgqt/qscilib.py
+@@ -7,6 +7,7 @@
+ 
+ from mercurial import util
+ 
++from tortoisehg.hgqt import qtlib
+ from tortoisehg.hgqt.i18n import _
+ 
+ from PyQt4.QtCore import *
+@@ -196,3 +197,97 @@ class Scintilla(QsciScintilla):
+     @pyqtSlot()
+     def _resetfindcond(self):
+         self.__findcond = ()
++
++class SearchToolBar(QToolBar):
++    conditionChanged = pyqtSignal(unicode, bool, bool)
++    """Emitted (pattern, icase, wrap) when search condition changed"""
++
++    searchRequested = pyqtSignal(unicode, bool, bool, bool)
++    """Emitted (pattern, icase, wrap, forward) when requested"""
++
++    def __init__(self, parent=None, hidable=False):
++        super(SearchToolBar, self).__init__(_('Search'), parent,
++                                            objectName='search',
++                                            iconSize=QSize(16, 16))
++        if hidable:
++            self._close_button = QToolButton(icon=qtlib.geticon('close'),
++                                             shortcut=Qt.Key_Escape)
++            self._close_button.clicked.connect(self.hide)
++            self.addWidget(self._close_button)
++
++        self._lbl = QLabel(_('Regexp:'),
++                           toolTip=_('Regular expression search pattern'))
++        self.addWidget(self._lbl)
++        self._le = QLineEdit()
++        self._le.textChanged.connect(self._emitConditionChanged)
++        self._le.returnPressed.connect(self._emitSearchRequested)
++        self._lbl.setBuddy(self._le)
++        self.addWidget(self._le)
++        self._chk = QCheckBox(_('Ignore case'))
++        self._chk.toggled.connect(self._emitConditionChanged)
++        self.addWidget(self._chk)
++        self._wrapchk = QCheckBox(_('Wrap search'))
++        self._wrapchk.toggled.connect(self._emitConditionChanged)
++        self.addWidget(self._wrapchk)
++        self._bt = QPushButton(_('Search'), enabled=False)
++        self._bt.clicked.connect(self._emitSearchRequested)
++        self._le.textChanged.connect(lambda s: self._bt.setEnabled(bool(s)))
++        self.addWidget(self._bt)
++
++        self.setFocusProxy(self._le)
++
++    def keyPressEvent(self, event):
++        if event.matches(QKeySequence.FindNext):
++            self._emitSearchRequested(forward=True)
++            return
++        if event.matches(QKeySequence.FindPrevious):
++            self._emitSearchRequested(forward=False)
++            return
++        super(SearchToolBar, self).keyPressEvent(event)
++
++    def wheelEvent(self, event):
++        if event.delta() > 0:
++            self._emitSearchRequested(forward=False)
++            return
++        if event.delta() < 0:
++            self._emitSearchRequested(forward=True)
++            return
++        super(SearchToolBar, self).wheelEvent(event)
++
++    @pyqtSlot()
++    def _emitConditionChanged(self):
++        self.conditionChanged.emit(self.pattern(), self.caseInsensitive(),
++                                   self.wrapAround())
++
++    @pyqtSlot()
++    def _emitSearchRequested(self, forward=True):
++        self.searchRequested.emit(self.pattern(), self.caseInsensitive(),
++                                  self.wrapAround(), forward)
++
++    def pattern(self):
++        """Returns the current search pattern [unicode]"""
++        return self._le.text()
++
++    def setPattern(self, text):
++        """Set the search pattern [unicode]"""
++        self._le.setText(text)
++
++    def caseInsensitive(self):
++        """True if case-insensitive search is requested"""
++        return self._chk.isChecked()
++
++    def setCaseInsensitive(self, icase):
++        self._chk.setChecked(icase)
++
++    def wrapAround(self):
++        """True if wrap search is requested"""
++        return self._wrapchk.isChecked()
++
++    def setWrapAround(self, wrap):
++        self._wrapchk.setChecked(wrap)
++
++    @pyqtSlot(unicode)
++    def search(self, text):
++        """Request search with the given pattern"""
++        self.setPattern(text)
++        self._emitSearchRequested()
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287243706 -32400
+# Node ID 60458f5cb32ed90b147247c1fdad6270b73430e5
+# Parent  567c07135006f8961672fea5e06ea1b0891e192c
+qscilib: save search options by SearchToolBar
+
+ - move codes from annotate
+ - additionally save icase option
+ - make sure conditionChanged not emitted by initial loading of options
+
+diff --git a/tortoisehg/hgqt/annotate.py b/tortoisehg/hgqt/annotate.py
+--- a/tortoisehg/hgqt/annotate.py
++++ b/tortoisehg/hgqt/annotate.py
+@@ -448,13 +448,10 @@ class AnnotateDialog(QMainWindow):
+     def storeSettings(self):
+         s = QSettings()
+         s.setValue('annotate/geom', self.saveGeometry())
+-        s.setValue('annotate/wrap', self._searchbar.wrapAround())
+ 
+     def restoreSettings(self):
+         s = QSettings()
+         self.restoreGeometry(s.value('annotate/geom').toByteArray())
+-        wrap = s.value('annotate/wrap', False).toBool()
+-        self._searchbar.setWrapAround(wrap)
+ 
+ def run(ui, *pats, **opts):
+     pats = hglib.canonpaths(pats)
+diff --git a/tortoisehg/hgqt/qscilib.py b/tortoisehg/hgqt/qscilib.py
+--- a/tortoisehg/hgqt/qscilib.py
++++ b/tortoisehg/hgqt/qscilib.py
+@@ -205,7 +205,7 @@ class SearchToolBar(QToolBar):
+     searchRequested = pyqtSignal(unicode, bool, bool, bool)
+     """Emitted (pattern, icase, wrap, forward) when requested"""
+ 
+-    def __init__(self, parent=None, hidable=False):
++    def __init__(self, parent=None, hidable=False, settings=None):
+         super(SearchToolBar, self).__init__(_('Search'), parent,
+                                             objectName='search',
+                                             iconSize=QSize(16, 16))
+@@ -219,15 +219,12 @@ class SearchToolBar(QToolBar):
+                            toolTip=_('Regular expression search pattern'))
+         self.addWidget(self._lbl)
+         self._le = QLineEdit()
+-        self._le.textChanged.connect(self._emitConditionChanged)
+         self._le.returnPressed.connect(self._emitSearchRequested)
+         self._lbl.setBuddy(self._le)
+         self.addWidget(self._le)
+         self._chk = QCheckBox(_('Ignore case'))
+-        self._chk.toggled.connect(self._emitConditionChanged)
+         self.addWidget(self._chk)
+         self._wrapchk = QCheckBox(_('Wrap search'))
+-        self._wrapchk.toggled.connect(self._emitConditionChanged)
+         self.addWidget(self._wrapchk)
+         self._bt = QPushButton(_('Search'), enabled=False)
+         self._bt.clicked.connect(self._emitSearchRequested)
+@@ -236,6 +233,18 @@ class SearchToolBar(QToolBar):
+ 
+         self.setFocusProxy(self._le)
+ 
++        def defaultsettings():
++            s = QSettings()
++            s.beginGroup('searchtoolbar')
++            return s
++        self._settings = settings or defaultsettings()
++        self.searchRequested.connect(self._writesettings)
++        self._readsettings()
++
++        self._le.textChanged.connect(self._emitConditionChanged)
++        self._chk.toggled.connect(self._emitConditionChanged)
++        self._wrapchk.toggled.connect(self._emitConditionChanged)
++
+     def keyPressEvent(self, event):
+         if event.matches(QKeySequence.FindNext):
+             self._emitSearchRequested(forward=True)
+@@ -254,6 +263,15 @@ class SearchToolBar(QToolBar):
+             return
+         super(SearchToolBar, self).wheelEvent(event)
+ 
++    def _readsettings(self):
++        self.setCaseInsensitive(self._settings.value('icase', False).toBool())
++        self.setWrapAround(self._settings.value('wrap', False).toBool())
++
++    @pyqtSlot()
++    def _writesettings(self):
++        self._settings.setValue('icase', self.caseInsensitive())
++        self._settings.setValue('wrap', self.wrapAround())
++
+     @pyqtSlot()
+     def _emitConditionChanged(self):
+         self.conditionChanged.emit(self.pattern(), self.caseInsensitive(),
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287244893 -32400
+# Node ID df23f6546036b75c0908e52bb1dcc4d215c53f74
+# Parent  60458f5cb32ed90b147247c1fdad6270b73430e5
+manifestdialog: rename methods and signals of ManifestWidget to camelCase
+
+It seems Qt-level API works better with Qt-style naming.
+
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -41,7 +41,7 @@ class ManifestDialog(QMainWindow):
+         self.resize(400, 300)
+ 
+         self._manifest_widget = ManifestWidget(ui, repo, rev)
+-        self._manifest_widget.revchanged.connect(self._updatewindowtitle)
++        self._manifest_widget.revChanged.connect(self._updatewindowtitle)
+         self._manifest_widget.editSelected.connect(self._openInEditor)
+         self._manifest_widget.grepRequested.connect(self._openSearchWidget)
+         self.setCentralWidget(self._manifest_widget)
+@@ -94,7 +94,9 @@ class ManifestDialog(QMainWindow):
+ 
+ class ManifestWidget(QWidget):
+     """Display file tree and contents at the specified revision"""
+-    revchanged = pyqtSignal(object)  # emit when curret revision changed
++
++    revChanged = pyqtSignal(object)
++    """Emitted (rev) when the current revision changed"""
+ 
+     revisionHint = pyqtSignal(unicode)
+     """Emitted when to show revision summary as a hint"""
+@@ -198,7 +200,7 @@ class ManifestWidget(QWidget):
+         return self._rev
+ 
+     @pyqtSlot(object)
+-    def setrev(self, rev):
++    def setRev(self, rev):
+         """Change revision to show"""
+         self.setSource(self.path, rev)
+ 
+@@ -210,10 +212,10 @@ class ManifestWidget(QWidget):
+         if revchanged:
+             self._rev = rev
+             self._setupmodel()
+-        self.setpath(path)
++        self.setPath(path)
+         self._fileview.setSource(path, rev, line)
+         if revchanged:
+-            self.revchanged.emit(rev)
++            self.revChanged.emit(rev)
+ 
+     @property
+     def path(self):
+@@ -221,7 +223,7 @@ class ManifestWidget(QWidget):
+         return self._treemodel.filePath(self._treeview.currentIndex())
+ 
+     @pyqtSlot(unicode)
+-    def setpath(self, path):
++    def setPath(self, path):
+         """Change path to show"""
+         self._treeview.setCurrentIndex(self._treemodel.indexFromPath(path))
+ 
+diff --git a/tortoisehg/hgqt/repowidget.py b/tortoisehg/hgqt/repowidget.py
+--- a/tortoisehg/hgqt/repowidget.py
++++ b/tortoisehg/hgqt/repowidget.py
+@@ -184,8 +184,8 @@ class RepoWidget(QWidget):
+         w = ManifestTaskWidget(self.repo.ui, self.repo, rev=filterrev(self.rev),
+                                parent=self)
+         self.repoview.revisionClicked.connect(lambda rev:
+-                           w.setrev(filterrev(rev)))
+-        w.revchanged.connect(self.repoview.goto)
++                           w.setRev(filterrev(rev)))
++        w.revChanged.connect(self.repoview.goto)
+         w.revisionHint.connect(self.showMessage)
+         w.grepRequested.connect(self.grep)
+         return w
+@@ -439,7 +439,7 @@ class RepoWidget(QWidget):
+             # patch ctx
+         else:
+             self.revDetailsWidget.revision_selected(rev)
+-            self.manifestDemand.forward('setrev', rev)
++            self.manifestDemand.forward('setRev', rev)
+             self.grepDemand.forward('setRevision', rev)
+ 
+     def goto(self, rev):
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287245165 -32400
+# Node ID 96a615786d99d44024e2ab6f253cdf9dbfb384f9
+# Parent  df23f6546036b75c0908e52bb1dcc4d215c53f74
+manifestdialog: accept initial path, line and pattern from command-line
+
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -80,6 +80,13 @@ class ManifestDialog(QMainWindow):
+         s.setValue('manifest/splitter',
+                    self._manifest_widget._splitter.saveState())
+ 
++    def setSource(self, path, rev, line=None):
++        self._manifest_widget.setSource(path, rev, line)
++
++    def setSearchPattern(self, text):
++        """Set search pattern [unicode]"""
++        self._searchbar.setPattern(text)
++
+     @pyqtSlot(unicode, dict)
+     def _openSearchWidget(self, pattern, opts):
+         opts = dict((str(k), str(v)) for k, v in opts.iteritems())
+@@ -328,4 +335,17 @@ def _openineditor(repo, path, rev, line=
+ 
+ def run(ui, *pats, **opts):
+     repo = opts.get('repo') or thgrepo.repository(ui, paths.find_root())
+-    return ManifestDialog(ui, repo, opts.get('rev'))
++    dlg = ManifestDialog(ui, repo, opts.get('rev'))
++
++    # set initial state after dialog visible
++    def init():
++        try:
++            path = hglib.canonpaths(pats)[0]
++            line = opts.get('line') and int(opts['line']) or None
++            dlg.setSource(path, opts.get('rev'), line)
++        except IndexError:
++            pass
++        dlg.setSearchPattern(hglib.tounicode(opts.get('pattern')))
++    QTimer.singleShot(0, init)
++
++    return dlg
+diff --git a/tortoisehg/hgqt/run.py b/tortoisehg/hgqt/run.py
+--- a/tortoisehg/hgqt/run.py
++++ b/tortoisehg/hgqt/run.py
+@@ -897,8 +897,10 @@ table = {
+          _('thg log [OPTIONS] [FILE]')),
+     "manifest":
+         (manifest,
+-         [('r', 'rev', '', _('revision to display'))],
+-         _('thg manifest [-r REV]')),
++         [('r', 'rev', '', _('revision to display')),
++          ('n', 'line', '', _('open to line')),
++          ('p', 'pattern', '', _('initial search pattern'))],
++         _('thg manifest [-r REV] [FILE]')),
+     "^merge":
+         (merge,
+          [('r', 'rev', '', _('revision to merge'))],
+# HG changeset patch
+# User Yuya Nishihara <yuya@tcha.org>
+# Date 1287253837 -32400
+# Node ID a95b6423bfbf8609d078144405e2e7133b832a52
+# Parent  96a615786d99d44024e2ab6f253cdf9dbfb384f9
+manifestdialog: update copyright line
+
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -1,22 +1,13 @@
+-# -*- coding: utf-8 -*-
+-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
+-# http://www.logilab.fr/ -- mailto:contact@logilab.fr
++# manifestdialog.py - Dialog and widget for TortoiseHg manifest view
++#
++# Copyright (C) 2003-2010 LOGILAB S.A. <http://www.logilab.fr/>
++# Copyright (C) 2010 Yuya Nishihara <yuya@tcha.org>
+ #
+ # This program is free software; you can redistribute it and/or modify it under
+ # the terms of the GNU General Public License as published by the Free Software
+ # Foundation; either version 2 of the License, or (at your option) any later
+ # version.
+-#
+-# This program is distributed in the hope that it will be useful, but WITHOUT
+-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+-#
+-# You should have received a copy of the GNU General Public License along with
+-# this program; if not, write to the Free Software Foundation, Inc.,
+-# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+-"""
+-Qt4 dialogs to display hg revisions of a file
+-"""
++
+ import os
+ 
+ from mercurial import util
+# HG changeset patch
+# Parent 6f6ddfa89dfba68f5ceeabd79595184e40008efe
+run: update thg help to follow the change of ui.wrap()
+
+The 'width' parameter of ui.wrap() becomes requisite since 7aef77e74cf3.
+
+Despite it's safe to specify 'width' parameter explicitly, this patch
+breaks compatibility with stable 1.6-series, due to ui.termwidth().
+ui.termwidth() is recently moved from util, as c52c629ce19e.
+
+
+diff --git a/tortoisehg/hgqt/run.py b/tortoisehg/hgqt/run.py
+--- a/tortoisehg/hgqt/run.py
++++ b/tortoisehg/hgqt/run.py
+@@ -627,6 +627,7 @@ def help_(ui, name=None, with_version=Fa
+     Given an extension name, print help for that extension, and the
+     commands it provides."""
+     option_lists = []
++    textwidth = ui.termwidth() - 2
+ 
+     def addglobalopts(aliases):
+         if ui.verbose:
+@@ -715,7 +716,7 @@ def help_(ui, name=None, with_version=Fa
+                 commands = cmds[f].replace("|",", ")
+                 ui.write(" %s:\n      %s\n"%(commands, h[f]))
+             else:
+-                ui.write('%s\n' % (util.wrap(h[f],
++                ui.write('%s\n' % (util.wrap(h[f], textwidth,
+                                              initindent=' %-*s   ' % (m, f),
+                                              hangindent=' ' * (m + 4))))
+ 
+@@ -786,7 +787,7 @@ def help_(ui, name=None, with_version=Fa
+             if second:
+                 initindent = ' %-*s  ' % (opts_len, first)
+                 hangindent = ' ' * (opts_len + 3)
+-                ui.write('%s\n' % (util.wrap(second,
++                ui.write('%s\n' % (util.wrap(second, textwidth,
+                                              initindent=initindent,
+                                              hangindent=hangindent)))
+             else:

File mani-filetree-visibility.diff

+# HG changeset patch
+# Parent d7a5f889e2055167d0aa48d112a12162c70477a7
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -45,6 +45,8 @@ class ManifestDialog(QMainWindow):
+         self._manifest_widget.editSelected.connect(self._openInEditor)
+         self._manifest_widget.grepRequested.connect(self._openSearchWidget)
+         self.setCentralWidget(self._manifest_widget)
++
++        self._initvisibilitybar()
+         self.addToolBar(self._manifest_widget.toolbar)
+ 
+         self._searchbar = qscilib.SearchToolBar()
+@@ -57,6 +59,17 @@ class ManifestDialog(QMainWindow):
+         self._readsettings()
+         self._updatewindowtitle()
+ 
++    def _initvisibilitybar(self):
++        self._visibilitybar = QToolBar('Visibility', self,
++                                       objectName='visibility')
++        self.addToolBar(self._visibilitybar)
++        self._action_showtree = QAction(qtlib.geticon('repobrowse'),
++                                        _('Show File Tree'), self,
++                                        checkable=True,
++                                        checked=self._manifest_widget.isFileTreeVisible())
++        self._action_showtree.toggled.connect(self._manifest_widget.setFileTreeVisible)
++        self._visibilitybar.addAction(self._action_showtree)
++
+     @pyqtSlot()
+     def _updatewindowtitle(self):
+         self.setWindowTitle(_('Hg manifest viewer - %s:%s') % (
+@@ -193,6 +206,14 @@ class ManifestWidget(QWidget):
+         if 'C' not in self._statusfilter.text:
+             self._treeview.expandAll()
+ 
++    @pyqtSlot(bool)
++    def setFileTreeVisible(self, visible):
++        self._treeview.setVisible(visible)
++        self._toolbar.setDisabled(not visible)
++
++    def isFileTreeVisible(self):
++        return self._treeview.isVisible()
++
+     def reload(self):
+         # TODO
+         pass

File mani-pathchanged.diff

+# HG changeset patch
+# Parent c0a898f3de7bf736503409e4c7198622f37a60fa
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -96,6 +96,9 @@ class ManifestWidget(QWidget):
+     """Display file tree and contents at the specified revision"""
+     revchanged = pyqtSignal(object)  # emit when curret revision changed
+ 
++    pathChanged = pyqtSignal(unicode)
++    """Emitted (path) when the current file path changed"""
++
+     revisionHint = pyqtSignal(unicode)
+     """Emitted when to show revision summary as a hint"""
+ 
+@@ -174,7 +177,11 @@ class ManifestWidget(QWidget):
+                                         statusfilter=self._statusfilter.text,
+                                         parent=self)
+         self._treeview.setModel(self._treemodel)
+-        self._treeview.selectionModel().currentChanged.connect(self._updatecontent)
++
++        selmodel = self._treeview.selectionModel()
++        selmodel.currentChanged.connect(self._updatecontent)
++        selmodel.currentChanged.connect(self._emitPathChanged)
++
+         self._statusfilter.textChanged.connect(self._treemodel.setStatusFilter)
+         self._statusfilter.textChanged.connect(self._autoexpandtree)
+         self._autoexpandtree()
+@@ -227,6 +234,11 @@ class ManifestWidget(QWidget):
+         self._contentview.setCurrentWidget(self._fileview)
+         self._contentview.currentWidget().setSource(self.path, self._rev)
+ 
++    @pyqtSlot(QModelIndex, QModelIndex)
++    def _emitPathChanged(self, current, previous):
++        if self.path != self._treemodel.filePath(previous):
++            self.pathChanged.emit(self.path)
++
+ # TODO: share this menu with status widget?
+ class _StatusFilterButton(QToolButton):
+     """Button with drop-down menu for status filter"""

File qsci-im.diff

-# HG changeset patch
-# Parent 64b20144863111f2544b1247f67e5a852a1ce79b
-qscilib, commit: implement better input method support on QsciScintilla
-
-QsciScintilla has very limited implementation for input method. It cannot
-display pre-edit text (characters under typing or conversion), and shows
-the candicates window at wrong place.
-Thus, it's pretty difficult to input Japanese or Chinese characters.
-
-This patch includes the following changes, so that pre-edit Kanji and Kana
-should be visible for users:
-
- - provide cursor position to input method via inputMethodQuery()
- - handle pre-edit text by inputMethodEvent()
-
-diff --git a/tortoisehg/hgqt/commit.py b/tortoisehg/hgqt/commit.py
---- a/tortoisehg/hgqt/commit.py
-+++ b/tortoisehg/hgqt/commit.py
-@@ -18,6 +18,7 @@ from tortoisehg.hgqt.i18n import _
- from tortoisehg.util import hglib, shlib, paths, wconfig
- 
- from tortoisehg.hgqt import qtlib, status, cmdui, branchop, revpanel, lexers
-+from tortoisehg.hgqt import qscilib
- from tortoisehg.hgqt.sync import loadIniFile
- 
- # Technical Debt for CommitWidget
-@@ -26,7 +27,7 @@ from tortoisehg.hgqt.sync import loadIni
- #  spell check / tab completion
- #  in-memory patching / committing chunk selected files
- 
--class MessageEntry(QsciScintilla):
-+class MessageEntry(qscilib.Scintilla):
-     escapePressed = pyqtSignal()
-     refreshPressed = pyqtSignal()
-     reflowPressed = pyqtSignal()
-@@ -37,7 +38,6 @@ class MessageEntry(QsciScintilla):
-         self.setEdgeMode(QsciScintilla.EdgeLine)
-         self.setWrapMode(QsciScintilla.WrapNone)
-         self.setReadOnly(False)
--        self.setUtf8(True)
-         self.setMarginWidth(1, 0)
-         self.setFont(qtlib.getfont('fontcomment').font())
-         self.setCaretWidth(10)
-diff --git a/tortoisehg/hgqt/qscilib.py b/tortoisehg/hgqt/qscilib.py
-new file mode 100644
---- /dev/null
-+++ b/tortoisehg/hgqt/qscilib.py
-@@ -0,0 +1,153 @@
-+# qscilib.py - Utility codes for QsciScintilla
-+#
-+# Copyright 2010 Yuya Nishihara <yuya@tcha.org>
-+#
-+# This software may be used and distributed according to the terms of the
-+# GNU General Public License version 2 or any later version.
-+
-+from mercurial import util
-+
-+from PyQt4.QtCore import *
-+from PyQt4.QtGui import *
-+from PyQt4.Qsci import QsciScintilla
-+
-+class _SciImSupport(object):
-+    """Patch for QsciScintilla to implement improved input method support
-+
-+    See http://doc.trolltech.com/4.7/qinputmethodevent.html
-+    """
-+
-+    PREEDIT_INDIC_ID = QsciScintilla.INDIC_MAX
-+    """indicator for highlighting preedit text"""
-+
-+    def __init__(self, sci):
-+        self._sci = sci
-+        self._preeditpos = (0, 0)  # (line, index) where preedit text starts
-+        self._preeditlen = 0
-+        self._preeditcursorpos = 0  # relative pos where preedit cursor exists
-+        self._undoactionbegun = False
-+        self._setuppreeditindic()
-+
-+    def removepreedit(self):
-+        """Remove the previous preedit text
-+
-+        original pos: preedit cursor
-+        final pos: target cursor
-+        """
-+        l, i = self._sci.getCursorPosition()
-+        i -= self._preeditcursorpos
-+        self._preeditcursorpos = 0
-+        try:
-+            self._sci.setSelection(
-+                self._preeditpos[0], self._preeditpos[1],
-+                self._preeditpos[0], self._preeditpos[1] + self._preeditlen)
-+            self._sci.removeSelectedText()
-+        finally:
-+            self._sci.setCursorPosition(l, i)
-+
-+    def commitstr(self, start, repllen, commitstr):
-+        """Remove the repl string followed by insertion of the commit string
-+
-+        original pos: target cursor
-+        final pos: end of committed text (= start of preedit text)
-+        """
-+        l, i = self._sci.getCursorPosition()
-+        i += start
-+        self._sci.setSelection(l, i, l, i + repllen)
-+        self._sci.removeSelectedText()
-+        self._sci.insert(commitstr)
-+        self._sci.setCursorPosition(l, i + len(commitstr))
-+        if commitstr:
-+            self.endundo()
-+
-+    def insertpreedit(self, text):
-+        """Insert preedit text
-+
-+        original pos: start of preedit text
-+        final pos: start of preedit text (unchanged)
-+        """
-+        if text and not self._preeditlen:
-+            self.beginundo()
-+        l, i = self._sci.getCursorPosition()
-+        self._sci.insert(text)
-+        self._updatepreeditpos(l, i, len(text))
-+        if not self._preeditlen:
-+            self.endundo()
-+
-+    def movepreeditcursor(self, pos):
-+        """Move the cursor to the relative pos inside preedit text"""
-+        self._preeditcursorpos = pos
-+        l, i = self._preeditpos
-+        self._sci.setCursorPosition(l, i + self._preeditcursorpos)
-+
-+    def beginundo(self):
-+        if self._undoactionbegun:
-+            return
-+        self._sci.beginUndoAction()
-+        self._undoactionbegun = True
-+
-+    def endundo(self):
-+        if not self._undoactionbegun:
-+            return
-+        self._sci.endUndoAction()
-+        self._undoactionbegun = False
-+
-+    def _updatepreeditpos(self, l, i, len):
-+        """Update the indicator and internal state for preedit text"""
-+        self._sci.SendScintilla(QsciScintilla.SCI_SETINDICATORCURRENT,
-+                                self.PREEDIT_INDIC_ID)
-+        self._preeditpos = (l, i)
-+        self._preeditlen = len
-+        if len <= 0:  # have problem on sci
-+            return
-+        p = self._sci.positionFromLineIndex(*self._preeditpos)
-+        q = self._sci.positionFromLineIndex(self._preeditpos[0],
-+                                            self._preeditpos[1] + len)
-+        self._sci.SendScintilla(QsciScintilla.SCI_INDICATORFILLRANGE,
-+                                p, q - p)  # q - p != len
-+
-+    def _setuppreeditindic(self):
-+        """Configure the style of preedit text indicator"""
-+        self._sci.SendScintilla(QsciScintilla.SCI_INDICSETSTYLE,
-+                                self.PREEDIT_INDIC_ID,
-+                                QsciScintilla.INDIC_PLAIN)
-+
-+class Scintilla(QsciScintilla):
-+    def __init__(self, parent=None):
-+        super(Scintilla, self).__init__(parent)
-+        self.setUtf8(True)
-+
-+    def inputMethodQuery(self, query):
-+        if query == Qt.ImMicroFocus:
-+            return self.cursorRect()
-+        return super(Scintilla, self).inputMethodQuery(query)
-+
-+    def inputMethodEvent(self, event):
-+        if self.isReadOnly():
-+            return
-+
-+        self.removeSelectedText()
-+        self._imsupport.removepreedit()
-+        self._imsupport.commitstr(event.replacementStart(),
-+                                  event.replacementLength(),
-+                                  event.commitString())
-+        self._imsupport.insertpreedit(event.preeditString())
-+        for a in event.attributes():
-+            if a.type == QInputMethodEvent.Cursor:
-+                self._imsupport.movepreeditcursor(a.start)
-+            # TODO TextFormat
-+
-+        event.accept()
-+
-+    @util.propertycache
-+    def _imsupport(self):
-+        return _SciImSupport(self)
-+
-+    def cursorRect(self):
-+        """Return a rectangle (in viewport coords) including the cursor"""
-+        l, i = self.getCursorPosition()
-+        p = self.positionFromLineIndex(l, i)
-+        x = self.SendScintilla(QsciScintilla.SCI_POINTXFROMPOSITION, 0, p)
-+        y = self.SendScintilla(QsciScintilla.SCI_POINTYFROMPOSITION, 0, p)
-+        w = self.SendScintilla(QsciScintilla.SCI_GETCARETWIDTH)
-+        return QRect(x, y, w, self.textHeight(l))

File revdetails-no-cursor-key.diff

-# HG changeset patch
-# Parent 0455b9bc8c72267dff2f9c33f3de0c43452c3d66
-revdetails: remove left/right shortcuts eating input for revset and output log
-
-Because QAction affects the whole active window, the left/right shortcuts
-aren't match for workbench.
-
-diff --git a/tortoisehg/hgqt/revdetails.py b/tortoisehg/hgqt/revdetails.py
---- a/tortoisehg/hgqt/revdetails.py
-+++ b/tortoisehg/hgqt/revdetails.py
-@@ -199,16 +199,6 @@ class RevDetailsWidget(QWidget):
-         self.actionPrevDiff.triggered.connect(self.prevDiff)
-         self.actionDiffMode.setChecked(True)
- 
--        # Next/Prev file
--        self.actionNextFile = QAction('Next file', self)
--        self.actionNextFile.setShortcut('Right')
--        self.actionNextFile.triggered.connect(self.filelist.nextFile)
--        self.actionPrevFile = QAction('Prev file', self)
--        self.actionPrevFile.setShortcut('Left')
--        self.actionPrevFile.triggered.connect(self.filelist.prevFile)
--        self.addAction(self.actionNextFile)
--        self.addAction(self.actionPrevFile)
--
-         # navigate in file viewer
-         self.actionNextLine = QAction('Next line', self)
-         self.actionNextLine.setShortcut(Qt.SHIFT + Qt.Key_Down)
-qsci-im.diff
-revdetails-no-cursor-key.diff
+9030.diff
+9031.diff
+9032.diff
+9033.diff
+9034.diff
+9035.diff
+9036.diff
+9037.diff
+9038.diff
+9039.diff
+9040.diff
+9041.diff
+9042.diff
+9043.diff
+9044.diff
+9045.diff
+9046.diff
+9047.diff
+help.diff
+treeview.diff
+mani-filetree-visibility.diff
+mani-pathchanged.diff
 lookuperror.diff
 error-mutiple.diff
 revdesc-template.diff

File treeview.diff

+# HG changeset patch
+# Parent d7a5f889e2055167d0aa48d112a12162c70477a7
+
+diff --git a/tortoisehg/hgqt/manifestdialog.py b/tortoisehg/hgqt/manifestdialog.py
+--- a/tortoisehg/hgqt/manifestdialog.py
++++ b/tortoisehg/hgqt/manifestdialog.py
+@@ -121,8 +121,6 @@ class ManifestWidget(QWidget):
+ 
+         self._initwidget()
+         self._initactions()
+-        self._setupmodel()
+-        self._treeview.setCurrentIndex(self._treemodel.index(0, 0))
+ 
+     def _initwidget(self):
+         self.setLayout(QVBoxLayout())
+@@ -134,7 +132,12 @@ class ManifestWidget(QWidget):
+         navlayout.setContentsMargins(0, 0, 0, 0)
+         self._toolbar = QToolBar()
+         self._toolbar.setIconSize(QSize(16,16))
+-        self._treeview = QTreeView(self, headerHidden=True, dragEnabled=True)
++        self._statusfilter = _StatusFilterButton(text='MAC')
++        self._toolbar.addWidget(self._statusfilter)
++        self._treeview = _FileTreeView(self._repo, self._rev,
++                                       self._statusfilter.text, self)
++        self._treeview.pathChanged.connect(self.setPath)
++        self._statusfilter.textChanged.connect(self._treeview.setStatusFilter)
+         navlayout.addWidget(self._toolbar)
+         navlayout.addWidget(self._treeview)
+         navlayoutw = QWidget()
+@@ -156,9 +159,6 @@ class ManifestWidget(QWidget):
+             getattr(self._fileview, name).connect(getattr(self, name))
+ 
+     def _initactions(self):
+-        self._statusfilter = _StatusFilterButton(text='MAC')
+-        self._toolbar.addWidget(self._statusfilter)
+-
+         self._action_annotate_mode = QAction(_('Annotate'), self, checkable=True)
+         self._action_annotate_mode.toggled.connect(
+             self._fileview.setAnnotationEnabled)
+@@ -177,22 +177,6 @@ class ManifestWidget(QWidget):
+     def highlightText(self, pattern, icase=False):
+         self._fileview.highlightText(pattern, icase)
+ 
+-    def _setupmodel(self):
+-        self._treemodel = ManifestModel(self._repo, self._rev,
+-                                        statusfilter=self._statusfilter.text,
+-                                        parent=self)
+-        self._treeview.setModel(self._treemodel)
+-        self._treeview.selectionModel().currentChanged.connect(self._updatecontent)
+-        self._statusfilter.textChanged.connect(self._treemodel.setStatusFilter)
+-        self._statusfilter.textChanged.connect(self._autoexpandtree)
+-        self._autoexpandtree()
+-
+-    @pyqtSlot()
+-    def _autoexpandtree(self):
+-        """expand file tree if the number of the items isn't large"""
+-        if 'C' not in self._statusfilter.text:
+-            self._treeview.expandAll()
+-
+     def reload(self):
+         # TODO
+         pass
+@@ -214,21 +198,20 @@ class ManifestWidget(QWidget):
+         revchanged = self._rev != rev
+         if revchanged:
+             self._rev = rev
+-            self._setupmodel()
+-        self.setPath(path)
++        self._treeview.setSource(path, rev)
+         self._fileview.setSource(path, rev, line)
+         if revchanged:
+             self.revChanged.emit(rev)
++        self._updatecontent()
+ 
+     @property
+     def path(self):
+-        """Return currently selected path"""
+-        return self._treemodel.filePath(self._treeview.currentIndex())
++        return self._treeview.path
+ 
+     @pyqtSlot(unicode)
+     def setPath(self, path):
+         """Change path to show"""
+-        self._treeview.setCurrentIndex(self._treemodel.indexFromPath(path))
++        self.setSource(path, self.rev)
+ 
+     @pyqtSlot()
+     def _updatecontent(self):
+@@ -239,6 +222,54 @@ class ManifestWidget(QWidget):
+         self._contentview.setCurrentWidget(self._fileview)
+         self._fileview.setSource(self.path, self._rev)
+ 
++class _FileTreeView(QTreeView):
++    """Widget to show repository file tree"""
++    pathChanged = pyqtSignal(unicode)
++
++    def __init__(self, repo, rev=None, statusfilter='MAC', parent=None):
++        super(_FileTreeView, self).__init__(parent, headerHidden=True,
++                                            dragEnabled=True)
++        self._repo = repo
++        self._rev = rev
++        self._setupmodel(statusfilter)
++        self.setCurrentIndex(self._treemodel.index(0, 0))
++
++    def _setupmodel(self, statusfilter):
++        self._treemodel = ManifestModel(self._repo, self._rev,
++                                        statusfilter=statusfilter,
++                                        parent=self)
++        self.setModel(self._treemodel)
++        self.selectionModel().currentChanged.connect(self._emitPathChanged)
++        self._autoexpandtree()
++
++    @pyqtSlot(unicode)
++    def setStatusFilter(self, status):
++        self._treemodel.setStatusFilter(status)
++        self._autoexpandtree()
++
++    @pyqtSlot()
++    def _autoexpandtree(self):
++        """expand file tree if the number of the items isn't large"""
++        if 'C' not in self._treemodel.statusFilter:
++            self.expandAll()
++
++    @property
++    def path(self):
++        """Return currently selected path"""
++        return self._treemodel.filePath(self.currentIndex())
++
++    @pyqtSlot(unicode, object)
++    def setSource(self, path, rev):
++        """Change path and revision to show"""
++        if self._rev != rev:
++            self._rev = rev
++            self._setupmodel(self._treemodel.statusFilter)
++        self.setCurrentIndex(self._treemodel.indexFromPath(path))
++
++    @pyqtSlot()
++    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"""