Source

thg / hggtk / clone.py

Full commit
# clone.py - Clone dialog for TortoiseHg
#
# Copyright 2007 TK Soh <teekaysoh@gmail.com>
# Copyright 2007 Steve Borho <steve@borho.org>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.

import gtk
import os
import pango
import traceback

from mercurial import ui, util
from thgutil.i18n import _
from thgutil import hglib, shlib, settings
from hggtk import gdialog, gtklib, hgcmd

class CloneDialog(gtk.Window):
    """ Dialog to clone a Mercurial repo """
    def __init__(self, repos=[]):
        """ Initialize the Dialog """
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        gtklib.set_tortoise_icon(self, 'menuclone.ico')
        gtklib.set_tortoise_keys(self)
        self.set_default_size(520, 180)
        self.set_title(_('TortoiseHg Clone'))

        self.ui = ui.ui()
        self.clonesettings = settings.Settings('clone')
        self.recentsrc = self.clonesettings.mrul('src_paths')
        self.recentdest = self.clonesettings.mrul('dest_paths')

        srcpath = hglib.toutf(os.getcwd())
        destpath = srcpath
        if len(repos) > 1:
            srcpath = repos[0]
            destpath = repos[1]
        elif len(repos):
            srcpath = repos[0]

        ewidth = 20

        vbox = gtk.VBox()
        self.add(vbox)

        # clone source
        srcbox = gtk.HBox()
        lbl = gtk.Label(_('Source Path:'))
        lbl.set_property('width-chars', ewidth)
        lbl.set_alignment(0, 0.5)

        # create drop-down list for source paths
        self.srclist = gtk.ListStore(str)
        srccombo = gtk.ComboBoxEntry(self.srclist, 0)
        self.srcentry = srccombo.get_child()
        self.srcentry.set_text(srcpath)
        self.srcentry.set_position(-1)

        # replace the drop-down widget so we can modify it's properties
        srccombo.clear()
        cell = gtk.CellRendererText()
        cell.set_property('ellipsize', pango.ELLIPSIZE_MIDDLE)
        srccombo.pack_start(cell)
        srccombo.add_attribute(cell, 'text', 0)

        srcbrowse = gtk.Button(_('Browse...'))
        srcbrowse.connect('clicked', self.source_browse_clicked)
        srcbox.pack_start(lbl, False, False)
        srcbox.pack_start(srccombo, True, True)
        srcbox.pack_end(srcbrowse, False, False, 5)
        vbox.pack_start(srcbox, False, False, 2)

        # add pre-defined src paths to pull-down list
        sync_src = settings.Settings('synch').mrul('src_paths')
        sympaths = [x[1] for x in self.ui.configitems('paths')]
        recent = [x for x in self.recentsrc]
        syncsrc = [x for x in sync_src]
        paths = list(set(sympaths + recent + syncsrc))
        paths.sort()
        for p in paths:
            self.srclist.append([p])

        # clone destination
        destbox = gtk.HBox()
        lbl = gtk.Label(_('Destination Path:'))
        lbl.set_property('width-chars', ewidth)
        lbl.set_alignment(0, 0.5)
        self.destlist = gtk.ListStore(str)
        destcombo = gtk.ComboBoxEntry(self.destlist, 0)
        self.destentry = destcombo.get_child()
        self.destentry.set_text(destpath)
        self.destentry.set_position(-1)
        self.destentry.connect('activate', self.clone_clicked)

        # replace the drop-down widget so we can modify it's properties
        destcombo.clear()
        cell = gtk.CellRendererText()
        cell.set_property('ellipsize', pango.ELLIPSIZE_MIDDLE)
        destcombo.pack_start(cell)
        destcombo.add_attribute(cell, 'text', 0)

        srcbrowse = gtk.Button(_('Browse...'))
        srcbrowse.connect('clicked', self.dest_browse_clicked)
        destbox.pack_start(lbl, False, False)
        destbox.pack_start(destcombo, True, True)
        destbox.pack_end(srcbrowse, False, False, 5)
        vbox.pack_start(destbox, False, False, 2)

        # add most-recent dest paths to pull-down list
        paths = list(self.recentdest)
        paths.sort()
        for p in paths:
            self.destlist.append([p])

        # revision input
        revbox = gtk.HBox()
        lbl = gtk.Label(_('Clone To Revision:'))
        lbl.set_property('width-chars', ewidth)
        lbl.set_alignment(0, 0.5)
        self.reventry = gtk.Entry()
        self.reventry.set_text("")
        revbox.pack_start(lbl, False, False)
        revbox.pack_start(self.reventry, False, False)
        vbox.pack_start(revbox, False, False, 2)

        # options
        option_box = gtk.VBox()
        self.optupdate = gtk.CheckButton(_('do not update the new working directory'))
        self.optpull = gtk.CheckButton(_('use pull protocol to copy metadata'))
        self.optuncomp = gtk.CheckButton(_('use uncompressed transfer'))
        self.optproxy = gtk.CheckButton(_('use proxy server'))
        option_box.pack_start(self.optupdate, False, False)
        option_box.pack_start(self.optpull, False, False)
        option_box.pack_start(self.optuncomp, False, False)
        option_box.pack_start(self.optproxy, False, False)
        vbox.pack_start(option_box, False, False, 15)

        if self.ui.config('http_proxy', 'host'):
            self.optproxy.set_active(True)
        else:
            self.optproxy.set_sensitive(False)

        # remote cmd
        lbl = gtk.Label(_('Remote Cmd:'))
        lbl.set_alignment(0, 0.5)
        self.remotecmdentry = gtk.Entry()
        vbox.pack_start(self.remotecmdentry, False, False, 1)
        vbox.pack_start(lbl, False, False, 1)

        accelgroup = gtk.AccelGroup()
        self.add_accel_group(accelgroup)
        mod = gtklib.get_thg_modifier()

        hbbox = gtk.HButtonBox()
        hbbox.set_layout(gtk.BUTTONBOX_END)
        vbox.pack_start(hbbox, False, False, 2)

        close = gtk.Button(_('Cancel'))
        close.connect('clicked', lambda x: self.destroy())
        key, modifier = gtk.accelerator_parse('Escape')
        close.add_accelerator('clicked', accelgroup, key, 0,
                gtk.ACCEL_VISIBLE)
        self.close_button = close
        hbbox.add(close)

        clone = gtk.Button(_('Clone'))
        key, modifier = gtk.accelerator_parse(mod+'Return')
        clone.add_accelerator('clicked', accelgroup, key, modifier,
                gtk.ACCEL_VISIBLE)
        clone.connect('clicked', self.clone_clicked)
        hbbox.add(clone)

        destcombo.grab_focus()

    def dest_browse_clicked(self, button):
        'select folder as clone destination'
        response = gtklib.NativeFolderSelectDialog(
                          initial=self.destentry.get_text(),
                          title=_('Select Destination Folder')).run()
        if response:
            self.destentry.set_text(response)

    def source_browse_clicked(self, button):
        'select source folder to clone'
        response = gtklib.NativeFolderSelectDialog(
                          initial=self.destentry.get_text(),
                          title=_('Select Source Folder')).run()
        if response:
            self.srcentry.set_text(response)

    def add_src_to_recent(self, src):
        if os.path.exists(src):
            src = os.path.abspath(src)

        # save path to recent list in history
        self.recentsrc.add(src)
        self.clonesettings.write()

        # update drop-down list
        self.srclist.clear()
        sympaths = [x[1] for x in self.ui.configitems('paths')]
        paths = list(set(sympaths + [x for x in self.recentsrc]))
        paths.sort()
        for p in paths:
            self.srclist.append([p])

    def add_dest_to_recent(self, dest):
        if not dest:
            return
        if os.path.exists(dest):
            dest = os.path.abspath(dest)

        # save path to recent list in history
        self.recentdest.add(dest)
        self.clonesettings.write()

        # update drop down list
        paths = list(self.recentdest)
        paths.sort()
        self.destlist.clear()
        for p in paths:
            self.destlist.append([p])

    def clone_clicked(self, toolbutton, data=None):
        # gather input data
        src = self.srcentry.get_text()
        dest = self.destentry.get_text() or os.path.basename(src)
        remotecmd = self.remotecmdentry.get_text()
        rev = self.reventry.get_text()

        # verify input
        if src == '':
            gdialog.Prompt(_('Source path is empty'),
                    _('Please enter a valid source path'), self).run()
            self.srcentry.grab_focus()
            return False

        if src == dest:
            gdialog.Prompt(_('Source and dest are the same'),
                    _('Please specify a different destination'), self).run()
            self.destentry.grab_focus()
            return False

        if dest == os.getcwd():
            if os.listdir(dest):
                # cur dir has files, specify no dest, let hg take
                # basename
                dest = None
            else:
                dest = '.'
        else:
            abs = os.path.abspath(dest)
            dirabs = os.path.dirname(abs)
            if dirabs == src:
                dest = os.path.join(os.path.dirname(dirabs), dest)

        # start cloning
        try:
            cmdline = ['hg', 'clone']
            if self.optupdate.get_active():
                cmdline.append('--noupdate')
            if self.optuncomp.get_active():
                cmdline.append('--uncompressed')
            if self.optpull.get_active():
                cmdline.append('--pull')
            if self.ui.config('http_proxy', 'host'):
                if not self.optproxy.get_active():
                    cmdline += ['--config', 'http_proxy.host=']
            if remotecmd:
                cmdline.append('--remotecmd')
                cmdline.append(hglib.fromutf(remotecmd))
            if rev:
                cmdline.append('--rev')
                cmdline.append(rev)

            cmdline.append('--verbose')
            cmdline.append(hglib.fromutf(src))
            if dest:
                cmdline.append(hglib.fromutf(dest))

            dlg = hgcmd.CmdDialog(cmdline)
            dlg.run()
            dlg.hide()
        except util.Abort, inst:
            gdialog.Prompt(_('Clone aborted'), str(inst), self).run()
            return False
        except:
            gdialog.Prompt(_('Clone error'),
                    traceback.format_exc(), self).run()
            return False

        self.add_src_to_recent(src)
        self.add_dest_to_recent(dest)
        self.close_button.grab_focus()

        if dlg.return_code() == 0:
            shlib.shell_notify([dest])

def run(_ui, *pats, **opts):
    return CloneDialog(pats)