by
Changes to 7 files · Browse files at cb60f632d185 Showing diff from parent 4582ce8cabac ef7cc78eb71a Diff from another changeset...
|
@@ -0,0 +1,33 @@ + #!/usr/bin/env python
+#
+# hgqt - front-end script for TortoiseHg dialogs
+#
+# Copyright (C) 2008-2010 Steve Borho <steve@borho.org>
+# Copyright (C) 2008 TK Soh <teekaysoh@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+import os
+import sys
+
+thgpath = os.path.dirname(os.path.realpath(__file__))
+testpath = os.path.join(thgpath, 'tortoisehg')
+if os.path.isdir(testpath) and thgpath not in sys.path:
+ sys.path.insert(0, thgpath)
+
+if 'HGPATH' in os.environ:
+ hgpath = os.environ['HGPATH']
+ testpath = os.path.join(hgpath, 'mercurial')
+ if os.path.isdir(testpath) and hgpath not in sys.path:
+ sys.path.insert(0, hgpath)
+
+try:
+ import tortoisehg.hgqt.run
+except ImportError:
+ sys.stderr.write("abort: couldn't find hgtk libraries in [%s]\n" %
+ ';'.join(sys.path))
+ sys.stderr.write("(check your install and PYTHONPATH)\n")
+ sys.exit(-1)
+
+sys.exit(tortoisehg.hgqt.run.dispatch(sys.argv[1:]))
|
|
@@ -0,0 +1,21 @@ + # clone.py - Clone dialog for TortoiseHg
+#
+# Copyright 2010 Yuki KODAMA <endflow.net@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+import os
+
+from tortoisehg.util import hglib
+from tortoisehg.hgqt import cmdui
+
+def run(ui, *pats, **opts):
+ src = hglib.toutf(os.getcwd())
+ dest = src
+ if len(pats) > 1:
+ src = pats[0]
+ dest = pats[1]
+ else:
+ src = pats[0]
+ return cmdui.Dialog(['clone', src, dest])
|
|
|
@@ -0,0 +1,165 @@ + # cmdui.py - A widget to execute Mercurial command for TortoiseHg
+#
+# Copyright 2010 Yuki KODAMA <endflow.net@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+from PyQt4.QtCore import Qt, QString
+from PyQt4.QtGui import QDialog, QDialogButtonBox, QLabel, QProgressBar
+from PyQt4.QtGui import QTextEdit, QHBoxLayout, QGridLayout, QMessageBox
+
+from tortoisehg.hgqt.i18n import _, localgettext
+from tortoisehg.hgqt import qtlib, thread
+
+local = localgettext()
+
+class Dialog(QDialog):
+ def __init__(self, cmdline, parent=None):
+ super(Dialog, self).__init__(parent, Qt.WindowTitleHint or \
+ Qt.WindowSystemMenuHint)
+
+ # main layout grid
+ grid = QGridLayout()
+ grid.setSpacing(6)
+ grid.setContentsMargins(*(7,)*4)
+
+ ## hbox for status and progress labels
+ hbox = QHBoxLayout()
+ grid.addLayout(hbox, 0, 0)
+
+ self.status_label = QLabel('')
+ hbox.addWidget(self.status_label, 1)
+
+ self.prog_label = QLabel('')
+ hbox.addWidget(self.prog_label, 0, Qt.AlignRight)
+
+ self.pbar = QProgressBar()
+ self.pbar.setTextVisible(False)
+ self.pbar.setMinimum(0)
+ grid.addWidget(self.pbar, 1, 0)
+
+ # command output area
+ self.log_text = QTextEdit()
+ self.log_text.setReadOnly(True)
+ grid.addWidget(self.log_text, 2, 0, 5, 0)
+ grid.setRowStretch(2, 1)
+
+ # bottom buttons
+ buttons = QDialogButtonBox()
+ self.cancel_btn = buttons.addButton(QDialogButtonBox.Cancel)
+ self.cancel_btn.clicked.connect(self.cancel_clicked)
+ self.close_btn = buttons.addButton(QDialogButtonBox.Close)
+ self.close_btn.setHidden(True)
+ self.close_btn.clicked.connect(self.reject)
+ grid.addWidget(buttons, 7, 0)
+
+ self.setLayout(grid)
+ self.setWindowTitle(_('TortoiseHg Command Dialog'))
+ self.resize(540, 420)
+
+ # setup and start command thread
+ self.cmd = thread.CmdThread(cmdline)
+ self.cmd.outputReceived.connect(self.output_received)
+ self.cmd.errorReceived.connect(self.error_received)
+ self.cmd.progressReceived.connect(self.progress_received)
+ self.cmd.started.connect(self.command_started)
+ self.cmd.commandFinished.connect(self.command_finished)
+ self.cmd.start()
+
+ def reject(self):
+ if self.cmd.isRunning():
+ ret = QMessageBox.question(self, _('Confirm Exit'), _('Mercurial'
+ ' command is still running.\nAre you sure you want'
+ ' to terminate?'), QMessageBox.Yes | QMessageBox.No,
+ QMessageBox.No)
+ if ret == QMessageBox.Yes:
+ self.cancel_clicked()
+
+ # don't close dialog
+ return
+
+ # close dialog
+ QDialog.reject(self)
+
+ def cancel_clicked(self):
+ # trigger KeyboardInterrupt
+ self.cmd.abort()
+
+ # until thread is terminated
+ self.cancel_btn.setDisabled(True)
+ self.pbar.setMaximum(0)
+
+ def command_started(self):
+ # use indeterminate mode
+ self.pbar.setMaximum(0)
+ self.status_label.setText(_('Running...'))
+
+ def command_finished(self, wrapper):
+ ret = wrapper.data
+ if ret is None or self.pbar.maximum() == 0:
+ self.clear_progress()
+ if ret is None:
+ if self.cmd.abortbyuser:
+ status = _('Terminated by user')
+ else:
+ status = _('Terminated')
+ else:
+ status = _('Finished')
+ self.status_label.setText(status)
+ self.cancel_btn.setHidden(True)
+ self.close_btn.setShown(True)
+ self.close_btn.setFocus()
+
+ def append_output(self, msg, style=''):
+ if isinstance(msg, str):
+ msg = unicode(msg, 'mbcs')
+ msg = msg.replace('\n', '<br />')
+ self.log_text.insertHtml('<pre style="%s">%s</pre>' % (style, msg))
+
+ def output_received(self, wrapper):
+ msg, label = wrapper.data
+ style = qtlib.LabelStyles.get(label, '')
+ style += 'font-size: 9pt;'
+ self.append_output(msg, style)
+
+ def error_received(self, wrapper):
+ self.append_output(wrapper.data, qtlib.LabelStyles['error'])
+
+ def clear_progress(self):
+ self.pbar.reset()
+ self.pbar.setMaximum(100)
+ self.status_label.setText('')
+ self.prog_label.setText('')
+ self.inprogress = False
+
+ def progress_received(self, wrapper):
+ if self.cmd.isFinished():
+ self.clear_progress()
+ return
+
+ counting = False
+ topic, item, pos, total, unit = wrapper.data
+ if pos is None:
+ self.clear_progress()
+ return
+ if total is None:
+ count = '%d' % pos
+ counting = True
+ else:
+ self.pbar.setMaximum(total)
+ self.pbar.setValue(pos)
+ count = '%d / %d' % (pos, total)
+ if unit:
+ count += ' ' + unit
+ self.prog_label.setText(unicode(count, 'mbcs'))
+ if item:
+ status = '%s: %s' % (topic, item)
+ else:
+ status = local._('Status: %s') % topic
+ self.status_label.setText(unicode(status, 'mbcs'))
+ self.inprogress = True
+
+ if not self.inprogress or counting:
+ # use indeterminate mode
+ self.pbar.setMinimum(0)
|
|
@@ -0,0 +1,16 @@ + # i18n.py - internationalization support for TortoiseHg
+#
+# Copyright 2010 Yuki KODAMA <endflow.net@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+from tortoisehg.util.i18n import _ as _gettext
+from tortoisehg.util.i18n import agettext, keepgettext
+
+def _(message):
+ return unicode(_gettext(message), 'utf-8')
+
+class localgettext(object):
+ def _(self, message):
+ return agettext(message)
|
|
@@ -0,0 +1,88 @@ + # common colors
+
+DRED = '#900000'
+DGREEN = '#006400'
+DBLUE = '#000090'
+DYELLOW = '#6A6A00'
+DORANGE = '#AA5000'
+DGRAY = '#404040'
+
+PRED = '#ffcccc'
+PGREEN = '#aaffaa'
+PBLUE = '#aaddff'
+PYELLOW = '#ffffaa'
+PORANGE = '#ffddaa'
+
+RED = 'red'
+GREEN = 'green'
+BLUE = 'blue'
+YELLOW = 'yellow'
+BLACK = 'black'
+WHITE = 'white'
+GRAY = 'gray'
+
+NORMAL = BLACK
+NEW_REV_COLOR = DGREEN
+CHANGE_HEADER = GRAY
+UP_ARROW_COLOR = '#feaf3e'
+DOWN_ARROW_COLOR = '#8ae234'
+STAR_COLOR = '#fce94f'
+CELL_GRAY = '#2e3436'
+STATUS_HEADER = '#DDDDDD'
+STATUS_REJECT_BACKGROUND = '#EEEEEE'
+STATUS_REJECT_FOREGROUND = '#888888'
+
+LabelStyles = {
+ 'error': 'font-weight: bold; color: %s;' % DRED,
+ 'control': 'font-weight: bold; color: %s;' % BLACK,
+ 'ui.debug': 'font-weight: lighter; color: %s;' % BLACK,
+ 'ui.status': 'color: %s;' % DGRAY,
+ 'ui.note': 'color: %s;' % BLACK,
+ 'ui.warning': 'font-weight: bold; color: %s;' % RED,
+ 'log.summary': 'color: %s;' % BLACK,
+ 'log.description': 'color: %s;' % DGRAY,
+ 'log.changeset': 'color: %s;' % GRAY,
+ 'log.tag': 'color: %s;' % RED,
+ 'log.user': 'color: %s;' % BLUE,
+ 'log.date': 'color: %s;' % BLACK,
+ 'log.files': 'color: %s;' % BLACK,
+ 'log.copies': 'font-weight: bold; color: %s;' % BLACK,
+ 'log.node': 'color: %s;' % BLACK,
+ 'log.branch': 'color: %s;' % BLACK,
+ 'log.parent': 'color: %s;' % BLACK,
+ 'log.manifest': 'color: %s;' % BLACK,
+ 'log.extra': 'color: %s;' % BLACK,
+ 'diff.diffline': 'color: %s;' % BLACK,
+ 'diff.inserted': 'color: %s;' % DGREEN,
+ 'diff.deleted': 'color: %s;' % RED,
+ 'diff.hunk': 'color: %s;' % BLUE,
+ 'diff.file_a': 'font-weight: bold; color: %s;' % BLACK,
+ 'diff.file_b': 'font-weight: bold; color: %s;' % BLACK,
+}
+
+# These labels are unreachable by TortoiseHg consoles, so we leave them
+# out for efficiency
+unusedLabelStyles = {
+ 'qseries.applied': 'color: %s;' % BLACK,
+ 'qseries.unapplied':'color: %s;' % DGRAY,
+ 'qseries.guarded': 'color: %s;' % BLUE,
+ 'qseries.missing': 'color: %s;' % DRED,
+ 'qguard.patch': 'color: %s;' % BLACK,
+ 'qguard.positive': 'color: %s;' % DGREEN,
+ 'qguard.negagive': 'color: %s;' % BLUE,
+ 'qguard.unguarded': 'color: %s;' % DGRAY,
+ 'diffstat.inserted':'color: %s;' % DGREEN,
+ 'diffstat.deleted': 'color: %s;' % RED,
+ 'bookmarks.current':'font-weight: bold; color: %s;' % BLACK,
+ 'resolve.resolved': 'color: %s;' % DGREEN,
+ 'resolve.unresolved':'color: %s;' % RED,
+ 'grep.match': 'font-weight: bold; color: %s;' % BLACK,
+ 'status.modified': 'color: %s;' % BLACK,
+ 'status.added': 'color: %s;' % BLACK,
+ 'status.removed': 'color: %s;' % BLACK,
+ 'status.missing': 'color: %s;' % BLACK,
+ 'status.unknown': 'color: %s;' % BLACK,
+ 'status.ignored': 'color: %s;' % BLACK,
+ 'status.clean': 'color: %s;' % BLACK,
+ 'status.copied': 'color: %s;' % BLACK,
+}
|
|
|
@@ -0,0 +1,521 @@ + # run.py - front-end script for TortoiseHg dialogs
+#
+# Copyright 2008 Steve Borho <steve@borho.org>
+# Copyright 2008 TK Soh <teekaysoh@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+shortlicense = '''
+Copyright (C) 2008-2010 Steve Borho <steve@borho.org> and others.
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+'''
+
+import os
+import pdb
+import sys
+import subprocess
+import traceback
+
+from PyQt4 import QtGui
+
+import mercurial.ui as _ui
+from mercurial import hg, util, fancyopts, cmdutil, extensions, error
+
+from tortoisehg.util.i18n import agettext as _
+from tortoisehg.util import hglib, paths, shlib
+from tortoisehg.util import version as thgversion
+try:
+ from tortoisehg.util.config import nofork as config_nofork
+except ImportError:
+ config_nofork = None
+
+try:
+ import win32con
+ openflags = win32con.CREATE_NO_WINDOW
+except ImportError:
+ openflags = 0
+
+nonrepo_commands = '''userconfig shellconfig clone debugcomplete init
+about help version thgstatus serve'''
+
+def dispatch(args):
+ "run the command specified in args"
+ try:
+ u = _ui.ui()
+ if '--traceback' in args:
+ u.setconfig('ui', 'traceback', 'on')
+ if '--debugger' in args:
+ pdb.set_trace()
+ return _runcatch(u, args)
+ except SystemExit:
+ pass
+ except KeyboardInterrupt:
+ print _('\nCaught keyboard interrupt, aborting.\n')
+ except:
+ pass
+
+origwdir = os.getcwd()
+def portable_fork(ui, opts):
+ if 'THG_HGTK_SPAWN' in os.environ or (
+ not opts.get('fork') and opts.get('nofork')):
+ return
+ elif ui.configbool('tortoisehg', 'hgtkfork', None) is not None:
+ if not ui.configbool('tortoisehg', 'hgtkfork'):
+ return
+ elif config_nofork:
+ return
+ # Spawn background process and exit
+ if hasattr(sys, "frozen"):
+ args = sys.argv
+ else:
+ args = [sys.executable] + sys.argv
+ os.environ['THG_HGTK_SPAWN'] = '1'
+ cmdline = subprocess.list2cmdline(args)
+ os.chdir(origwdir)
+ subprocess.Popen(cmdline,
+ creationflags=openflags,
+ shell=True)
+ sys.exit(0)
+
+def get_list_from_file(filename):
+ try:
+ if filename == '-':
+ lines = [ x.replace("\n", "") for x in sys.stdin.readlines() ]
+ else:
+ fd = open(filename, "r")
+ lines = [ x.replace("\n", "") for x in fd.readlines() ]
+ fd.close()
+ os.unlink(filename)
+ except IOError, e:
+ sys.stderr.write(_('can not read file "%s". Ignored.\n') % filename)
+ return []
+
+ # Convert absolute file paths to repo/cwd canonical
+ cwd = os.getcwd()
+ root = paths.find_root(cwd)
+ if not root:
+ return lines
+ if cwd == root:
+ cwd_rel = ''
+ else:
+ cwd_rel = cwd[len(root+os.sep):] + os.sep
+ files = []
+ for f in lines:
+ try:
+ cpath = util.canonpath(root, cwd, f)
+ # canonpath will abort on .hg/ paths
+ except util.Abort:
+ continue
+ if cpath.startswith(cwd_rel):
+ cpath = cpath[len(cwd_rel):]
+ files.append(cpath)
+ else:
+ files.append(f)
+ return files
+
+def _parse(ui, args):
+ options = {}
+ cmdoptions = {}
+
+ try:
+ args = fancyopts.fancyopts(args, globalopts, options)
+ except fancyopts.getopt.GetoptError, inst:
+ raise error.ParseError(None, inst)
+
+ if args:
+ alias, args = args[0], args[1:]
+ aliases, i = cmdutil.findcmd(alias, table, ui.config("ui", "strict"))
+ for a in aliases:
+ if a.startswith(alias):
+ alias = a
+ break
+ cmd = aliases[0]
+ c = list(i[1])
+ else:
+ alias = None
+ cmd = None
+ c = []
+
+ # combine global options into local
+ for o in globalopts:
+ c.append((o[0], o[1], options[o[1]], o[3]))
+
+ try:
+ args = fancyopts.fancyopts(args, c, cmdoptions)
+ except fancyopts.getopt.GetoptError, inst:
+ raise error.ParseError(cmd, inst)
+
+ # separate global options back out
+ for o in globalopts:
+ n = o[1]
+ options[n] = cmdoptions[n]
+ del cmdoptions[n]
+
+ listfile = options.get('listfile')
+ if listfile:
+ del options['listfile']
+ args += get_list_from_file(listfile)
+
+ return (cmd, cmd and i[0] or None, args, options, cmdoptions, alias)
+
+def _runcatch(ui, args):
+ try:
+ try:
+ return runcommand(ui, args)
+ finally:
+ ui.flush()
+ except error.ParseError, inst:
+ if inst.args[0]:
+ ui.status(_("hgtk %s: %s\n") % (inst.args[0], inst.args[1]))
+ help_(ui, inst.args[0])
+ else:
+ ui.status(_("hgtk: %s\n") % inst.args[1])
+ help_(ui, 'shortlist')
+ except error.AmbiguousCommand, inst:
+ ui.status(_("hgtk: command '%s' is ambiguous:\n %s\n") %
+ (inst.args[0], " ".join(inst.args[1])))
+ except error.UnknownCommand, inst:
+ ui.status(_("hgtk: unknown command '%s'\n") % inst.args[0])
+ help_(ui, 'shortlist')
+ except error.RepoError, inst:
+ ui.status(_("abort: %s!\n") % inst)
+
+ return -1
+
+def runcommand(ui, args):
+ fullargs = args
+ cmd, func, args, options, cmdoptions, alias = _parse(ui, args)
+ cmdoptions['alias'] = alias
+ ui.setconfig("ui", "verbose", str(bool(options["verbose"])))
+
+ if options['help']:
+ return help_(ui, cmd)
+ elif not cmd:
+ return help_(ui, 'shortlist')
+
+ path = options['repository']
+ if path:
+ if path.startswith('bundle:'):
+ s = path[7:].split('+', 1)
+ if len(s) == 1:
+ path, bundle = os.getcwd(), s[0]
+ else:
+ path, bundle = s
+ cmdoptions['bundle'] = os.path.abspath(bundle)
+ path = ui.expandpath(path)
+ cmdoptions['repository'] = path
+ os.chdir(path)
+ if options['fork']:
+ cmdoptions['fork'] = True
+ if options['nofork'] or options['profile']:
+ cmdoptions['nofork'] = True
+ path = paths.find_root(os.getcwd())
+ if path:
+ try:
+ lui = ui.copy()
+ lui.readconfig(os.path.join(path, ".hg", "hgrc"))
+ except IOError:
+ pass
+ else:
+ lui = ui
+
+ extensions.loadall(ui)
+
+ if options['quiet']:
+ ui.quiet = True
+
+ if cmd not in nonrepo_commands.split() and not path:
+ raise error.RepoError(_("There is no Mercurial repository here"
+ " (.hg not found)"))
+
+ cmdoptions['mainapp'] = True
+ d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
+ return _runcommand(lui, options, cmd, d)
+
+def _runcommand(ui, options, cmd, cmdfunc):
+ def checkargs():
+ try:
+ return cmdfunc()
+ except error.SignatureError:
+ raise error.ParseError(cmd, _("invalid arguments"))
+
+ if options['profile']:
+ format = ui.config('profiling', 'format', default='text')
+
+ if not format in ['text', 'kcachegrind']:
+ ui.warn(_("unrecognized profiling format '%s'"
+ " - Ignored\n") % format)
+ format = 'text'
+
+ output = ui.config('profiling', 'output')
+
+ if output:
+ path = ui.expandpath(output)
+ ostream = open(path, 'wb')
+ else:
+ ostream = sys.stderr
+
+ try:
+ from mercurial import lsprof
+ except ImportError:
+ raise util.Abort(_(
+ 'lsprof not available - install from '
+ 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
+ p = lsprof.Profiler()
+ p.enable(subcalls=True)
+ try:
+ return checkargs()
+ finally:
+ p.disable()
+
+ if format == 'kcachegrind':
+ import lsprofcalltree
+ calltree = lsprofcalltree.KCacheGrind(p)
+ calltree.output(ostream)
+ else:
+ # format == 'text'
+ stats = lsprof.Stats(p.getstats())
+ stats.sort()
+ stats.pprint(top=10, file=ostream, climit=5)
+
+ if output:
+ ostream.close()
+ else:
+ return checkargs()
+
+def qtrun(dlgfunc, ui, *args, **opts):
+ portable_fork(ui, opts)
+ app = QtGui.QApplication(sys.argv)
+ dlg = dlgfunc(ui, *args, **opts)
+ if not dlg:
+ return
+ dlg.show()
+ app.exec_()
+
+def thgstatus(ui, *pats, **opts):
+ """update TortoiseHg status cache"""
+ from tortoisehg.util.thgstatus import run
+ run(ui, *pats, **opts)
+
+def clone(ui, *pats, **opts):
+ """clone tool"""
+ from tortoisehg.hgqt.clone import run
+ qtrun(run, ui, *pats, **opts)
+
+### help management, adapted from mercurial.commands.help_()
+def help_(ui, name=None, with_version=False, **opts):
+ """show help for a command, extension, or list of commands
+
+ With no arguments, print a list of commands and short help.
+
+ Given a command name, print help for that command.
+
+ Given an extension name, print help for that extension, and the
+ commands it provides."""
+ option_lists = []
+
+ def addglobalopts(aliases):
+ if ui.verbose:
+ option_lists.append((_("global options:"), globalopts))
+ if name == 'shortlist':
+ option_lists.append((_('use "hgtk help" for the full list '
+ 'of commands'), ()))
+ else:
+ if name == 'shortlist':
+ msg = _('use "hgtk help" for the full list of commands '
+ 'or "hgtk -v" for details')
+ elif aliases:
+ msg = _('use "hgtk -v help%s" to show aliases and '
+ 'global options') % (name and " " + name or "")
+ else:
+ msg = _('use "hgtk -v help %s" to show global options') % name
+ option_lists.append((msg, ()))
+
+ def helpcmd(name):
+ if with_version:
+ version(ui)
+ ui.write('\n')
+
+ try:
+ aliases, i = cmdutil.findcmd(name, table, False)
+ except error.AmbiguousCommand, inst:
+ select = lambda c: c.lstrip('^').startswith(inst.args[0])
+ helplist(_('list of commands:\n\n'), select)
+ return
+
+ # synopsis
+ ui.write("%s\n" % i[2])
+
+ # aliases
+ if not ui.quiet and len(aliases) > 1:
+ ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
+
+ # description
+ doc = i[0].__doc__
+ if not doc:
+ doc = _("(No help text available)")
+ if ui.quiet:
+ doc = doc.splitlines(0)[0]
+ ui.write("\n%s\n" % doc.rstrip())
+
+ if not ui.quiet:
+ # options
+ if i[1]:
+ option_lists.append((_("options:\n"), i[1]))
+
+ addglobalopts(False)
+
+ def helplist(header, select=None):
+ h = {}
+ cmds = {}
+ for c, e in table.iteritems():
+ f = c.split("|", 1)[0]
+ if select and not select(f):
+ continue
+ if (not select and name != 'shortlist' and
+ e[0].__module__ != __name__):
+ continue
+ if name == "shortlist" and not f.startswith("^"):
+ continue
+ f = f.lstrip("^")
+ if not ui.debugflag and f.startswith("debug"):
+ continue
+ doc = e[0].__doc__
+ if doc and 'DEPRECATED' in doc and not ui.verbose:
+ continue
+ #doc = gettext(doc)
+ if not doc:
+ doc = _("(no help text available)")
+ h[f] = doc.splitlines()[0].rstrip()
+ cmds[f] = c.lstrip("^")
+
+ if not h:
+ ui.status(_('no commands defined\n'))
+ return
+
+ ui.status(header)
+ fns = sorted(h)
+ m = max(map(len, fns))
+ for f in fns:
+ if ui.verbose:
+ commands = cmds[f].replace("|",", ")
+ ui.write(" %s:\n %s\n"%(commands, h[f]))
+ else:
+ ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
+
+ if not ui.quiet:
+ addglobalopts(True)
+
+ def helptopic(name):
+ from mercurial import help
+ for names, header, doc in help.helptable:
+ if name in names:
+ break
+ else:
+ raise error.UnknownCommand(name)
+
+ # description
+ if not doc:
+ doc = _("(No help text available)")
+ if hasattr(doc, '__call__'):
+ doc = doc()
+
+ ui.write("%s\n" % header)
+ ui.write("%s\n" % doc.rstrip())
+
+ if name and name != 'shortlist':
+ i = None
+ for f in (helpcmd, helptopic):
+ try:
+ f(name)
+ i = None
+ break
+ except error.UnknownCommand, inst:
+ i = inst
+ if i:
+ raise i
+
+ else:
+ # program name
+ if ui.verbose or with_version:
+ version(ui)
+ else:
+ ui.status(_("Hgtk - TortoiseHg's GUI tools for Mercurial SCM (Hg)\n"))
+ ui.status('\n')
+
+ # list of commands
+ if name == "shortlist":
+ header = _('basic commands:\n\n')
+ else:
+ header = _('list of commands:\n\n')
+
+ helplist(header)
+
+ # list all option lists
+ opt_output = []
+ for title, options in option_lists:
+ opt_output.append(("\n%s" % title, None))
+ for shortopt, longopt, default, desc in options:
+ if "DEPRECATED" in desc and not ui.verbose: continue
+ opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
+ longopt and " --%s" % longopt),
+ "%s%s" % (desc,
+ default
+ and _(" (default: %s)") % default
+ or "")))
+
+ if opt_output:
+ opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
+ for first, second in opt_output:
+ if second:
+ ui.write(" %-*s %s\n" % (opts_len, first, second))
+ else:
+ ui.write("%s\n" % first)
+
+def version(ui, **opts):
+ """output version and copyright information"""
+ ui.write(_('TortoiseHg Dialogs (version %s), '
+ 'Mercurial (version %s)\n') %
+ (hglib.fromutf(thgversion.version()), hglib.hgversion))
+ if not ui.quiet:
+ ui.write(shortlicense)
+
+def debugcomplete(ui, cmd='', **opts):
+ """output list of possible commands"""
+ if opts.get('options'):
+ options = []
+ otables = [globalopts]
+ if cmd:
+ aliases, entry = cmdutil.findcmd(cmd, table, False)
+ otables.append(entry[1])
+ for t in otables:
+ for o in t:
+ if o[0]:
+ options.append('-%s' % o[0])
+ options.append('--%s' % o[1])
+ ui.write("%s\n" % "\n".join(options))
+ return
+
+ cmdlist = cmdutil.findpossible(cmd, table)
+ if ui.verbose:
+ cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
+ ui.write("%s\n" % "\n".join(sorted(cmdlist)))
+
+globalopts = [
+ ('R', 'repository', '',
+ _('repository root directory or symbolic path name')),
+ ('v', 'verbose', None, _('enable additional output')),
+ ('q', 'quiet', None, _('suppress output')),
+ ('h', 'help', None, _('display help and exit')),
+ ('', 'debugger', None, _('start debugger')),
+ ('', 'profile', None, _('print command execution profile')),
+ ('', 'nofork', None, _('do not fork GUI process')),
+ ('', 'fork', None, _('always fork GUI process')),
+ ('', 'listfile', '', _('read file list from file')),
+]
+
+table = {
+ "^clone": (clone, [], _('hgtk clone SOURCE [DEST]')),
+}
|
|
|
@@ -0,0 +1,226 @@ + # thread.py - A seprated thread to run Mercurial command
+#
+# Copyright 2009 Steve Borho <steve@borho.org>
+# Copyright 2010 Yuki KODAMA <endflow.net@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+import Queue
+import time
+import urllib2
+
+from PyQt4.QtCore import SIGNAL, pyqtSignal, QObject, QThread
+from PyQt4.QtGui import QMessageBox, QInputDialog, QLineEdit
+
+from mercurial import ui, util, error, dispatch
+
+from tortoisehg.util import thread2, hglib
+from tortoisehg.hgqt.i18n import _, localgettext
+
+local = localgettext()
+
+SIG_OUTPUT = SIGNAL('output(PyQt_PyObject)')
+SIG_ERROR = SIGNAL('error(PyQt_PyObject)')
+SIG_INTERACT = SIGNAL('interact(PyQt_PyObject)')
+SIG_PROGRESS = SIGNAL('progress(PyQt_PyObject)')
+
+class QtUi(ui.ui):
+ def __init__(self, src=None, responseq=None):
+ super(QtUi, self).__init__(src)
+
+ if src:
+ self.sig = src.sig
+ self.responseq = src.responseq
+ else:
+ self.sig = QObject() # dummy object to emit signals
+ self.responseq = responseq
+
+ self.setconfig('ui', 'interactive', 'on')
+ self.setconfig('progress', 'disable', 'True')
+
+ def write(self, *args, **opts):
+ if self._buffers:
+ self._buffers[-1].extend([str(a) for a in args])
+ else:
+ for a in args:
+ data = DataWrapper((str(a), opts.get('label', '')))
+ self.sig.emit(SIG_OUTPUT, data)
+
+ def write_err(self, *args, **opts):
+ for a in args:
+ data = DataWrapper(str(a))
+ self.sig.emit(SIG_ERROR, data)
+
+ def label(self, msg, label):
+ return msg
+
+ def flush(self):
+ pass
+
+ def prompt(self, msg, choices=None, default='y'):
+ if not self.interactive(): return default
+ try:
+ # emit SIG_INTERACT signal
+ data = DataWrapper((msg, False, choices, None))
+ self.sig.emit(SIG_INTERACT, data)
+ # await response
+ r = self.responseq.get(True)
+ if r is None:
+ raise EOFError
+ if not r:
+ return default
+ if choices:
+ # return char for Mercurial 1.3
+ choice = choices[r]
+ return choice[choice.index('&')+1].lower()
+ return r
+ except EOFError:
+ raise util.Abort(local._('response expected'))
+
+ def promptchoice(self, msg, choices, default=0):
+ if not self.interactive(): return default
+ try:
+ # emit SIG_INTERACT signal
+ data = DataWrapper((msg, False, choices, default))
+ self.sig.emit(SIG_INTERACT, data)
+ # await response
+ r = self.responseq.get(True)
+ if r is None:
+ raise EOFError
+ return r
+ except EOFError:
+ raise util.Abort(local._('response expected'))
+
+ def getpass(self, prompt=_('password: '), default=None):
+ # emit SIG_INTERACT signal
+ data = DataWrapper((prompt, True, None, default))
+ self.sig.emit(SIG_INTERACT, data)
+ # await response
+ r = self.responseq.get(True)
+ if r is None:
+ raise util.Abort(local._('response expected'))
+ return r
+
+ def progress(self, topic, pos, item='', unit='', total=None):
+ data = DataWrapper((topic, item, pos, total, unit))
+ self.sig.emit(SIG_PROGRESS, data)
+
+class DataWrapper(QObject):
+ def __init__(self, data):
+ super(DataWrapper, self).__init__(None)
+ self.data = data
+
+class CmdThread(QThread):
+ """Run an Mercurial command in a background thread, implies output
+ is being sent to a rendered text buffer interactively and requests
+ for feedback from Mercurial can be handled by the user via dialog
+ windows.
+ """
+ # (msg=str, label=str) [wrapped]
+ outputReceived = pyqtSignal(DataWrapper)
+
+ # msg=str [wrapped]
+ errorReceived = pyqtSignal(DataWrapper)
+
+ # (msg=str, password=bool, choices=tuple, default=str) [wrapped]
+ # password: whether should be masked by asterisk chars
+ # choices: tuple of choice strings
+ interactReceived = pyqtSignal(DataWrapper)
+
+ # (topic=str, item=str, pos=int, total=int, unit=str) [wrapped]
+ progressReceived = pyqtSignal(DataWrapper)
+
+ # result=int or None [wrapped]
+ # result: None - command is incomplete, possibly exited with exception
+ # 0 - command is finished successfully
+ # others - return code of command
+ commandFinished = pyqtSignal(DataWrapper)
+
+ def __init__(self, cmdline, parent=None):
+ super(QThread, self).__init__(None)
+
+ self.cmdline = cmdline
+ self.parent = parent
+ self.ret = None
+ self.abortbyuser = False
+ self.responseq = Queue.Queue()
+ self.ui = QtUi(responseq=self.responseq)
+
+ # Re-emit all ui.sig's signals to CmdThread (self).
+ # QSignalMapper doesn't help for this since our SIGNAL
+ # parameters contain 'PyQt_PyObject' types.
+ for name, sig in ((SIG_OUTPUT, self.outputReceived),
+ (SIG_ERROR, self.errorReceived),
+ (SIG_INTERACT, self.interactReceived),
+ (SIG_PROGRESS, self.progressReceived)):
+ def repeater(sig): # hide 'sig' local variable name
+ return lambda data: sig.emit(data)
+ self.connect(self.ui.sig, name, repeater(sig))
+
+ self.finished.connect(self.thread_finished)
+ self.interactReceived.connect(self.interact_handler)
+
+ def abort(self):
+ if self.isRunning() and hasattr(self, 'thread_id'):
+ self.abortbyuser = True
+ thread2._async_raise(self.thread_id, KeyboardInterrupt)
+
+ def thread_finished(self):
+ self.commandFinished.emit(DataWrapper(self.ret))
+
+ def interact_handler(self, wrapper):
+ prompt, password, choices, default = wrapper.data
+ if isinstance(prompt, str):
+ prompt = unicode(prompt, 'mbcs')
+ if choices:
+ dlg = QMessageBox(QMessageBox.Question,
+ _('TortoiseHg Prompt'), prompt,
+ QMessageBox.Yes | QMessageBox.Cancel, self.parent)
+ dlg.setDefaultButton(QMessageBox.Cancel)
+ rmap = {}
+ for index, choice in enumerate(choices):
+ button = dlg.addButton(unicode(choice, 'mbcs'),
+ QMessageBox.ActionRole)
+ rmap[id(button)] = index
+ dlg.exec_()
+ button = dlg.clickedButton()
+ if button is 0:
+ result = default
+ else:
+ result = rmap[id(button)]
+ self.responseq.put(result)
+ else:
+ mode = password and QLineEdit.Password \
+ or QLineEdit.Normal
+ text, ok = QInputDialog().getText(self.parent,
+ _('TortoiseHg Prompt'), prompt, mode)
+ self.responseq.put(ok and text or None)
+
+ def run(self):
+ # save thread id in order to terminate by KeyboardInterrupt
+ self.thread_id = int(QThread.currentThreadId())
+
+ try:
+ for k, v in self.ui.configitems('defaults'):
+ self.ui.setconfig('defaults', k, '')
+ self.ret = dispatch._dispatch(self.ui, self.cmdline) or 0
+ except util.Abort, e:
+ self.ui.write_err(local._('abort: ') + str(e) + '\n')
+ except (error.RepoError, urllib2.HTTPError), e:
+ self.ui.write_err(str(e) + '\n')
+ except (Exception, OSError, IOError), e:
+ self.ui.write_err(str(e) + '\n')
+ except KeyboardInterrupt:
+ pass
+
+ if self.ret is None:
+ if self.abortbyuser:
+ msg = _('[command terminated by user %s]')
+ else:
+ msg = _('[command interrupted %s]')
+ elif self.ret:
+ msg = _('[command returned code %d %%s]') % int(ret)
+ else:
+ msg = _('[command completed successfully %s]')
+ self.ui.write(msg % time.asctime() + '\n', label='control')
|
Loading...