|
# hgignore.py - TortoiseHg's dialog for editing .hgignore
#
# Copyright 2008 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 os
import gtk
import gobject
import re
from mercurial import hg, ui, match, util
from tortoisehg.util.i18n import _
from tortoisehg.util import shlib, hglib, paths
from tortoisehg.hgtk import gtklib, gdialog
class HgIgnoreDialog(gtk.Window):
'Edit a reposiory .hgignore file'
def __init__(self, fileglob='', *pats):
'Initialize the Dialog'
gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
gtklib.set_tortoise_icon(self, 'ignore.ico')
gtklib.set_tortoise_keys(self)
self.root = paths.find_root()
base = os.path.basename(self.root)
self.set_title(_('Ignore filter for ') + hglib.toutf(base))
self.set_default_size(630, 400)
self.notify_func = None
mainvbox = gtk.VBox()
hbox = gtk.HBox()
lbl = gtk.Label(_('Glob:'))
lbl.set_property('width-chars', 9)
lbl.set_alignment(1.0, 0.5)
hbox.pack_start(lbl, False, False, 4)
glob_entry = gtk.Entry()
hbox.pack_start(glob_entry, True, True, 4)
glob_button = gtk.Button(_('Add'))
hbox.pack_start(glob_button, False, False, 4)
glob_button.connect('clicked', self.add_glob, glob_entry)
glob_entry.connect('activate', self.add_glob, glob_entry)
glob_entry.set_text(hglib.toutf(fileglob))
self.glob_entry = glob_entry
mainvbox.pack_start(hbox, False, False)
hbox = gtk.HBox()
lbl = gtk.Label(_('Regexp:'))
lbl.set_property('width-chars', 9)
lbl.set_alignment(1.0, 0.5)
hbox.pack_start(lbl, False, False, 4)
regexp_entry = gtk.Entry()
hbox.pack_start(regexp_entry, True, True, 4)
regexp_button = gtk.Button(_('Add'))
hbox.pack_start(regexp_button, False, False, 4)
regexp_button.connect('clicked', self.add_regexp, regexp_entry)
regexp_entry.connect('activate', self.add_regexp, regexp_entry)
mainvbox.pack_start(hbox, False, False)
mainvbox.set_border_width(2)
try: repo = hg.repository(ui.ui(), path=self.root)
except: self.destroy()
ignorefiles = [repo.wjoin('.hgignore')]
for name, value in repo.ui.configitems('ui'):
if name == 'ignore' or name.startswith('ignore.'):
ignorefiles.append(os.path.expanduser(value))
if len(ignorefiles) > 1:
combo = gtk.combo_box_new_text()
for f in ignorefiles:
combo.append_text(hglib.toutf(f))
combo.set_active(0)
combo.connect('changed', self.fileselect)
mainvbox.pack_start(combo, False, False, 4)
self.ignorefile = ignorefiles[0]
hbox = gtk.HBox()
frame = gtk.Frame(_('Filters'))
hbox.pack_start(frame, True, True, 4)
pattree = gtk.TreeView()
pattree.set_enable_search(False)
pattree.set_reorderable(False)
sel = pattree.get_selection()
sel.set_mode(gtk.SELECTION_SINGLE)
col = gtk.TreeViewColumn(_('Patterns'), gtk.CellRendererText(), text=0)
pattree.append_column(col)
pattree.set_headers_visible(False)
self.pattree = pattree
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolledwindow.set_border_width(4)
scrolledwindow.add(pattree)
vbox = gtk.VBox()
vbox.pack_start(scrolledwindow, True, True, 2)
bhbox = gtk.HBox()
remove = gtk.Button(_('Remove Selected'))
remove.connect('pressed', self.remove_pressed, sel)
remove.set_sensitive(False)
bhbox.pack_start(remove, False, False, 2)
vbox.pack_start(bhbox, False, False, 2)
vbox.set_border_width(2)
frame.add(vbox)
frame = gtk.Frame(_('Unknown Files'))
hbox.pack_start(frame, True, True, 4)
unknowntree = gtk.TreeView()
unknowntree.set_search_equal_func(self.unknown_search)
col = gtk.TreeViewColumn(_('Files'), gtk.CellRendererText(), text=0)
unknowntree.append_column(col)
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolledwindow.set_border_width(4)
scrolledwindow.add(unknowntree)
model = gtk.ListStore(str, str)
unknowntree.set_model(model)
unknowntree.set_headers_visible(False)
self.unkmodel = model
vbox = gtk.VBox()
vbox.pack_start(scrolledwindow, True, True, 2)
bhbox = gtk.HBox()
refresh = gtk.Button(_('Refresh'))
refresh.connect('pressed', self.refresh_clicked, sel)
self.connect('thg-refresh', self.thgrefresh)
bhbox.pack_start(refresh, False, False, 2)
vbox.pack_start(bhbox, False, False, 2)
vbox.set_border_width(2)
frame.add(vbox)
mainvbox.pack_start(hbox, True, True)
self.add(mainvbox)
glob_entry.grab_focus()
pattree.get_selection().connect('changed', self.pattree_rowchanged, remove)
unknowntree.get_selection().connect('changed', self.unknown_rowchanged)
gobject.idle_add(self.refresh)
def fileselect(self, combo):
'select another ignore file'
self.ignorefile = hglib.fromutf(combo.get_active_text())
self.refresh()
def unknown_search(self, model, column, key, iter):
'case insensitive filename search'
key = key.lower()
if key in model.get_value(iter, 0).lower():
return False
return True
def remove_pressed(self, widget, selection):
model, rows = selection.get_selected_rows()
del model[rows[0]]
del self.ignorelines[rows[0][0]]
self.write_ignore_lines()
self.refresh()
def pattree_rowchanged(self, sel, remove):
model, ppaths = sel.get_selected()
sensitive = ppaths and True or False
remove.set_sensitive(sensitive)
def unknown_rowchanged(self, sel):
model, upaths = sel.get_selected()
if not upaths:
return
self.glob_entry.set_text(model[upaths][0])
def add_glob(self, widget, glob_entry):
newglob = hglib.fromutf(glob_entry.get_text())
if newglob == '':
return
newglob = 'glob:' + newglob
try:
match.match(self.root, '', [], [newglob])
except util.Abort, inst:
gdialog.Prompt(_('Invalid glob expression'), str(inst),
self).run()
return
self.ignorelines.append(newglob)
self.write_ignore_lines()
glob_entry.set_text('')
self.refresh()
def add_regexp(self, widget, regexp_entry):
newregexp = hglib.fromutf(regexp_entry.get_text())
if newregexp == '':
return
try:
match.match(self.root, '', [], ['relre:' + newregexp])
re.compile(newregexp)
except (util.Abort, re.error), inst:
gdialog.Prompt(_('Invalid regexp expression'), str(inst),
self).run()
return
self.ignorelines.append('relre:' + newregexp)
self.write_ignore_lines()
regexp_entry.set_text('')
self.refresh()
def thgrefresh(self, window):
self.refresh()
def refresh_clicked(self, togglebutton, data=None):
self.refresh()
def set_notify_func(self, func):
self.notify_func = func
def refresh(self):
try: repo = hg.repository(ui.ui(), path=self.root)
except: self.destroy()
matcher = match.always(repo.root, repo.root)
changes = repo.dirstate.status(matcher, ignored=False, clean=False,
unknown=True)
(lookup, modified, added, removed,
deleted, unknown, ignored, clean) = changes
self.unkmodel.clear()
for u in unknown:
self.unkmodel.append([hglib.toutf(u), u])
try:
l = open(self.ignorefile, 'rb').readlines()
self.doseoln = l[0].endswith('\r\n')
except (IOError, ValueError, IndexError):
self.doseoln = os.name == 'nt'
l = []
model = gtk.ListStore(str)
self.ignorelines = []
for line in l:
model.append([hglib.toutf(line.strip())])
self.ignorelines.append(line.strip())
self.pattree.set_model(model)
self.repo = repo
def write_ignore_lines(self):
if self.doseoln:
out = [line + '\r\n' for line in self.ignorelines]
else:
out = [line + '\n' for line in self.ignorelines]
try:
f = open(self.ignorefile, 'wb')
f.writelines(out)
f.close()
except IOError:
pass
shlib.shell_notify([self.ignorefile])
if self.notify_func:
self.notify_func()
def run(_ui, *pats, **opts):
if pats and pats[0].endswith('.hgignore'):
pats = []
return HgIgnoreDialog(*pats)
|
Loading...