|
# thgimport.py - TortoiseHg's dialog for (q)importing patches
#
# Copyright 2009 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
import gtk
import gobject
import tempfile
from tortoisehg.util.i18n import _
from tortoisehg.hgtk import gtklib, gdialog, cslist
COL_NAME = 0
COL_LABEL = 1
DEST_REPO = 'repo'
DEST_MQ = 'mq'
class ImportDialog(gdialog.GDialog):
""" Dialog to import patches """
def __init__(self, repo=None, dest=DEST_REPO, sources=None):
gdialog.GDialog.__init__(self, resizable=True)
self.done = False
self.mqloaded = hasattr(self.repo, 'mq')
self.clipboard = gtk.Clipboard()
self.tempfiles = []
self.initdest = dest
if not self.mqloaded and dest == DEST_MQ:
self.initdest = DEST_REPO
self.initsrc = sources
# persistent settings
self.recent = self.settings.mrul('src_paths')
### Start of Overriding Section ###
def get_title(self, reponame):
return _('Import - %s') % reponame
def get_icon(self):
return 'menuimport.ico'
def get_defsize(self):
return (500, 390)
def get_setting_name(self):
return 'import'
def get_body(self, vbox):
# layout table
self.table = table = gtklib.LayoutTable()
vbox.pack_start(table, True, True, 2)
## source path combo & browse buttons
self.src_list = gtk.ListStore(str)
self.src_combo = gtk.ComboBoxEntry(self.src_list, 0)
self.files_btn = gtk.Button(_('Browse...'))
## set given sources (but preview later)
if self.initsrc:
src = os.pathsep.join(self.initsrc)
self.src_combo.child.set_text(src)
## other sources
menubtn = gtk.ToggleButton()
menubtn.set_focus_on_click(False)
menubtn.add(gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE))
m = gtklib.MenuBuilder()
m.append(_('Browse Directory...'), self.dir_clicked,
gtk.STOCK_DIRECTORY)
m.append(_('Import from Clipboard'), self.clip_clicked,
gtk.STOCK_PASTE)
self.menu = m.build()
table.add_row(_('Source:'), self.src_combo, 1,
self.files_btn, menubtn, expand=0)
self.p0check = gtk.CheckButton(_('Do not strip paths '
'(-p0), required for SVN patches'))
table.add_row(None, self.p0check, 1, expand=0)
## add MRU paths to source combo
for path in self.recent:
self.src_list.append([path])
# copy form thgstrip.py
def createlabel():
label = gtk.Label()
label.set_alignment(0, 0.5)
label.set_size_request(-1, 25)
label.size_request()
return label
## info label
self.infolbl = createlabel()
self.infobox = gtk.HBox()
self.infobox.pack_start(self.infolbl, False, False)
table.add_row(_('Preview:'), self.infobox, padding=False)
## dest combo
self.dest_model = gtk.ListStore(gobject.TYPE_STRING, # dest name
gobject.TYPE_STRING) # dest label
for row in {DEST_REPO: _('Repository'),
DEST_MQ: _('Patch Queue')}.items():
self.dest_model.append(row)
self.dest_combo = gtk.ComboBox(self.dest_model)
cell = gtk.CellRendererText()
self.dest_combo.pack_start(cell, True)
self.dest_combo.add_attribute(cell, 'text', COL_LABEL)
for row in self.dest_model:
name, label = self.dest_model[row.path]
if name == self.initdest:
self.dest_combo.set_active_iter(row.iter)
break
## patch preview
self.cslist = cslist.ChangesetList()
table.add_row(None, self.cslist, padding=False,
yopt=gtk.FILL|gtk.EXPAND)
self.cslist.set_dnd_enable(True)
self.cslist.set_checkbox_enable(True)
# signal handlers
self.files_btn.connect('clicked', self.files_clicked)
self.cslist.connect('list-updated', self.list_updated)
self.cslist.connect('files-dropped', self.files_dropped)
self.src_combo.connect('changed', lambda e: self.preview(queue=True))
menubtn.connect('button-press-event', self.extra_pressed)
popdown = lambda m: menubtn.set_active(False)
self.menu.connect('selection-done', popdown)
self.menu.connect('cancel', popdown)
# hook thg-close/thg-exit
def hook(dialog, orig):
self.unlink_all_tempfiles()
return orig(dialog)
self.connect('thg-close', hook, gtklib.thgclose)
self.connect('thg-exit', hook, gtklib.thgexit)
# prepare to show
self.cslist.clear()
def get_extras(self, vbox):
# dest combo
if self.mqloaded:
self.dest_combo.show_all()
self.dest_combo.hide()
self.infobox.pack_start(self.dest_combo, False, False, 6)
# start preview
if self.initsrc:
self.preview()
def get_buttons(self):
return [('import', _('Import'), gtk.RESPONSE_OK),
('close', gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)]
def get_default_button(self):
return 'import'
def get_action_map(self):
return {gtk.RESPONSE_OK: self.doimport}
def switch_to(self, normal, working, cmd):
self.table.set_sensitive(normal)
self.buttons['import'].set_property('visible', normal)
self.buttons['close'].set_property('visible', normal)
if normal:
self.buttons['close'].grab_focus()
def command_done(self, returncode, useraborted, *args):
self.done = True
self.add_to_mru()
if returncode == 0:
self.cmd.set_result(_('Imported successfully'), style='ok')
elif useraborted:
self.cmd.set_result(_('Canceled importing'), style='error')
else:
self.cmd.set_result(_('Failed to import'), style='error')
def before_close(self):
if len(self.cslist.get_items()) != 0 and not self.done:
ret = gdialog.Confirm(_('Confirm Close'), [], self,
_('Do you want to close?')).run()
if ret != gtk.RESPONSE_YES:
return False # don't close
self.unlink_all_tempfiles()
return True
### End of Overriding Section ###
def files_clicked(self, button):
initdir = self.get_initial_dir()
result = gtklib.NativeSaveFileDialogWrapper(title=_('Select Patches'),
initial=initdir, open=True, multi=True).run()
if result and result != initdir:
if not isinstance(result, basestring):
result = os.pathsep.join(result)
self.src_combo.child.set_text(result)
self.preview()
def dir_clicked(self, menuitem):
initdir = self.get_initial_dir()
result = gtklib.NativeFolderSelectDialog(
title=_('Select Directory contains patches:'),
initial=initdir).run()
if result and result != initdir:
self.src_combo.child.set_text(result)
self.preview()
def clip_clicked(self, menuitem):
text = self.clipboard.wait_for_text()
if not text:
return
filepath = self.save_as_tempfile(text)
cur_text = self.src_combo.child.get_text()
self.src_combo.child.set_text(cur_text + os.pathsep + filepath)
self.preview()
def extra_pressed(self, button, event):
if not button.get_active():
button.set_active(True)
self.menu.show_all()
def pos(*args):
x, y = button.window.get_origin()
r = button.allocation
return (x + r.x, y + r.y + r.height, True)
self.menu.popup(None, None, pos, event.button, event.time)
def list_updated(self, cslist, total, sel, *args):
self.update_status(sel)
def files_dropped(self, cslist, files, *args):
src = self.src_combo.child.get_text()
if src:
files = [src] + files
self.src_combo.child.set_text(os.pathsep.join(files))
self.preview()
def get_initial_dir(self):
src = self.src_combo.child.get_text()
if src and os.path.exists(src):
if os.path.isdir(src):
return src
parent = os.path.dirname(src)
if parent and os.path.exists(parent):
return parent
return None
def add_to_mru(self):
dirs = self.get_dirpaths()
for dir in dirs:
if dir.find(tempfile.gettempdir()) != -1:
continue
self.recent.add(dir)
self.src_list.append([dir])
self.settings.write()
def save_as_tempfile(self, text):
fd, filepath = tempfile.mkstemp(prefix='thg-patch-')
os.write(fd, text)
os.close(fd)
self.tempfiles.append(filepath)
return filepath
def unlink_all_tempfiles(self):
for path in self.tempfiles:
os.unlink(path)
def update_status(self, count):
if count:
inner = gtklib.markup(_('%s patches') % count, weight='bold')
if self.mqloaded:
info = _('%s will be imported to the') % inner
else:
info = _('%s will be imported to the repository') % inner
else:
info = gtklib.markup(_('Nothing to import'),
weight='bold', color=gtklib.DRED)
self.infolbl.set_markup(info)
if self.mqloaded:
self.dest_combo.set_property('visible', bool(count))
self.buttons['import'].set_sensitive(bool(count))
def get_filepaths(self):
src = self.src_combo.child.get_text()
if not src:
return []
files = []
for path in src.split(os.pathsep):
path = path.strip('\r\n\t ')
if not os.path.exists(path) or path in files:
continue
if os.path.isdir(path):
entries = os.listdir(path)
for entry in entries:
file = os.path.join(path, entry)
if os.path.isfile(file):
files.append(file)
elif os.path.isfile(path):
files.append(path)
return files
def get_dirpaths(self):
dirs = []
files = self.get_filepaths()
for file in files:
dir = os.path.dirname(file)
if os.path.isdir(dir) and dir not in dirs:
dirs.append(dir)
return dirs
def get_dest(self):
iter = self.dest_combo.get_active_iter()
return self.dest_model.get(iter, COL_NAME)[0]
def preview(self, queue=False):
files = self.get_filepaths()
if files:
self.cslist.update(files, self.repo, queue=queue)
else:
self.cslist.clear()
def doimport(self):
items = self.cslist.get_items(sel=True)
files = [file for file, sel in items if sel]
if not files:
return
dest = self.get_dest()
if DEST_REPO == dest:
cmd = 'import'
elif DEST_MQ == dest:
cmd = 'qimport'
else:
raise _('unexpected destination name: %s') % dest
# prepare command line
cmdline = ['hg', cmd, '--verbose', '--']
cmdline.extend(files)
if self.p0check.get_active():
cmdline.insert(2, '-p0')
# start importing
self.execute_command(cmdline)
def run(ui, *pats, **opts):
dest = opts.get('mq', False) and DEST_MQ or DEST_REPO
sources = []
for item in pats:
if os.path.exists(item):
sources.append(os.path.abspath(item))
return ImportDialog(dest=dest, sources=sources)
|
Loading...