示例#1
0
 def __init__(self, ui, repo, cwd, pats, opts, main):
     gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
     self.cwd = cwd
     self.ui = ui
     self.ui.interactive = False
     self.repo = repo
     self.pats = pats
     self.opts = opts
     self.main = main
     self.tmproot = None
     self.toolbuttons = {}
     self.settings = Settings(self.__class__.__name__)
     self.init()
示例#2
0
class GDialog(gtk.Window):
    """GTK+ based dialog for displaying mercurial information

    The following methods are meant to be overridden by subclasses. At this
    point GCommit is really the only intended subclass.

        parse_opts(self)
        get_title(self)
        get_minsize(self)
        get_defsize(self)
        get_tbbuttons(self)
        get_body(self)
        get_extras(self)
        prepare_display(self)
        should_live(self, widget, event)
        save_settings(self)
        load_settings(self, settings)
    """

    # "Constants"
    settings_version = 1

    def __init__(self, ui, repo, cwd, pats, opts, main):
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        self.cwd = cwd
        self.ui = ui
        self.ui.interactive = False
        self.repo = repo
        self.pats = pats
        self.opts = opts
        self.main = main
        self.tmproot = None
        self.toolbuttons = {}
        self.settings = Settings(self.__class__.__name__)
        self.init()

    ### Following methods are meant to be overridden by subclasses ###

    def init(self):
        pass

    def parse_opts(self):
        pass

    def get_title(self):
        return ""

    def get_icon(self):
        return ""

    def get_minsize(self):
        return (395, 200)

    def get_defsize(self):
        return self._setting_defsize

    def get_tbbuttons(self):
        return []

    def get_body(self):
        return None

    def get_extras(self):
        return None

    def prepare_display(self):
        pass

    def should_live(self, widget=None, event=None):
        return False

    def save_settings(self):
        rect = self.get_allocation()
        return {"gdialog": (rect.width, rect.height)}

    def load_settings(self, settings):
        if settings:
            self._setting_defsize = settings["gdialog"]
        else:
            self._setting_defsize = (678, 585)

    ### End of overridable methods ###

    def display(self, opengui=True):
        self._parse_config()
        self._load_settings()
        if opengui:
            self._setup_gtk()
            self._parse_opts()
            self.prepare_display()
            self.show_all()
        else:
            self._parse_opts()
            self.tooltips = gtk.Tooltips()

    def test_opt(self, opt):
        return opt in self.opts and self.opts[opt]

    def _parse_config(self):
        # defaults
        self.fontcomment = "monospace 10"
        self.fontdiff = "monospace 10"
        self.fontlist = "monospace 9"
        self.diffopts = ""
        self.diffcmd = ""
        self.diffbottom = ""

        for attr, setting in self.ui.configitems("gtools"):
            if setting:
                setattr(self, attr, setting)

        if not self.diffcmd:
            # default to tortoisehg's configuration
            vdiff = self.ui.config("tortoisehg", "vdiff")
            if vdiff:
                self.diffcmd = self.ui.config("extdiff", "cmd." + vdiff) or vdiff
            else:
                self.diffcmd = "diff"
                if not self.diffopts:
                    self.diffopts = "-Npru"

        if not self.diffbottom:
            self.diffbottom = False
        elif self.diffbottom.lower() == "false" or self.diffbottom == "0":
            self.diffbottom = False
        else:
            self.diffbottom = True

    def _parse_opts(self):
        # Remove dry_run since Hg only honors it for certain commands
        self.opts["dry_run"] = False
        self.opts["force_editor"] = False
        self.parse_opts()

    def merge_opts(self, defaults, mergelist=()):
        """Merge default options with the specified local options and globals.
        Results is defaults + merglist + globals
        """
        newopts = {}
        for hgopt in defaults:
            newopts[hgopt[1].replace("-", "_")] = hgopt[2]
        for mergeopt in mergelist:
            newopts[mergeopt] = self.opts[mergeopt]
        newopts.update(self.global_opts())
        return newopts

    def global_opts(self):
        globals = {}
        hgglobals = [opt[1].replace("-", "_") for opt in commands.globalopts if opt[1] != "help"]
        for key in self.opts:
            if key in hgglobals:
                globals[key] = self.opts[key]
        return globals

    def count_revs(self):
        cnt = 0
        if self.test_opt("rev"):
            for rev in self.opts["rev"]:
                cnt += len(rev.split(cmdutil.revrangesep, 1))
        return cnt

    def make_toolbutton(self, stock, label, handler, userdata=None, menu=None, tip=None):
        if menu:
            tbutton = gtk.MenuToolButton(stock)
            tbutton.set_menu(menu)
        else:
            tbutton = gtk.ToolButton(stock)

        if tip:
            tbutton.set_tooltip(self.tooltips, tip)
        tbutton.set_use_underline(True)
        tbutton.set_label(label)
        tbutton.connect("clicked", handler, userdata)
        self.toolbuttons[label] = tbutton
        return tbutton

    def get_toolbutton(self, label):
        return self.toolbuttons[label]

    def _setup_gtk(self):
        self.set_title(self.get_title())
        set_tortoise_icon(self, self.get_icon())

        # Minimum size
        minx, miny = self.get_minsize()
        self.set_size_request(minx, miny)
        # Initial size
        defx, defy = self.get_defsize()
        self.set_default_size(defx, defy)

        vbox = gtk.VBox(False, 0)
        self.add(vbox)

        self.tooltips = gtk.Tooltips()
        toolbar = gtk.Toolbar()
        tbuttons = self.get_tbbuttons()
        for tbutton in tbuttons:
            toolbar.insert(tbutton, -1)
        sep = gtk.SeparatorToolItem()
        sep.set_expand(True)
        sep.set_draw(False)
        toolbar.insert(sep, -1)
        if self.main:
            name = "Quit"
            tip = "Close Application"
        else:
            name = "Close"
            tip = "Close Window"
        button = self.make_toolbutton(gtk.STOCK_CLOSE, name, self._quit_clicked, tip=tip)
        toolbar.insert(button, -1)
        self.toolbar = toolbar
        vbox.pack_start(toolbar, False, False, 0)

        # Subclass returns the main body
        body = self.get_body()
        vbox.pack_start(body, True, True, 0)

        # Subclass provides extra stuff in bottom hbox
        extras = self.get_extras()
        if extras:
            vbox.pack_end(extras, False, False, 0)

        self.connect("destroy", self._destroying)
        self.connect("delete_event", self.should_live)

    def _quit_clicked(self, button, data=None):
        if not self.should_live():
            self.destroy()

    def _destroying(self, gtkobj):
        try:
            settings = self.save_settings()
            self.settings.set_value("settings_version", GDialog.settings_version)
            self.settings.set_value("dialogs", settings)
            self.settings.write()
        finally:
            if self.main:
                gtk.main_quit()

    def _load_settings(self):
        settings = {}
        version = self.settings.get_value("settings_version", None)
        if version == GDialog.settings_version:
            settings = self.settings.get_value("dialogs", {})
        self.load_settings(settings)

    def _hg_call_wrapper(self, title, command, showoutput=True):
        """Run the specified command and display any resulting aborts, messages, 
        and errors 
        """
        textout = ""
        saved = sys.stderr
        errors = StringIO.StringIO()
        try:
            sys.stderr = errors
            self.ui.pushbuffer()
            try:
                command()
            except util.Abort, inst:
                Prompt(title + " Aborted", str(inst), self).run()
                return False, ""
        finally:
            sys.stderr = saved
            textout = self.ui.popbuffer()
            prompttext = ""
            if showoutput:
                prompttext = textout + "\n"
            prompttext += errors.getvalue()
            errors.close()
            if len(prompttext) > 1:
                Prompt(title + " Messages and Errors", prompttext, self).run()

        return True, textout

    def _diff_file(self, stat, file):
        def dodiff():
            extdiff.dodiff(self.ui, self.repo, self.diffcmd, [self.diffopts], [self.repo.wjoin(file)], self.opts)

        if self.diffcmd == "diff":
            Prompt("No visual diff configured", "Please select a visual diff application.", self).run()
            dlg = ConfigDialog(self.repo.root, False)
            dlg.show_all()
            dlg.focus_field("tortoisehg.vdiff")
            dlg.run()
            dlg.hide()
            self.ui = ui.ui()
            self._parse_config()
            return
        thread = threading.Thread(target=dodiff, name="diff:" + file)
        thread.setDaemon(True)
        thread.start()

    def _view_file(self, stat, file, force_left=False):
        import atexit

        def cleanup():
            shutil.rmtree(self.tmproot)

        if not self.tmproot:
            self.tmproot = tempfile.mkdtemp(prefix="gtools.")
            atexit.register(cleanup)

        def snapshot_node(ui, repo, files, node, tmproot):
            """
            snapshot files as of some revision
            (adapted from Extdiff extension)
            """
            mf = repo.changectx(node).manifest()
            dirname = os.path.basename(repo.root)
            if dirname == "":
                dirname = "root"
            dirname = "%s.%s" % (dirname, short(node))
            base = os.path.join(tmproot, dirname)
            try:
                os.mkdir(base)
            except:
                pass
            ui.note(_("making snapshot of %d files from rev %s\n") % (len(files), short(node)))
            for fn in files:
                if not fn in mf:
                    # skipping new file after a merge ?
                    continue
                wfn = util.pconvert(fn)
                ui.note("  %s\n" % wfn)
                dest = os.path.join(base, wfn)
                destdir = os.path.dirname(dest)
                if not os.path.isdir(destdir):
                    os.makedirs(destdir)
                data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn]))
                open(dest, "wb").write(data)
            return dirname

        def doedit():
            pathroot = self.repo.root
            copynode = None
            # if we aren't looking at the wc, copy the node...
            if stat in "R!" or force_left:
                copynode = self._node1
            elif self._node2:
                copynode = self._node2

            if copynode:
                copydir = snapshot_node(self.ui, self.repo, [util.pconvert(file)], copynode, self.tmproot)
                pathroot = os.path.join(self.tmproot, copydir)

            file_path = os.path.join(pathroot, file)
            util.system(
                '%s "%s"' % (editor, file_path),
                environ={"HGUSER": self.ui.username()},
                onerr=util.Abort,
                errprefix=_("edit failed"),
            )

        editor = (
            self.ui.config("tortoisehg", "editor")
            or self.ui.config("gtools", "editor")
            or os.environ.get("HGEDITOR")
            or self.ui.config("ui", "editor")
            or os.environ.get("EDITOR", "vi")
        )
        if os.path.basename(editor) in ("vi", "vim", "hgeditor"):
            Prompt("No visual editor configured", "Please configure a visual editor.", self).run()
            dlg = ConfigDialog(self.repo.root, False)
            dlg.show_all()
            dlg.focus_field("tortoisehg.editor")
            dlg.run()
            dlg.hide()
            self.ui = ui.ui()
            self._parse_config()
            return

        file = util.localpath(file)
        thread = threading.Thread(target=doedit, name="edit:" + file)
        thread.setDaemon(True)
        thread.start()