Commits

Yuya Nishihara committed e6ddcba

trying theme icons

Comments (0)

Files changed (15)

+# HG changeset patch
+# Parent f9ff9db3ef5970ba3d5878fab5ddbafd52d7ab4f
+annotate: reset known annfile by clear() so that setSource() takes effect
+
+Without this, the following code path doesn't work:
+
+ 1. annview.setSource('foo', 1)
+ 2. annview.clear()
+ 3. annview.setSource('foo', 1)  # no change
+
+It happens when user clicks "annotate originating revision" 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
+@@ -206,6 +206,7 @@ class AnnotateView(qscilib.Scintilla):
+         super(AnnotateView, self).clear()
+         self.clearMarginText()
+         self.markerDeleteAll()
++        self.annfile = None
+ 
+     @pyqtSlot(bool)
+     def setAnnotationEnabled(self, enabled):
+# HG changeset patch
+# Parent ade32e4dd5310154326481dfb66bb5c225148601
+diff --git a/tortoisehg/hgqt/annotate.py b/tortoisehg/hgqt/annotate.py
+--- a/tortoisehg/hgqt/annotate.py
++++ b/tortoisehg/hgqt/annotate.py
+@@ -224,8 +224,6 @@ class AnnotateView(qscilib.Scintilla):
+ 
+     def isAnnotationEnabled(self):
+         """True if annotation enabled and available"""
+-        if self.rev is None:
+-            return False  # annotate working copy is not supported
+         return self._annotation_enabled
+ 
+     def _updatelexer(self, fctx):
+@@ -356,7 +354,7 @@ class AnnotateDialog(QMainWindow):
+             lambda: self._searchbar.setFocus(Qt.OtherFocusReason))
+ 
+         self.av.sourceChanged.connect(
+-            lambda *args: self.setWindowTitle(_('Annotate %s@%d') % args))
++            lambda *args: self.setWindowTitle(_('Annotate %s@%s') % args))
+ 
+         self.searchwidget = opts.get('searchwidget')
+ 
+@@ -371,7 +369,7 @@ class AnnotateDialog(QMainWindow):
+ 
+         # run heavy operation after the dialog visible
+         path = hglib.tounicode(pats[0])
+-        rev = opts.get('rev') or '.'
++        rev = opts.get('rev') or None
+         QTimer.singleShot(0, lambda: av.setSource(path, rev, line))
+ 
+     def closeEvent(self, event):
+# HG changeset patch
+# Parent dbc2859697d3a6adc8ea6d44b9005d259c72d63c
+setup: generate workbench.qrc automatically
+
+TODO: Could not add child element to parent element because the types are incorrect.
+
+diff --git a/.hgignore b/.hgignore
+--- a/.hgignore
++++ b/.hgignore
+@@ -39,4 +39,5 @@ win32/*.reg
+ tortoisehg/util/__version__.py
+ *.wixobj
+ tortoisehg/hgqt/workbench_rc.py
++tortoisehg/hgqt/workbench.qrc
+ tortoisehg/hgqt/*_ui.py
+diff --git a/setup.py b/setup.py
+--- a/setup.py
++++ b/setup.py
+@@ -156,9 +156,35 @@ class build_qt(Command):
+         else:
+             log.info('compiled %s into %s' % (qrc_file, py_file))
+ 
++    def build_qrc(self, qrc_file, basedir, prefix='/'):
++        """Generate .qrc which lists any files under basedir"""
++        # For details, see http://doc.qt.nokia.com/resources.html
++        from cgi import escape
++        qrc_file = os.path.realpath(qrc_file)
++        qrc_dir = os.path.dirname(qrc_file)
++        basedir = os.path.realpath(basedir)
++        assert basedir.startswith(qrc_dir + os.path.sep)
++        # TODO: overwrite only if the content has been changed
++        f = open(qrc_file, 'w')
++        try:
++            f.write('<!DOCTYPE RCC><RCC version="1.0">\n')
++            f.write('  <qresource prefix="%s">\n' % escape(prefix))
++            for root, _dirs, files in os.walk(basedir):
++                for e in files:
++                    path = os.path.join(root[len(qrc_dir) + 1:], e)
++                    f.write('    <file>%s</file>\n'
++                            % escape(path.replace(os.path.sep, '/')))
++            f.write('  </qresource>\n')
++            f.write('</RCC>\n')
++            log.info('generated %s' % qrc_file)
++        finally:
++            f.close()
++
+     def run(self):
+         self._wrapuic()
+         basepath = join(os.path.dirname(__file__), 'tortoisehg', 'hgqt')
++        self.build_qrc(os.path.join(basepath, 'workbench.qrc'),
++                       os.path.join(basepath, 'icons'))
+         for dirpath, _, filenames in os.walk(basepath):
+             for filename in filenames:
+                 if filename.endswith('.ui'):
+diff --git a/tortoisehg/hgqt/workbench.qrc b/tortoisehg/hgqt/workbench.qrc
+deleted file mode 100644
+--- a/tortoisehg/hgqt/workbench.qrc
++++ /dev/null
+@@ -1,81 +0,0 @@
+-<RCC>
+-  <qresource prefix="/" >
+-    <file>icons/annotate.svg</file>
+-    <file>icons/log.svg</file>
+-    <file>icons/quit.svg</file>
+-    <file>icons/reload.svg</file>
+-    <file>icons/back.svg</file>
+-    <file>icons/forward.svg</file>
+-    <file>icons/left.svg</file>
+-    <file>icons/right.svg</file>
+-    <file>icons/up.svg</file>
+-    <file>icons/down.svg</file>
+-    <file>icons/leftright.svg</file>
+-    <file>icons/close.png</file>
+-    <file>icons/help.svg</file>
+-    <file>icons/find.svg</file>
+-    <file>icons/goto.svg</file>
+-    <file>icons/modified.svg</file>
+-    <file>icons/clean.svg</file>
+-    <file>icons/mqdiff.svg</file>
+-    <file>icons/mqpatch.svg</file>
+-    <file>icons/plus.png</file>
+-    <file>icons/minus.png</file>
+-    <file>icons/hg.ico</file>
+-    <file>icons/fileadd.ico</file>
+-    <file>icons/filedelete.ico</file>
+-    <file>icons/fallback.svg</file>
+-    <file>icons/success.svg</file>
+-    <file>icons/error.svg</file>
+-    <file>icons/warning.svg</file>
+-    <file>icons/tag.svg</file>
+-    <file>icons/archive.svg</file>
+-    <file>icons/clone.svg</file>
+-    <file>icons/update.svg</file>
+-    <file>icons/sync.svg</file>
+-    <file>icons/merge.svg</file>
+-    <file>icons/rename.svg</file>
+-    <file>icons/commit.svg</file>
+-    <file>icons/fonts.svg</file>
+-    <file>icons/reloadtt.svg</file>
+-    <file>icons/repotree.svg</file>
+-    <file>icons/repobrowse.svg</file>
+-    <file>icons/loadall.png</file>
+-    <file>icons/showlog.png</file>
+-    <file>icons/status.svg</file>
+-    <file>icons/incoming.svg</file>
+-    <file>icons/outgoing.svg</file>
+-    <file>icons/pull.svg</file>
+-    <file>icons/push.svg</file>
+-    <file>icons/incoming-24x24.png</file>
+-    <file>icons/outgoing-24x24.png</file>
+-    <file>icons/pull-24x24.png</file>
+-    <file>icons/push-24x24.png</file>
+-    <file>icons/qpush.svg</file>
+-    <file>icons/qpop.svg</file>
+-    <file>icons/qpopall.svg</file>
+-    <file>icons/qpushall.svg</file>
+-    <file>icons/qreorder.svg</file>
+-    <file>icons/mq.svg</file>
+-    <file>icons/qguards.svg</file>
+-    <file>icons/reviewboard.png</file>
+-    <file>icons/bookmark.png</file>
+-    <file>icons/extensions.svg</file>
+-    <file>icons/mail-forward.svg</file>
+-    <file>icons/process-stop.svg</file>
+-    <file>icons/edit-find.svg</file>
+-    <file>icons/go-jump.svg</file>
+-    <file>icons/help-browser.svg</file>
+-    <file>icons/Checkmark.svg</file>
+-    <file>icons/all2left.svg</file>
+-    <file>icons/all2right.svg</file>
+-    <file>icons/edit-cut.svg</file>
+-    <file>icons/document-new.svg</file>
+-    <file>icons/file2left.svg</file>
+-    <file>icons/file2right.svg</file>
+-    <file>icons/chunk2left.svg</file>
+-    <file>icons/chunk2right.svg</file>
+-    <file>icons/delfilesleft.svg</file>
+-    <file>icons/delfilesright.svg</file>
+-  </qresource>
+-</RCC>
+# HG changeset patch
+# Parent ea8b6409d709a683d4648780d0dee09861338e53
+diff --git a/tortoisehg/hgqt/hgemail.py b/tortoisehg/hgqt/hgemail.py
+--- a/tortoisehg/hgqt/hgemail.py
++++ b/tortoisehg/hgqt/hgemail.py
+@@ -19,7 +19,7 @@ from tortoisehg.hgqt.hgemail_ui import U
+ 
+ class EmailDialog(QDialog):
+     """Dialog for sending patches via email"""
+-    def __init__(self, repo, revs, parent=None):
++    def __init__(self, repo, revs, base=None, parent=None):
+         super(EmailDialog, self).__init__(parent)
+         self._repo = repo
+ 
+@@ -27,7 +27,7 @@ class EmailDialog(QDialog):
+         self._qui.setupUi(self)
+         self._qui.bundle_radio.setEnabled(False)  # TODO: bundle support
+ 
+-        self._initchangesets(revs)
++        self._initchangesets(revs, base)
+         self._initpreviewtab()
+         self._initenvelopebox()
+         self._initintrobox()
+@@ -82,10 +82,18 @@ class EmailDialog(QDialog):
+             w = getattr(self._qui, '%s_edit' % k)
+             s.setValue('email/%s_history' % k, list(itercombo(w))[:10])
+ 
+-    def _initchangesets(self, revs):
+-        def purerevs(revs):
++    def _initchangesets(self, revs, base):
++        def revrange(revs):
+             return cmdutil.revrange(self._repo,
+                                     iter(str(e) for e in revs))
++        def purerevs(revs):
++            if not base:
++                return revrange(revs)
++            revns = [self._repo.lookup(r) for r in revrange(revs)]
++            basens = [self._repo.lookup(r) for r in revrange(base)]
++            nodes = self._repo.changelog.nodesbetween(basens, revns)[0]
++            # XXX: make base exclusive
++            return map(self._repo.changelog.rev, nodes)
+ 
+         self._changesets = _ChangesetsModel(self._repo,
+                                             # TODO: [':'] is inefficient
+@@ -416,12 +424,13 @@ def run(ui, *revs, **opts):
+         if revs:
+             raise util.Abort(_('use only one form to specify the revision'))
+         revs = opts.get('rev')
++    base = opts.get('base')
+ 
+     # TODO: repo should be a required argument?
+     repo = opts.get('repo') or thgrepo.repository(ui, paths.find_root())
+ 
+     try:
+-        return EmailDialog(repo, revs)
++        return EmailDialog(repo, revs, base)
+     except error.RepoLookupError, e:
+         qtlib.ErrorMsgBox(_('Failed to open Email dialog'),
+                           hglib.tounicode(e.message))
+diff --git a/tortoisehg/hgqt/run.py b/tortoisehg/hgqt/run.py
+--- a/tortoisehg/hgqt/run.py
++++ b/tortoisehg/hgqt/run.py
+@@ -960,7 +960,9 @@ table = {
+     "^init": (init, [], _('thg init [DEST]')),
+     "^email":
+         (email,
+-         [('r', 'rev', [], _('a revision to send')),],
++         [('r', 'rev', [], _('a revision to send')),
++          ('', 'base', [],
++           _('a base changeset assumed to be available at the destination'))],
+          _('thg email [REVS]')),
+     "^log|history|explorer|workbench":
+         (log,
+# HG changeset patch
+# Parent 8e6596fa3c0dd9a7918cff0633d2f1d97a42b8f9
+diff --git a/tortoisehg/hgqt/hgemail.py b/tortoisehg/hgqt/hgemail.py
+--- a/tortoisehg/hgqt/hgemail.py
++++ b/tortoisehg/hgqt/hgemail.py
+@@ -25,11 +25,11 @@ class EmailDialog(QDialog):
+ 
+         self._qui = Ui_EmailDialog()
+         self._qui.setupUi(self)
+-        self._qui.bundle_radio.setEnabled(False)  # TODO: bundle support
+ 
+         self._initchangesets(revs)
+         self._initpreviewtab()
+         self._initenvelopebox()
++        self._qui.bundle_radio.toggled.connect(self._updateforms)
+         self._initintrobox()
+         self._readhistory()
+         self._filldefaults()
+@@ -175,6 +175,11 @@ class EmailDialog(QDialog):
+ 
+         opts.update(self.getextraopts())
+ 
++        if self._qui.bundle_radio.isChecked():
++            opts['bundle'] = True
++            opts['base'] = [str(self._revs[0])]  # XXX
++            opts['rev'] = [str(self._revs[-1])]  # XXX
++
+         def writetempfile(s):
+             fd, fname = tempfile.mkstemp(prefix='thg_emaildesc_')
+             try:
+@@ -261,7 +266,7 @@ class EmailDialog(QDialog):
+ 
+     def _introrequired(self):
+         """Is intro message required?"""
+-        return len(self._revs) > 1
++        return len(self._revs) > 1 or self._qui.bundle_radio.isChecked()
+ 
+     def _initpreviewtab(self):
+         def initqsci(w):

email-minimal-bundle.diff

+# HG changeset patch
+# Parent 0fc71b3f4982af777baa4dba2a0c4e083b8550dd
+hgemail: add minimal support for outgoing bundle
+
+Currently email bundle is enabled only by sync widget.
+
+Sync widget invokes email dialog with revs, outgoing and outgoingrevs.
+Here revs are list of changesets to be sent, and outgoingrevs are list of
+target outgoing revisions. outgoing flag and outgoingrevs are used only
+if bundle radio is checked.
+
+If bundle radio is checked, preview tab becomes disabled due to performance
+reason. It also makes changesets view readonly, because we cannot pick out
+changesets to be bundled.
+
+diff --git a/tortoisehg/hgqt/hgemail.py b/tortoisehg/hgqt/hgemail.py
+--- a/tortoisehg/hgqt/hgemail.py
++++ b/tortoisehg/hgqt/hgemail.py
+@@ -19,17 +19,28 @@ from tortoisehg.hgqt.hgemail_ui import U
+ 
+ class EmailDialog(QDialog):
+     """Dialog for sending patches via email"""
+-    def __init__(self, repo, revs, parent=None):
++    def __init__(self, repo, revs, parent=None, outgoing=False,
++                 outgoingrevs=None):
++        """Create EmailDialog for the given repo and revs
++
++        :revs: List of revisions to be sent.
++        :outgoing: Enable outgoing bundle support. You also need to set
++                   outgoing revisions to `revs`.
++        :outgoingrevs: Target revision of outgoing bundle.
++                       (Passed as `hg email --bundle --rev {rev}`)
++        """
+         super(EmailDialog, self).__init__(parent)
+         self._repo = repo
++        self._outgoing = outgoing
++        self._outgoingrevs = outgoingrevs or []
+ 
+         self._qui = Ui_EmailDialog()
+         self._qui.setupUi(self)
+-        self._qui.bundle_radio.setEnabled(False)  # TODO: bundle support
+ 
+         self._initchangesets(revs)
+         self._initpreviewtab()
+         self._initenvelopebox()
++        self._qui.bundle_radio.toggled.connect(self._updateforms)
+         self._initintrobox()
+         self._readhistory()
+         self._filldefaults()
+@@ -163,7 +174,13 @@ class EmailDialog(QDialog):
+         opts['from'] = headertext(self._qui.from_edit.currentText())
+         opts['in_reply_to'] = headertext(self._qui.inreplyto_edit.text())
+         opts['flag'] = [headertext(self._qui.flag_edit.currentText())]
+-        opts['rev'] = map(str, self._revs)
++
++        if self._qui.bundle_radio.isChecked():
++            assert self._outgoing  # only outgoing bundle is supported
++            opts['rev'] = map(str, self._outgoingrevs)
++            opts['bundle'] = True
++        else:
++            opts['rev'] = map(str, self._revs)
+ 
+         def diffformat():
+             n = self.getdiffformat()
+@@ -216,6 +233,15 @@ class EmailDialog(QDialog):
+         self._qui.send_button.setEnabled(valid)
+         self._qui.main_tabs.setTabEnabled(self._previewtabindex(), valid)
+         self._qui.writeintro_check.setEnabled(not self._introrequired())
++
++        self._qui.bundle_radio.setEnabled(
++            self._outgoing and self._changesets.isselectedall())
++        self._changesets.setReadOnly(self._qui.bundle_radio.isChecked())
++        if self._qui.bundle_radio.isChecked():
++            # workaround to disable preview for outgoing bundle because it
++            # may freeze main thread
++            self._qui.main_tabs.setTabEnabled(self._previewtabindex(), False)
++
+         if self._introrequired():
+             self._qui.writeintro_check.setChecked(True)
+ 
+@@ -261,7 +287,7 @@ class EmailDialog(QDialog):
+ 
+     def _introrequired(self):
+         """Is intro message required?"""
+-        return len(self._revs) > 1
++        return len(self._revs) > 1 or self._qui.bundle_radio.isChecked()
+ 
+     def _initpreviewtab(self):
+         def initqsci(w):
+@@ -345,6 +371,7 @@ class _ChangesetsModel(QAbstractTableMod
+         self._repo = repo
+         self._revs = list(reversed(sorted(revs)))
+         self._selectedrevs = set(selectedrevs)
++        self._readonly = False
+ 
+     @property
+     def revs(self):
+@@ -355,6 +382,9 @@ class _ChangesetsModel(QAbstractTableMod
+         """Return the list of selected revisions"""
+         return list(sorted(self._selectedrevs))
+ 
++    def isselectedall(self):
++        return len(self._revs) == len(self._selectedrevs)
++
+     def data(self, index, role):
+         if not index.isValid():
+             return QVariant()
+@@ -369,7 +399,7 @@ class _ChangesetsModel(QAbstractTableMod
+         return QVariant()
+ 
+     def setData(self, index, value, role=Qt.EditRole):
+-        if not index.isValid():
++        if not index.isValid() or self._readonly:
+             return False
+ 
+         rev = self._revs[index.row()]
+@@ -387,9 +417,12 @@ class _ChangesetsModel(QAbstractTableMod
+ 
+         return False
+ 
++    def setReadOnly(self, readonly):
++        self._readonly = readonly
++
+     def flags(self, index):
+         v = super(_ChangesetsModel, self).flags(index)
+-        if index.column() == 0:
++        if index.column() == 0 and not self._readonly:
+             return Qt.ItemIsUserCheckable | v
+         else:
+             return v
+@@ -421,7 +454,8 @@ def run(ui, *revs, **opts):
+     repo = opts.get('repo') or thgrepo.repository(ui, paths.find_root())
+ 
+     try:
+-        return EmailDialog(repo, revs)
++        return EmailDialog(repo, revs, outgoing=opts.get('outgoing', False),
++                           outgoingrevs=opts.get('outgoingrevs', None))
+     except error.RepoLookupError, e:
+         qtlib.ErrorMsgBox(_('Failed to open Email dialog'),
+                           hglib.tounicode(e.message))
+diff --git a/tortoisehg/hgqt/sync.py b/tortoisehg/hgqt/sync.py
+--- a/tortoisehg/hgqt/sync.py
++++ b/tortoisehg/hgqt/sync.py
+@@ -743,8 +743,13 @@ class SyncWidget(QWidget):
+                 nodes = [n for n in data.splitlines() if len(n) == 40]
+                 self.showMessage.emit(_('%d outgoing changesets') %
+                                         len(nodes))
++                try:
++                    outgoingrevs = [cmdline[cmdline.index('--rev') + 1]]
++                except ValueError:
++                    outgoingrevs = None
+                 from tortoisehg.hgqt import run as _run
+-                _run.email(ui.ui(), repo=self.repo, rev=nodes)
++                _run.email(ui.ui(), repo=self.repo, rev=nodes,
++                           outgoing=True, outgoingrevs=outgoingrevs)
+             elif ret == 1:
+                 self.showMessage.emit(_('No outgoing changesets'))
+             else:

geticon-movefindicon.diff

+# HG changeset patch
+# Parent 2f2e5133efa3dc48d79498b972c6c0b5a01cd2b5
+qtlib: move findicon() to module scope
+
+Also changed it to return None instead of fallback.svg.
+
+diff --git a/tortoisehg/hgqt/qtlib.py b/tortoisehg/hgqt/qtlib.py
+--- a/tortoisehg/hgqt/qtlib.py
++++ b/tortoisehg/hgqt/qtlib.py
+@@ -249,6 +249,17 @@ def descriptionhtmlizer(ui):
+ 
+ _iconcache = {}
+ 
++def _findicon(name):
++    # TODO: icons should be placed at single location before release
++    for pfx in (':/icons', os.path.join(paths.get_icon_path(), 'svg'),
++                paths.get_icon_path()):
++        for ext in ('svg', 'png', 'ico'):
++            path = '%s/%s.%s' % (pfx, name, ext)
++            if QFile.exists(path):
++                return QIcon(path)
++
++    return None
++
+ def geticon(name):
+     """
+     Return a QIcon for the specified name. (the given 'name' parameter
+@@ -260,21 +271,10 @@ def geticon(name):
+     if QIcon.hasThemeIcon(name):
+         return QIcon.fromTheme(name)
+ 
+-    # TODO: icons should be placed at single location before release
+-    def findicon(name):
+-        for pfx in (':/icons', os.path.join(paths.get_icon_path(), 'svg'),
+-                    paths.get_icon_path()):
+-            for ext in ('svg', 'png', 'ico'):
+-                path = '%s/%s.%s' % (pfx, name, ext)
+-                if QFile.exists(path):
+-                    return QIcon(path)
+-
+-        return QIcon(':/icons/fallback.svg')
+-
+     try:
+         return _iconcache[name]
+     except KeyError:
+-        _iconcache[name] = findicon(name)
++        _iconcache[name] = _findicon(name) or QIcon(':/icons/fallback.svg')
+         return _iconcache[name]
+ 
+ _pixmapcache = {}

geticon-scalable.diff

+# HG changeset patch
+# Parent 9fedcd2fb925db1915488ac45d89f74b954694ac
+qtlib: add support for scalable icon directory
+
+diff --git a/tortoisehg/hgqt/qtlib.py b/tortoisehg/hgqt/qtlib.py
+--- a/tortoisehg/hgqt/qtlib.py
++++ b/tortoisehg/hgqt/qtlib.py
+@@ -260,6 +260,19 @@ def _findicon(name):
+ 
+     return None
+ 
++_SCALABLE_ICON_PATHS = [(QSize(), 'scalable', '.svg'),
++                        (QSize(24, 24), '24x24', '.png')]
++
++def _findscalableicon(name, context='actions', theme='fallback'):  # XXX theme name
++    o = QIcon()
++    for size, sizename, sfx in _SCALABLE_ICON_PATHS:
++        path = ':/icons/%s/%s/%s/%s%s' % (theme, sizename, context, name, sfx)
++        if QFile.exists(path):
++            o.addFile(path, size)
++
++    if not o.isNull():
++        return o
++
+ def geticon(name):
+     """
+     Return a QIcon for the specified name. (the given 'name' parameter
+@@ -274,7 +287,8 @@ def geticon(name):
+     try:
+         return _iconcache[name]
+     except KeyError:
+-        _iconcache[name] = _findicon(name) or QIcon(':/icons/fallback.svg')
++        _iconcache[name] = (_findscalableicon(name) or _findicon(name)
++                            or QIcon(':/icons/fallback.svg'))
+         return _iconcache[name]
+ 
+ _pixmapcache = {}
+# HG changeset patch
+# Parent 310299b846cf262240eb47cc84924964895a0946
+qtlib: geticon() from system theme if available
+
+
+diff --git a/tortoisehg/hgqt/qtlib.py b/tortoisehg/hgqt/qtlib.py
+--- a/tortoisehg/hgqt/qtlib.py
++++ b/tortoisehg/hgqt/qtlib.py
+@@ -254,9 +254,12 @@ def geticon(name):
+     Return a QIcon for the specified name. (the given 'name' parameter
+     must *not* provide the extension).
+ 
+-    This searches for the icon from Qt resource or icons directory,
++    This searches for the icon from theme, Qt resource or icons directory,
+     named as 'name.(svg|png|ico)'.
+     """
++    if QIcon.hasThemeIcon(name):
++        return QIcon.fromTheme(name)
++
+     # TODO: icons should be placed at single location before release
+     def findicon(name):
+         for pfx in (':/icons', os.path.join(paths.get_icon_path(), 'svg'),

i18n-qt-uilang.diff

-# HG changeset patch
-# Parent 6047515c1491cea23dde765fbfcc414cfe9071fc
-hgqt: add configuration point to switch ui language
-
-TODO: config dialog: only userconfig
-TODO: "restart" dialog
-
-diff --git a/tortoisehg/hgqt/run.py b/tortoisehg/hgqt/run.py
---- a/tortoisehg/hgqt/run.py
-+++ b/tortoisehg/hgqt/run.py
-@@ -25,7 +25,7 @@ import mercurial.ui as _ui
- from mercurial import hg, util, fancyopts, cmdutil, extensions, error
- 
- from tortoisehg.hgqt.i18n import agettext as _
--from tortoisehg.util import hglib, paths, shlib
-+from tortoisehg.util import hglib, paths, shlib, i18n
- from tortoisehg.util import version as thgversion
- from tortoisehg.hgqt import qtlib
- from tortoisehg.hgqt.bugreport import run as bugrun
-@@ -247,6 +247,7 @@ def runcommand(ui, args):
-     cmd, func, args, options, cmdoptions, alias = _parse(ui, args)
-     cmdoptions['alias'] = alias
-     ui.setconfig("ui", "verbose", str(bool(options["verbose"])))
-+    i18n.setlanguage(ui.config('tortoisehg', 'ui.language'))
- 
-     if options['help']:
-         return help_(ui, cmd)
-diff --git a/tortoisehg/hgqt/settings.py b/tortoisehg/hgqt/settings.py
---- a/tortoisehg/hgqt/settings.py
-+++ b/tortoisehg/hgqt/settings.py
-@@ -9,7 +9,7 @@ import os
- 
- from mercurial import ui, util, error
- 
--from tortoisehg.util import hglib, settings, paths, wconfig
-+from tortoisehg.util import hglib, settings, paths, wconfig, i18n
- from tortoisehg.hgqt.i18n import _
- from tortoisehg.hgqt import qtlib, qscilib, thgrepo
- 
-@@ -247,6 +247,9 @@ def genCheckBox(opts):
- 
- INFO = (
- ({'name': 'general', 'label': 'TortoiseHg', 'icon': 'thg_logo'}, (
-+    (_('UI Language'), 'tortoisehg.ui.language',
-+        (genDeferredCombo, i18n.availablelanguages),
-+        _('Specify your preferred user interface language (restart needed)')),
-     (_('Three-way Merge Tool'), 'ui.merge',
-         (genDeferredCombo, findMergeTools),
-         _('Graphical merge program for resolving merge conflicts.  If left'

icons-arrow-scalable.diff

+# HG changeset patch
+# Parent 1e0c620160383a5772530c9d6c3d4df9cefd956b
+icons: rename up/down/back/forward icons according to xdg theme icons
+
+
+diff --git a/tortoisehg/hgqt/filedialogs.py b/tortoisehg/hgqt/filedialogs.py
+--- a/tortoisehg/hgqt/filedialogs.py
++++ b/tortoisehg/hgqt/filedialogs.py
+@@ -189,9 +189,9 @@ class FileLogDialog(_AbstractFileDialog)
+         self.actionReload.setIcon(geticon('reload'))
+ 
+         self.actionBack = QAction(_('Back'), self, enabled=False,
+-                                  icon=geticon('back'))
++                                  icon=geticon('go-previous'))
+         self.actionForward = QAction(_('Forward'), self, enabled=False,
+-                                     icon=geticon('forward'))
++                                     icon=geticon('go-next'))
+         self.repoview.revisionSelected.connect(self._updateHistoryActions)
+         self.actionBack.triggered.connect(self.repoview.back)
+         self.actionForward.triggered.connect(self.repoview.forward)
+@@ -365,11 +365,11 @@ class FileDiffDialog(_AbstractFileDialog
+         self.actionReload.triggered.connect(self.reload)
+         self.actionReload.setIcon(geticon('reload'))
+ 
+-        self.actionNextDiff = QAction(geticon('down'), 'Next diff', self)
++        self.actionNextDiff = QAction(geticon('go-down'), 'Next diff', self)
+         self.actionNextDiff.setShortcut('Alt+Down')
+         self.actionNextDiff.triggered.connect(self.nextDiff)
+ 
+-        self.actionPrevDiff = QAction(geticon('up'), 'Previous diff', self)
++        self.actionPrevDiff = QAction(geticon('go-up'), 'Previous diff', self)
+         self.actionPrevDiff.setShortcut('Alt+Up')
+         self.actionPrevDiff.triggered.connect(self.prevDiff)
+ 
+diff --git a/tortoisehg/hgqt/fileview.py b/tortoisehg/hgqt/fileview.py
+--- a/tortoisehg/hgqt/fileview.py
++++ b/tortoisehg/hgqt/fileview.py
+@@ -154,11 +154,11 @@ class HgFileView(QFrame):
+         self.modeToggleGroup.triggered.connect(self.setMode)
+ 
+         # Next/Prev diff (in full file mode)
+-        self.actionNextDiff = QAction(qtlib.geticon('down'),
++        self.actionNextDiff = QAction(qtlib.geticon('go-down'),
+                                       'Next diff (alt+down)', self)
+         self.actionNextDiff.setShortcut('Alt+Down')
+         self.actionNextDiff.triggered.connect(self.nextDiff)
+-        self.actionPrevDiff = QAction(qtlib.geticon('up'),
++        self.actionPrevDiff = QAction(qtlib.geticon('go-up'),
+                                       'Previous diff (alt+up)', self)
+         self.actionPrevDiff.setShortcut('Alt+Up')
+         self.actionPrevDiff.triggered.connect(self.prevDiff)
+diff --git a/tortoisehg/hgqt/icons/down.svg b/tortoisehg/hgqt/icons/fallback/scalable/actions/go-down.svg
+rename from tortoisehg/hgqt/icons/down.svg
+rename to tortoisehg/hgqt/icons/fallback/scalable/actions/go-down.svg
+diff --git a/tortoisehg/hgqt/icons/forward.svg b/tortoisehg/hgqt/icons/fallback/scalable/actions/go-next.svg
+rename from tortoisehg/hgqt/icons/forward.svg
+rename to tortoisehg/hgqt/icons/fallback/scalable/actions/go-next.svg
+diff --git a/tortoisehg/hgqt/icons/back.svg b/tortoisehg/hgqt/icons/fallback/scalable/actions/go-previous.svg
+rename from tortoisehg/hgqt/icons/back.svg
+rename to tortoisehg/hgqt/icons/fallback/scalable/actions/go-previous.svg
+diff --git a/tortoisehg/hgqt/icons/up.svg b/tortoisehg/hgqt/icons/fallback/scalable/actions/go-up.svg
+rename from tortoisehg/hgqt/icons/up.svg
+rename to tortoisehg/hgqt/icons/fallback/scalable/actions/go-up.svg
+diff --git a/tortoisehg/hgqt/workbench.py b/tortoisehg/hgqt/workbench.py
+--- a/tortoisehg/hgqt/workbench.py
++++ b/tortoisehg/hgqt/workbench.py
+@@ -280,10 +280,10 @@ class Workbench(QMainWindow):
+ 
+         newseparator(toolbar='edit')
+         self.actionBack = \
+-        newaction(_("Back"), self._repofwd('back'), icon='back',
++        newaction(_("Back"), self._repofwd('back'), icon='go-previous',
+                   enabled=False, toolbar='edit')
+         self.actionForward = \
+-        newaction(_("Forward"), self._repofwd('forward'), icon='forward',
++        newaction(_("Forward"), self._repofwd('forward'), icon='go-next',
+                   enabled=False, toolbar='edit')
+         newseparator(toolbar='edit', menu='View')
+ 

icons-sync-scalable.diff

+# HG changeset patch
+# Parent 323d8112521a3a361dec5aecb5249899480bfa02
+
+diff --git a/tortoisehg/hgqt/icons/incoming-24x24.png b/tortoisehg/hgqt/icons/fallback/24x24/actions/hg-incoming.png
+rename from tortoisehg/hgqt/icons/incoming-24x24.png
+rename to tortoisehg/hgqt/icons/fallback/24x24/actions/hg-incoming.png
+diff --git a/tortoisehg/hgqt/icons/outgoing-24x24.png b/tortoisehg/hgqt/icons/fallback/24x24/actions/hg-outgoing.png
+rename from tortoisehg/hgqt/icons/outgoing-24x24.png
+rename to tortoisehg/hgqt/icons/fallback/24x24/actions/hg-outgoing.png
+diff --git a/tortoisehg/hgqt/icons/pull-24x24.png b/tortoisehg/hgqt/icons/fallback/24x24/actions/hg-pull.png
+rename from tortoisehg/hgqt/icons/pull-24x24.png
+rename to tortoisehg/hgqt/icons/fallback/24x24/actions/hg-pull.png
+diff --git a/tortoisehg/hgqt/icons/push-24x24.png b/tortoisehg/hgqt/icons/fallback/24x24/actions/hg-push.png
+rename from tortoisehg/hgqt/icons/push-24x24.png
+rename to tortoisehg/hgqt/icons/fallback/24x24/actions/hg-push.png
+diff --git a/tortoisehg/hgqt/icons/incoming.svg b/tortoisehg/hgqt/icons/fallback/scalable/actions/hg-incoming.svg
+rename from tortoisehg/hgqt/icons/incoming.svg
+rename to tortoisehg/hgqt/icons/fallback/scalable/actions/hg-incoming.svg
+diff --git a/tortoisehg/hgqt/icons/outgoing.svg b/tortoisehg/hgqt/icons/fallback/scalable/actions/hg-outgoing.svg
+rename from tortoisehg/hgqt/icons/outgoing.svg
+rename to tortoisehg/hgqt/icons/fallback/scalable/actions/hg-outgoing.svg
+diff --git a/tortoisehg/hgqt/icons/pull.svg b/tortoisehg/hgqt/icons/fallback/scalable/actions/hg-pull.svg
+rename from tortoisehg/hgqt/icons/pull.svg
+rename to tortoisehg/hgqt/icons/fallback/scalable/actions/hg-pull.svg
+diff --git a/tortoisehg/hgqt/icons/push.svg b/tortoisehg/hgqt/icons/fallback/scalable/actions/hg-push.svg
+rename from tortoisehg/hgqt/icons/push.svg
+rename to tortoisehg/hgqt/icons/fallback/scalable/actions/hg-push.svg
+diff --git a/tortoisehg/hgqt/sync.py b/tortoisehg/hgqt/sync.py
+--- a/tortoisehg/hgqt/sync.py
++++ b/tortoisehg/hgqt/sync.py
+@@ -87,13 +87,13 @@ class SyncWidget(QWidget):
+         self.opbuttons = []
+         for tip, icon, cb in (
+             (_('Preview incoming changesets from specified URL'),
+-             'incoming', self.inclicked),
++             'hg-incoming', self.inclicked),
+             (_('Pull incoming changesets from specified URL'),
+-             'pull', self.pullclicked),
++             'hg-pull', self.pullclicked),
+             (_('Filter outgoing changesets to specified URL'),
+-             'outgoing', self.outclicked),
++             'hg-outgoing', self.outclicked),
+             (_('Push outgoing changesets to specified URL'),
+-             'push', self.pushclicked),
++             'hg-push', self.pushclicked),
+             (_('Email outgoing changesets for specified URL'),
+              'mail-forward', self.emailclicked)):
+             a = QAction(self)
+diff --git a/tortoisehg/hgqt/workbench.py b/tortoisehg/hgqt/workbench.py
+--- a/tortoisehg/hgqt/workbench.py
++++ b/tortoisehg/hgqt/workbench.py
+@@ -307,16 +307,16 @@ class Workbench(QMainWindow):
+         menu.addAction(self.tasktbar.toggleViewAction())
+         self.menuView.addMenu(menu)
+ 
+-        newaction(_('Pull'), self._repofwd('pull'), icon='pull-24x24',
++        newaction(_('Pull'), self._repofwd('pull'), icon='hg-pull',
+                   tooltip=_('Pull incoming changes from default pull target'),
+                   enabled='repoopen', toolbar='sync')
+-        newaction(_('Push'), self._repofwd('push'), icon='push-24x24',
++        newaction(_('Push'), self._repofwd('push'), icon='hg-push',
+                   tooltip=_('Push outgoing changes to default push target'),
+                   enabled='repoopen', toolbar='sync')
+-        newaction(_('Incoming'), self._repofwd('incoming'), icon='incoming-24x24',
++        newaction(_('Incoming'), self._repofwd('incoming'), icon='hg-incoming',
+                   tooltip=_('Check for incoming changes from default pull target'),
+                   enabled='repoopen', toolbar='sync')
+-        newaction(_('Outgoing'), self._repofwd('outgoing'), icon='outgoing-24x24',
++        newaction(_('Outgoing'), self._repofwd('outgoing'), icon='hg-outgoing',
+                    tooltip=_('Detect outgoing changes to default push target'),
+                    enabled='repoopen', toolbar='sync')
+ 

repowidget-actions.diff

+# HG changeset patch
+# Parent 8779ea2c596f507cadbcb070c3d51476ca1d44a9
+
+diff --git a/tortoisehg/hgqt/quickbar.py b/tortoisehg/hgqt/quickbar.py
+--- a/tortoisehg/hgqt/quickbar.py
++++ b/tortoisehg/hgqt/quickbar.py
+@@ -47,7 +47,7 @@ class QuickBar(QToolBar):
+         self._actions = {'open': openact}
+ 
+     def createContent(self):
+-        self.parent().addAction(self._actions['open'])
++        pass # XXX self.parent().addAction(self._actions['open'])
+ 
+     def hide(self):
+         self.setVisible(False)
+diff --git a/tortoisehg/hgqt/repowidget.py b/tortoisehg/hgqt/repowidget.py
+--- a/tortoisehg/hgqt/repowidget.py
++++ b/tortoisehg/hgqt/repowidget.py
+@@ -189,25 +189,28 @@ class RepoWidget(QWidget):
+         else:
+             return self.repo.shortname
+ 
+-    @pyqtSlot()
+-    def toggleSearchBar(self):
++    @pyqtSlot(bool)
++    def toggleSearchBar(self, visible):
+         """Toggle display of tasktab-specific search bar if available"""
++        # XXX handle visibility correctly; maybe toggleSearchBar action
++        # should also be implemented at each taskTabsWidget, and we need
++        # to reconnect it on taskTabsWidget.currentChanged signal.
+         curtt = self.taskTabsWidget.currentWidget()
+         show = getattr(curtt, 'toggleSearchBar', None)
+         if show:
+             show()
+ 
+-    @pyqtSlot()
+-    def toggleFilterBar(self):
++    @pyqtSlot(bool)
++    def toggleFilterBar(self, visible):
+         """Toggle display repowidget filter bar"""
+-        vis = self.filterbar.isVisible()
+-        self.filterbar.setVisible(not vis)
++        self.filterbar.setVisible(visible)
++        self._action_toggleFilterBar.setChecked(visible)
+ 
+-    @pyqtSlot()
+-    def toggleGotoBar(self):
++    @pyqtSlot(bool)
++    def toggleGotoBar(self, visible):
+         """Toggle display repowidget goto bar"""
+-        vis = self.gototb.isVisible()
+-        self.gototb.setVisible(not vis)
++        self.gototb.setVisible(visible)
++        self._action_toggleGotoBar.setChecked(visible)
+ 
+     @pyqtSlot(unicode)
+     def _openLink(self, link):
+@@ -403,6 +406,25 @@ class RepoWidget(QWidget):
+     def createActions(self):
+         QShortcut(QKeySequence('CTRL+P'), self, self.gotoParent)
+ 
++        # XXX This can be implemented as a decorator?
++        self._action_toggleFilterBar = QAction(
++            geticon('find'), _('Filter Toolbar'), self,
++            checkable=True, shortcut='Ctrl+S')
++        self._action_toggleFilterBar.toggled.connect(self.toggleFilterBar)
++        self.addAction(self._action_toggleFilterBar)
++
++        self._action_toggleGotoBar = QAction(
++            geticon('go-jump'), _('Goto Toolbar'), self,
++            checkable=True, shortcut='Ctrl+T')
++        self._action_toggleGotoBar.toggled.connect(self.toggleGotoBar)
++        self.addAction(self._action_toggleGotoBar)
++
++        self._action_toggleSearchBar = QAction(
++            geticon('edit-find'), _('Find in File'), self,
++            checkable=True, shortcut=QKeySequence.Find)
++        self._action_toggleSearchBar.toggled.connect(self.toggleSearchBar)
++        self.addAction(self._action_toggleSearchBar)
++
+     def dragEnterEvent(self, event):
+         paths = [unicode(u.toLocalFile()) for u in event.mimeData().urls()]
+         if util.any(os.path.isfile(p) for p in paths):
+diff --git a/tortoisehg/hgqt/workbench.py b/tortoisehg/hgqt/workbench.py
+--- a/tortoisehg/hgqt/workbench.py
++++ b/tortoisehg/hgqt/workbench.py
+@@ -284,18 +284,6 @@ class Workbench(QMainWindow):
+                   enabled='repoopen', toolbar='edit',
+                   tooltip=_('Load all revisions into graph'))
+         newseparator(toolbar='edit', menu='View')
+-        newaction(_('Filter Toolbar'), self._repofwd('toggleFilterBar'),
+-                  icon='find', shortcut='Ctrl+S', enabled='repoopen',
+-                  toolbar='edit', menu='View',
+-                  tooltip=_('Filter graph with revision sets or branches'))
+-        newaction(_('Goto Toolbar'), self._repofwd('toggleGotoBar'),
+-                  icon='go-jump', shortcut='Ctrl+T', enabled='repoopen',
+-                  toolbar='edit', menu='View',
+-                  tooltip=_('Jump to a specific revision'))
+-        newaction(_('Find in File'), self._repofwd('toggleSearchBar'),
+-                  icon='edit-find', shortcut='Find', enabled='repoopen',
+-                  toolbar='edit', menu='View', checkable=False,
+-                  tooltip=_('Search file and revision contents for keyword'))
+ 
+         newaction(_('Incoming'), self._repofwd('incoming'), icon='incoming',
+                   tooltip=_('Check for incoming changes from default pull target'),
+@@ -446,8 +434,16 @@ class Workbench(QMainWindow):
+         if w:
+             self.updateHistoryActions()
+             self.updateMenu()
++        self.updateRepoTabActions()
+         self.log.setRepository(w and w.repo or None)
+ 
++    def updateRepoTabActions(self):
++        # XXX: update menu, etc. QActionGroup may help something, not sure.
++        self.edittbar.clear()
++        w = self.repoTabsWidget.currentWidget()
++        if w:
++            self.edittbar.addActions(w.actions())
++
+     def addRepoTab(self, repo):
+         '''opens the given repo in a new tab'''
+         rw = RepoWidget(repo, self)
-i18n-qt-uilang.diff
+autogen-qrc.diff
+geticon-theme.diff
+geticon-movefindicon.diff
+geticon-scalable.diff
+icons-sync-scalable.diff
+icons-arrow-scalable.diff
+annotate-wc.diff
+ann-clear.diff
+windows-iconfont.diff
+email-minimal-bundle.diff
+email-base.diff
+email-bundle.diff
+repowidget-actions.diff
 trying-open-in-new-window.diff #+experimental
 colormap-halflife.diff
 break-qtlib.diff #+debug

windows-iconfont.diff

+# HG changeset patch
+# Parent c32aa44bcf9621e3785292f0c5b2e57bf848f12d
+qtlib: use icon-title font as the default main window font on Windows
+
+On Windows, main window seems to use "icon-title" font, but Qt doesn't.
+
+diff --git a/tortoisehg/hgqt/qtlib.py b/tortoisehg/hgqt/qtlib.py
+--- a/tortoisehg/hgqt/qtlib.py
++++ b/tortoisehg/hgqt/qtlib.py
+@@ -379,6 +379,27 @@ class CustomPrompt(QMessageBox):
+ def setup_font_substitutions():
+     QFont.insertSubstitutions('monospace', ['monaco', 'courier new'])
+ 
++def fix_application_font():
++    if os.name != 'nt':
++        return
++    try:
++        import win32gui, win32con
++    except ImportError:
++        return
++
++    # On Windows, the font for main window seems to be determined by "icon"
++    # font, but Qt chooses uncustomizable system font.
++    lf = win32gui.SystemParametersInfo(win32con.SPI_GETICONTITLELOGFONT)
++    f = QFont(hglib.tounicode(lf.lfFaceName))
++    f.setItalic(lf.lfItalic)
++    if lf.lfWeight != win32con.FW_DONTCARE:
++        weights = [(0, QFont.Light), (400, QFont.Normal), (600, QFont.DemiBold),
++                   (700, QFont.Bold), (800, QFont.Black)]
++        n, w = filter(lambda e: e[0] <= lf.lfWeight, weights)[-1]
++        f.setWeight(w)
++    f.setPixelSize(abs(lf.lfHeight))
++    QApplication.setFont(f, 'QMainWindow')
++
+ class PMButton(QPushButton):
+     """Toggle button with plus/minus icon images"""
+ 
+diff --git a/tortoisehg/hgqt/run.py b/tortoisehg/hgqt/run.py
+--- a/tortoisehg/hgqt/run.py
++++ b/tortoisehg/hgqt/run.py
+@@ -450,6 +450,7 @@ class _QtRunner(QObject):
+             self._mainapp.setOrganizationDomain('tortoisehg.org')
+             self._mainapp.setApplicationVersion(thgversion.version())
+             qtlib.setup_font_substitutions()
++            qtlib.fix_application_font()
+             qtlib.configstyles(ui)
+             qtlib.initfontcache(ui)
+             self._mainapp.setWindowIcon(qtlib.geticon('thg_logo'))