#!/usr/local/bin/python3.8
"""
 Copyright (c) 2011-2014 Kuan-Chung Chiu <buganini@gmail.com>

 Permission to use, copy, modify, and distribute this software for any
 purpose with or without fee is hereby granted, provided that the above
 copyright notice and this permission notice appear in all copies.

 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

"""

import os
import sys
from bsdconv import Bsdconv
from ctypes import *
from gi.repository import Gtk, Gdk

taglib=CDLL('gbsdconv_taglib.so')

taglib.taglib_file_new.argtypes=[c_char_p]
taglib.taglib_file_new.restype=c_void_p

taglib.taglib_file_is_valid.argtypes=[c_void_p]
taglib.taglib_file_is_valid.restype=c_int

taglib.taglib_file_save.argtypes=[c_void_p]
taglib.taglib_file_save.restype=c_int

taglib.taglib_file_free.argtypes=[c_void_p]

taglib.taglib_mpeg_file.argtypes=[c_void_p]
taglib.taglib_mpeg_file.restype=c_void_p

taglib.taglib_mpeg_file_save3.argtypes=[c_void_p, c_int, c_bool, c_int]
taglib.taglib_mpeg_file_save3.restype=c_bool

taglib.taglib_mpeg_file_strip.argtypes=[c_void_p, c_int]
taglib.taglib_mpeg_file_strip.restype=c_bool

taglib.taglib_file_tag.argtypes=[c_void_p]
taglib.taglib_file_tag.restype=c_void_p

taglib.taglib_tag_title.argtypes=[c_void_p]
taglib.taglib_tag_title.restype=c_char_p

taglib.taglib_tag_artist.argtypes=[c_void_p]
taglib.taglib_tag_artist.restype=c_char_p

taglib.taglib_tag_album.argtypes=[c_void_p]
taglib.taglib_tag_album.restype=c_char_p

taglib.taglib_tag_comment.argtypes=[c_void_p]
taglib.taglib_tag_comment.restype=c_char_p

taglib.taglib_tag_genre.argtypes=[c_void_p]
taglib.taglib_tag_genre.restype=c_char_p

taglib.taglib_tag_year.argtypes=[c_void_p]
taglib.taglib_tag_year.restype=c_uint

taglib.taglib_tag_track.argtypes=[c_void_p]
taglib.taglib_tag_track.restype=c_uint

taglib.taglib_tag_set_title.argtypes=[c_void_p, c_char_p]
taglib.taglib_tag_set_artist.argtypes=[c_void_p, c_char_p]
taglib.taglib_tag_set_album.argtypes=[c_void_p, c_char_p]
taglib.taglib_tag_set_comment.argtypes=[c_void_p, c_char_p]
taglib.taglib_tag_set_genre.argtypes=[c_void_p, c_char_p]


items = Gtk.TreeStore(int, str, str, str, str, str, str, str, str, str, str, str, str, str, str, str, str)
attr_map={
	'selected':	0,	#0=no, 1=yes, -1=not_in_zone
	'name0':	1,
	'name2':	2,	#used by naming operation
	'name1':	3,	#name2 or name0 if there are duplication or collision
	'uname0':	4,	#utf8er(name0)
	'uname1':	5,	#utf8er(name1)
	'lname0':	6,	#utf8er(name0 + target0)
	'lname1':	7,	#utf8er(name1 + target1)
	'uname2':	8,	#utf8er(name2) (unused)
	'error_counter':9,
	'collision':	10,
	'duplication':	11,
	'target0':	12,	#symlink target0
	'target1':	13,	#symlink target1
	'naming_info':	14,
	'content_info':	15,
	'metatag_info':	16,
}

class Bsdconvs(object):
	def __init__(self, conversion):
		cs=conversion.upper().strip(',:|;').split(';')
		self.failed=False
		self.conversions=[]
		self.evaluaters=[]
		self.trainer=None
		self.cache={}
		self.lastc=0
		self.cinfo=None
		self.tempfile=None
		done={}
		train=builder.get_object('chk_path_train').get_active()
		if train:
			self.tempfile=mktemp('/tmp/gbsdconv.score.XXXXXX')
			os.unlink(self.tempfile[1])
			self.trainer=Bsdconv('LOCALE:HALF:LOWER:ZH-FUZZY-TW:SCORE-TRAIN:NULL')
			self.trainer.ctl(CTL_ATTACH_SCORE, self.tempfile[0], 0)
		for c in cs:
			if c in done:
				continue
			done[c]=1
			c1=c.replace(':SCORE:',':')
			c2=c.replace(':SCORE:',':HALF:LOWER:ZH-FUZZY-TW:SCORE:COUNT:')
			h1=Bsdconv(c1)
			h2=Bsdconv(c2)
			if not h1 or not h2:
				self.errorstr=Bsdconv.error()
				self.failed=True
				self.conversions=[Bsdconv('BYTE:BYTE')]
			else:
				if train:
					h2.ctl(CTL_ATTACH_SCORE, self.tempfile[0], 0)
				self.conversions.append(h1)
				self.evaluaters.append(h2)

	def __bool__(self):
		return not self.failed

	def __nonzero__(self):
		return self.__bool__()

	def __str__(self):
		return ';'.join([x.split('"')[1] for x in [str(x) for x in self.conversions]])

	def weighted_score(self, i):
		ierr=i.get("IERR", 0)
		oerr=i.get("OERR", 0)
		score=i.get("SCORE", 0)
		count=i.get("COUNT", 0)
		if count==0:
			return 0
		return float(score - (ierr + oerr) * 10) / float(count)

	def score_train(self, s):
		if self.trainer:
			self.trainer.testconv(s)

	def score_clear(self):
		if self.trainer:
			self.tempfile=mktemp("/tmp/gbsdconv.score.XXXXXX")
			os.unlink(self.tempfile[1])
			self.trainer.ctl(CTL_ATTACH_SCORE, self.tempfile[0], 0)
			for c in self.evaluaters:
				c.ctl(CTL_ATTACH_SCORE, self.tempfile[0], 0)

	def conv(self, s):
		self.cinfo=None
		for k,c in enumerate(self.evaluaters):
			c.testconv(s)
			score=self.weighted_score(c.counter())
			if k==0:
				self.lastc=0
				max_score=score
			elif score>max_score:
				self.lastc=k
				max_score=score
		return self.conversions[self.lastc].conv(s)

	def conv_list(self, a):
		for k,c in enumerate(self.evaluaters):
			score=0
			for s in a:
				c.testconv(s)
				i=c.counter()
				score+=self.weighted_score(c.counter())
			if k==0:
				self.lastc=0
				max_score=score
			elif score>max_score:
				self.lastc=k
				max_score=score
		ret=[]
		for k,s in enumerate(a):
			ret.append(self.conversions[self.lastc].conv(s))
			if k==0:
				self.cinfo=self.conversions[self.lastc].counter()
			else:
				n=self.conversions[self.lastc].counter()
				self.cinfo={x:n[x]+self.cinfo[x] for x in n}
		return ret

	def conv_file(self, ifile, ofile):
		self.cinfo=None
		for k,c in enumerate(self.evaluaters):
			c.testconv_file(ifile)
			score=self.weighted_score(c.counter())
			if k==0:
				self.lastc=0
				max_score=score
			elif score>max_score:
				self.lastc=k
				max_score=score
		self.conversions[self.lastc].conv_file(ifile, ofile)
		return

	def testconv_file(self, str):
		for k,c in enumerate(self.evaluaters):
			c.testconv_file(str)
			score=self.weighted_score(c.counter())
			if k==0:
				self.lastc=0
				max_score=score
			elif score>max_score:
				self.lastc=k
				max_score=score
		self.cinfo=self.evaluaters[self.lastc].counter()
		return

	def counter(self):
		if self.cinfo:
			return self.cinfo
		return self.conversions[self.lastc].counter()

	def error(self):
		return self.errorstr

class Item(object):
	def __init__(self, iter):
		self.iter=iter
		self.item=items[iter]

	def __getattr__(self, key):
		if key == 'fullpath':
			ret=[]
			iter=self.iter
			while iter!=None:
				ret.insert(0, items[iter][attr_map['name0']])
				iter = items.iter_parent(iter)
			return os.path.join(*ret)
		elif key in attr_map:
			return self.item[attr_map[key]]
		else:
			return super(Item, self).__getattr__(key)

	def __setattr__(self, key, value):
		if key in attr_map:
			self.item[attr_map[key]]=value
		else:
			super(Item, self).__setattr__(key, value)

class gBsdConv(object):
	def __init__(self):
		global builder
		builder = Gtk.Builder()
		loaded=False
		if getattr(sys, 'frozen', False):
			app_path = os.path.dirname(sys.executable)
		elif __file__:
			app_path = os.path.dirname(__file__)
		for datadir in [app_path,app_path+'/../share/gbsdconv']:
			fullpath=os.path.join(datadir, 'gbsdconv.xml')
			if os.path.exists(fullpath):
				builder.add_from_file(fullpath)
				loaded=True
				break
		if not loaded:
			sys.stderr.write('Unable to find gbsdconv.xml\n')
			sys.exit(1)
		self.window = builder.get_object("main_window")
		self.window.connect("delete-event", Gtk.main_quit)
		self.window.set_icon_from_file(os.path.join(datadir, ('gbsdconv.png','gbsdconv2.png')[os.getpid()%2]))
		self.utf8er = Bsdconv('utf-8,3f:utf-8,3f')

		#Global objects
		self.text_extensions=['.txt', '.xml', '.sql', '.htm', '.html', '.cue', '.c', '.cc', '.h', '.cpp', '.cxx', '.php', '.pl', '.py', '.rb', '.php3', '.phtml', '.csv', '.log', '.text', '.utxt', '.srt']
		self.multimedia_extensions=['.ogg', '.flac', '.oga', '.mp3', '.mpc', '.wv', '.spx', '.tta', '.m4a', '.m4r', '.m4b', '.m4p', '.3g2', '.mp4', '.wma', '.asf', '.aif', '.aiff', '.wav', '.ape', '.mod', '.module', '.nst', '.wow', '.s3m', '.it', '.xm']
		self.counter = 0
		self.err_counter = 0
		self.err_counter_selected = 0
		self.collision = 0
		self.duplication = 0
		self.converter = Bsdconvs("")

		self.inter_codecs = {}

		self.item_tab_init()
		self.conversion_tab_init()
		self.operation_text_tab_init()
		self.operation_naming_tab_init()
		self.operation_content_tab_init()
		self.operation_metatag_tab_init()

		self.set_status("Hint: Right click for recursive operations")

		self.converter_mtime=0
		self.items_mtime=0
		self.items_selection_mtime=0

		#launch
		self.window.show()

	#Callbacks for items tab
	def item_tab_init(self):
		items_view = builder.get_object("items")
		toggle=Gtk.CellRendererToggle()
		toggle.connect("toggled", self.items_toggle_cb)
		items_view.insert_column_with_data_func(0, "#", toggle, self.items_toggle_render_cb, 0)
		items_path=Gtk.TreeViewColumn("Path", Gtk.CellRendererText(), text=attr_map['uname0'])
		items_view.append_column(items_path)
		items_view.set_expander_column(items_path)
		items_view.set_model(items)

		items_view.connect("button-press-event", self.items_button_press)

		builder.get_object("item_add").connect("clicked", self.item_add_cb)
		builder.get_object("item_add_r").connect("clicked", self.item_add_cb)
		builder.get_object("item_remove").connect("clicked", self.item_remove_cb)
		builder.get_object("item_remove_selected_leaves").connect("clicked", self.item_remove_selected_leaves_cb)

		self.item_chooser_pwd=None

		self.filter_profiles = Gtk.ListStore(str, str)
		self.filter_profiles.append(['All', '\\a'])
		self.filter_profiles.append(['File', '\\f'])
		self.filter_profiles.append(['Directory', '\\d'])
		self.filter_profiles.append(['Link', '\\l'])
		self.filter_profiles.append(['Text', ';'.join(self.text_extensions)])
		self.filter_profiles.append(['Multimedia', ';'.join(self.multimedia_extensions)])
		filter_profiles=builder.get_object('filter_profiles')
		renderer=Gtk.CellRendererText()
		filter_profiles.pack_start(renderer, False)
		filter_profiles.add_attribute(renderer, "text", 0)
		filter_profiles.set_model(self.filter_profiles)
		filter_profiles.connect('changed', self.filter_profiles_cb)

		builder.get_object("item_chooser").connect('current-folder-changed', self.item_chooser_cd_cb)
		builder.get_object("items_page").connect('draw', self.item_tab_cb)

	def item_tab_cb(self, obj1=None, obj2=None):
		self.set_status("Hint: Right click for recursive operations")
		if self.item_chooser_pwd:
			builder.get_object("item_chooser").set_current_folder_uri(self.item_chooser_pwd)

	def filter_match(self, item, filter):
		for t in filter:
			if t=='\\a':
				return True
			elif t=='\\f':
				if os.path.isfile(item.fullpath):
					return True
			elif t=='\\d':
				if os.path.isdir(item.fullpath):
					return True
			elif t=='\\l':
				if os.path.islink(item.fullpath):
					return True
			else:
				if item.name0.endswith(t):
					return True
		return False

	def filter_cb(self, obj, tree):
		mode, iter=tree.get_selection().get_selected()
		if not iter:
			return
		self.items_selection_mtime+=1
		ext=[x.strip() for x in builder.get_object('filter').get_text().split(';') if x.strip()!='']
		if '\\a' in ext:
			ext=['\\a']
		if obj.get_label()=="Filter: Select":
			self.filter_select(iter, ext)
		elif obj.get_label()=="Filter: Deselect":
			self.filter_deselect(iter, ext)
		elif obj.get_label()=="Filter: Select Others":
			self.filter_select_others(iter, ext)
		elif obj.get_label()=="Filter: Deselect Others":
			self.filter_deselect_others(iter, ext)

	def filter_select(self, parent, filter):
		curr = items.iter_children(parent)
		while curr != None:
			item=Item(curr)
			if item.selected==0:
				if self.filter_match(item, filter):
					item.selected=1
			if items.iter_has_child(curr):
				self.filter_select(curr, filter)
			curr = items.iter_next(curr)

	def filter_deselect(self, parent, filter):
		curr = items.iter_children(parent)
		while curr != None:
			item=Item(curr)
			if item.selected==1:
				if self.filter_match(item, filter):
					item.selected=0
			if items.iter_has_child(curr):
				self.filter_deselect(curr, filter)
			curr = items.iter_next(curr)

	def filter_select_others(self, parent, filter):
		curr = items.iter_children(parent)
		while curr != None:
			item=Item(curr)
			if item.selected==0:
				if not self.filter_match(item, filter):
					item.selected=1
			if items.iter_has_child(curr):
				self.filter_select_others(curr, filter)
			curr = items.iter_next(curr)

	def filter_deselect_others(self, parent, filter):
		curr = items.iter_children(parent)
		while curr != None:
			item=Item(curr)
			if item.selected==1:
				if not self.filter_match(item, filter):
					item.selected=0
			if items.iter_has_child(curr):
				self.filter_deselect_others(curr, filter)
			curr = items.iter_next(curr)

	def item_toggle_cb(self, obj, tree):
		mode, iter=tree.get_selection().get_selected()
		if not iter:
			return
		self.items_selection_mtime+=1
		if obj.get_label()=="Inverse":
			self.item_toggle_cb_r(iter, -1)
		elif obj.get_label()=="Select":
			self.item_toggle_cb_r(iter, 1)
		elif obj.get_label()=="Deselect":
			self.item_toggle_cb_r(iter, 0)

	def item_toggle_cb_r(self, parent, v):
		iter = items.iter_children(parent)
		while iter!=None:
			item=Item(iter)
			if item.selected>=0:
				if v==-1:
					if item.selected:
						item.selected=0
					else:
						item.selected=1
				else:
					item.selected=v
			if items.iter_has_child(iter):
				self.item_toggle_cb_r(iter, v)
			iter=items.iter_next(iter)

	def items_button_press(self, obj, evt):
		mode, iter=obj.get_selection().get_selected()
		if not iter:
			return False
		if not items.iter_has_child(iter):
			return False
		self.items_button_press_sub(obj, evt)
		if obj==builder.get_object("naming"):
			self.naming_cb_r(items.iter_parent(iter))

	def items_button_press_sub(self, obj, evt):
		if evt.button==3 and evt.type==Gdk.EventType.BUTTON_PRESS :
			menu=Gtk.Menu()

			item=Gtk.MenuItem.new_with_label("Inverse")
			item.set_visible(True)
			item.connect("activate", self.item_toggle_cb, obj)
			menu.attach(item, 0, 1, 0, 1)

			item=Gtk.MenuItem.new_with_label("Select")
			item.set_visible(True)
			item.connect("activate", self.item_toggle_cb, obj)
			menu.attach(item, 0, 1, 1, 2)

			item=Gtk.MenuItem.new_with_label("Deselect")
			item.set_visible(True)
			item.connect("activate", self.item_toggle_cb, obj)
			menu.attach(item, 0, 1, 2, 3)

			if obj==builder.get_object("items"):
				item=Gtk.MenuItem.new_with_label("Filter: Select")
				item.set_visible(True)
				item.connect("activate", self.filter_cb, obj)
				menu.attach(item, 0, 1, 3, 4)

				item=Gtk.MenuItem.new_with_label("Filter: Deselect")
				item.set_visible(True)
				item.connect("activate", self.filter_cb, obj)
				menu.attach(item, 0, 1, 4, 5)

				item=Gtk.MenuItem.new_with_label("Filter: Select Others")
				item.set_visible(True)
				item.connect("activate", self.filter_cb, obj)
				menu.attach(item, 0, 1, 5, 6)

				item=Gtk.MenuItem.new_with_label("Filter: Deselect Others")
				item.set_visible(True)
				item.connect("activate", self.filter_cb, obj)
				menu.attach(item, 0, 1, 6, 7)

			menu.attach_to_widget(obj, None)
			menu.popup(None, None, None, None, evt.button, evt.time)
		return False

	def item_chooser_cd_cb(self, obj):
		self.items_chooser_pwd=obj.get_current_folder_uri()

	def filter_profiles_cb(self, obj):
		builder.get_object('filter').set_text(self.filter_profiles[builder.get_object('filter_profiles').get_active_iter()][1])

	def item_add_cb(self, obj):
		self.items_mtime+=1
		recur = obj==builder.get_object("item_add_r")
		for path in builder.get_object("item_chooser").get_filenames():
			self.item_add_cb_r(path, recur)

		builder.get_object("items").expand_all()
		builder.get_object("content_items").expand_all()

	def item_add_cb_r(self, path, recur):
		self.item_add_cb_sub(path)
		if recur and os.path.isdir(path):
			for spath in os.listdir(path):
				self.item_add_cb_r(os.path.join(path, spath), recur)

	def item_add_cb_sub(self, path):
		a=self.pathsplit(path)
		parent = None
		for i in xrange(0, len(a)):
			p=a[i]
			curr = items.iter_children(parent)
			while curr != None:
				item=Item(curr)
				if p==item.name0:
					parent = curr
					break
				curr = items.iter_next(curr)
			if curr == None:
				if i==len(a)-1:
					selected=1
				else:
					selected=-1
				parent = items.append(parent, [selected, p, '', '', self.utf8er.conv(p), '', '', '', '', '', '', '', '', '', '', '', ''])
			else:
				if i==len(a)-1:
					items[curr][attr_map['selected']]=1

	def item_remove_cb(self, obj):
		self.items_mtime+=1
		model, treeiter = builder.get_object("items").get_selection().get_selected()
		if treeiter:
			model.remove(treeiter)

	def item_remove_selected_leaves_cb(self, obj):
		self.item_remove_selected_leaves_cb_r(None)

	def item_remove_selected_leaves_cb_r(self, parent):
		curr = items.iter_children(parent)
		while curr != None:
			next=items.iter_next(curr)
			if items.iter_has_child(curr):
				self.item_remove_selected_leaves_cb_r(curr)
			else:
				item=Item(curr)
				if item.selected>0:
					items.remove(curr)
			curr = next

	#Callbacks for conversion tab
	def conversion_tab_init(self):
		self.from_codecs = Gtk.ListStore(str)
		self.inter_codecs = Gtk.ListStore(str)
		self.to_codecs = Gtk.ListStore(str)
		self.profiles = Gtk.ListStore(str, str)
		self.fitting_algo = Gtk.ListStore(str, str)

		codecs=Bsdconv.modules_list(Bsdconv.FROM)
		codecs.sort(self._cmp)
		for codec in codecs:
			self.from_codecs.append([codec])
		codecs=Bsdconv.modules_list(Bsdconv.INTER)
		codecs.sort(self._cmp)
		for codec in codecs:
			self.inter_codecs.append([codec])
		codecs=Bsdconv.modules_list(Bsdconv.TO)
		codecs.sort(self._cmp)
		for codec in codecs:
			self.to_codecs.append([codec])

		self.profiles.append(['Chineses Simplified -> Traditional','utf-8:score:whitespace-derail:zhtw:zhtw-words:whitespace-rerail:utf-8;gbk:score:whitespace-derail:zhtw:zhtw-words:whitespace-rerail:utf-8'])
		self.profiles.append(['Chineses Traditional -> Simplified','utf-8:score:zhcn:utf-8;big5:score:zhcn:utf-8'])
		self.profiles.append(['China','utf-8:score:zhcn:utf-8;gbk:score:zhcn:utf-8'])
		self.profiles.append(['West Europe','utf-8:score:utf-8;cp1252:score:utf-8'])
		self.profiles.append(['Japan','utf-8:score:utf-8;shift-jis:score:utf-8;jis:score:utf-8'])
		self.profiles.append(['Korea','utf-8:score:utf-8;cp949:score:utf-8'])
		self.profiles.append(['Russia','utf-8:score:utf-8;cp1251:score:utf-8'])
		self.profiles.append(['Taiwan','utf-8:score:whitespace-derail:zhtw:zhtw-words:whitespace-rerail:utf-8;big5:score:whitespace-derail:zhtw:zhtw-words:whitespace-rerail:utf-8'])
		self.profiles.append(['Thailand','utf-8:score:utf-8;cp874:score:utf-8'])

		from_codecs=builder.get_object('from_codecs')
		renderer=Gtk.CellRendererText()
		from_codecs.pack_start(renderer, False)
		from_codecs.add_attribute(renderer, "text", 0)
		from_codecs.set_model(self.from_codecs)
		from_codecs.set_active(0)

		inter_codecs=builder.get_object('inter_codecs')
		renderer=Gtk.CellRendererText()
		inter_codecs.pack_start(renderer, False)
		inter_codecs.add_attribute(renderer, "text", 0)
		inter_codecs.set_model(self.inter_codecs)
		inter_codecs.set_active(0)

		to_codecs=builder.get_object('to_codecs')
		renderer=Gtk.CellRendererText()
		to_codecs.pack_start(renderer, False)
		to_codecs.add_attribute(renderer, "text", 0)
		to_codecs.set_model(self.to_codecs)
		to_codecs.set_active(0)

		profiles=builder.get_object('profiles')
		renderer=Gtk.CellRendererText()
		profiles.pack_start(renderer, False)
		profiles.add_attribute(renderer, "text", 0)
		profiles.set_model(self.profiles)
		profiles.set_active(0)

		builder.get_object("from_add").connect("clicked", self.from_add_cb)
		builder.get_object("inter_add").connect("clicked", self.inter_add_cb)
		builder.get_object("to_add").connect("clicked", self.to_add_cb)
		builder.get_object("profile_add").connect("clicked", self.profile_add_cb)

		builder.get_object("factor_ierr").set_text("-2")
		builder.get_object("factor_oerr").set_text("-2")
		builder.get_object("factor_score").set_text("1")

		builder.get_object("url_arg1").set_text("utf-8")
		builder.get_object("url_arg2").set_text("utf-8")
		builder.get_object("url_add").connect("clicked", self.url_add_cb)

		builder.get_object("mistreat_arg1").set_text("big5")
		builder.get_object("mistreat_arg2").set_text("iso-8859-1")
		builder.get_object("mistreat_add").connect("clicked", self.mistreat_add_cb)

		builder.get_object("factor_ierr").connect("changed", self.bump_converter_mtime)
		builder.get_object("factor_oerr").connect("changed", self.bump_converter_mtime)
		builder.get_object("factor_score").connect("changed", self.bump_converter_mtime)
		builder.get_object("conversion").connect("changed", self.conversion_apply_cb)
		builder.get_object("chk_path_train").connect("clicked", self.conversion_apply_cb)
		builder.get_object("conversion_clear").connect("clicked", lambda x: builder.get_object("conversion").set_text(""))

		builder.get_object('chk_remove_id3v1').set_active(True)
		builder.get_object('chk_use_id3v2_3').set_active(True)

		self.last_add=None

	def _cmp(self, x,y):
		a,b=x,y
		if x[0]=='_':
			a=x[1:]
		if y[0]=='_':
			b=y[1:]
		if x==y:
			return cmp(x, y)
		return cmp(a, b)

	def from_add_cb(self, obj):
		iter=builder.get_object('from_codecs').get_active_iter()
		conversion=builder.get_object("conversion")
		op={
			None:'',
			'from':',',
			'inter':':locale|',
			'to':'|',
			'profile':';',
		}
		conversion.set_text('%s%s%s' % (conversion.get_text(), op[self.last_add], self.from_codecs[iter][0]))
		self.last_add='from'

	def inter_add_cb(self, obj):
		iter=builder.get_object('inter_codecs').get_active_iter()
		conversion=builder.get_object("conversion")
		op={
			None:'',
			'from':':',
			'inter':':',
			'to':'|locale:',
			'profile':';locale:',
		}
		conversion.set_text('%s%s%s' % (conversion.get_text(), op[self.last_add], self.inter_codecs[iter][0]))
		self.last_add='inter'

	def to_add_cb(self, obj):
		iter=builder.get_object('to_codecs').get_active_iter()
		conversion=builder.get_object("conversion")
		op={
			None:'',
			'from':':',
			'inter':':',
			'to':'|locale:',
			'profile':';locale:',
		}
		conversion.set_text('%s%s%s' % (conversion.get_text(), op[self.last_add], self.to_codecs[iter][0]))
		self.last_add='to'

	def profile_add_cb(self, obj):
		iter=builder.get_object('profiles').get_active_iter()
		conversion=builder.get_object("conversion")
		op={
			None:'',
			'from':':locale;',
			'inter':':locale;',
			'to':';',
			'profile':';',
		}
		conversion.set_text('%s%s%s' % (conversion.get_text(), op[self.last_add], self.profiles[iter][1]))
		self.last_add='profile'

	def url_add_cb(self, obj):
		conversion=builder.get_object("conversion")
		op={
			None:'',
			'from':':locale;',
			'inter':':locale;',
			'to':';',
			'profile':';',
		}
		conversion.set_text('%s%sescape,%s:unicode,byte|skip,%s:utf-8' % (conversion.get_text(), op[self.last_add], builder.get_object('url_arg2').get_text(), builder.get_object('url_arg1').get_text()))
		self.last_add='profile'

	def mistreat_add_cb(self, obj):
		conversion=builder.get_object("conversion")
		op={
			None:'',
			'from':':locale;',
			'inter':':locale;',
			'to':';',
			'profile':';',
		}
		conversion.set_text('%s%sutf-8:%s|%s:utf-8' % (conversion.get_text(), op[self.last_add], builder.get_object('mistreat_arg2').get_text(), builder.get_object('mistreat_arg1').get_text()))
		self.last_add='profile'

	def bump_converter_mtime(self, *arg):
		self.converter_mtime+=1

	def conversion_apply_cb(self, obj):
		self.converter_mtime+=1
		self.converter = Bsdconvs(builder.get_object("conversion").get_text())
		if self.converter:
			self.set_status(str(self.converter))
		else:
			self.set_status(self.converter.error())

	#Callbacks for operation tab
	#+Text
	def operation_text_tab_init(self):
		text_origin_buffer = builder.get_object("text_origin").get_buffer()
		text_origin_buffer.connect("changed", self.text_changed)
		builder.get_object("text_result").connect("draw", self.text_cb)

		self.operation_text_origin_mtime=0

		self.operation_text_result_mtime=(0,0) #converter, origin

	def text_changed(self, obj=None):
		self.operation_text_origin_mtime+=1
		self.text_cb()

	def text_cb(self, obj1=None, obj2=None):
		mtime=(self.converter_mtime, self.operation_text_origin_mtime)
		if self.operation_text_result_mtime==mtime:
			return
		self.operation_text_result_mtime=mtime
		i=builder.get_object("text_origin").get_buffer()
		o=builder.get_object("text_result").get_buffer()
		cnt = i.get_text(i.get_start_iter(), i.get_end_iter(), 0)
		o.set_text(cnt)
		o.set_text(self.utf8er.conv(self.converter.conv(cnt)))
		info=self.converter.counter()
		self.set_status('ierr:%d oerr:%d' % (info['IERR'], info['OERR']))

	#+Naming
	def operation_naming_tab_init(self):
		naming = builder.get_object("naming")

		toggle=Gtk.CellRendererToggle()
		toggle.connect("toggled", self.naming_items_toggle_cb)
		naming.insert_column_with_data_func(0, "#", toggle, self.items_toggle_render_cb, 0)

		renderer = Gtk.CellRendererText()
		items_path = Gtk.TreeViewColumn("Path", renderer, text=attr_map['lname0'])
		items_path.set_resizable(True)
		naming.append_column(items_path)
		naming.set_expander_column(items_path)

		renderer = Gtk.CellRendererText()
		items_path2 = Gtk.TreeViewColumn("Result", renderer, text=attr_map['lname1'])
		items_path2.set_resizable(True)
		naming.append_column(items_path2)

		renderer = Gtk.CellRendererText()
		info = Gtk.TreeViewColumn("Info", renderer, text=attr_map['naming_info'])
		naming.append_column(info)

		naming.set_model(items)

		naming.connect("button-press-event", self.items_button_press)
		naming.connect("draw", self.naming_cb)

		builder.get_object('naming_convert').connect('clicked', self.naming_convert)

		self.operation_naming_mtime=(0,0,0) #converter, items, selection

	def naming_cb(self, obj1=None, obj2=None):
		mtime=(self.converter_mtime, self.items_mtime, self.items_selection_mtime)
		if self.operation_naming_mtime==mtime:
			return
		self.operation_naming_mtime=mtime
		naming = builder.get_object("naming")
		self.err_counter=0
		self.err_counter_selected=0
		self.collision=0
		self.duplication=0
		self.naming_cb_r(None)
		self.naming_cb_r_sym(None)
		self.set_status('Error: %d Collision: %d Duplication: %d' % (self.err_counter_selected, self.collision, self.duplication))
		naming.expand_all()

	def naming_cb_r(self, parent):
		curr = items.iter_children(parent)
		pool={}
		while curr != None:
			item=Item(curr)
			item.error_counter=''
			item.collision=''
			item.duplication=''
			fullpath=item.fullpath
			if os.path.islink(fullpath):
				item.target0=os.readlink(fullpath)
			if item.selected>0:
				newname=self.converter.conv(item.name0)
				item.name2=newname
				item.name1=item.name2
				info=self.converter.counter()
				if info['IERR']+info['OERR']>0:
					item.error_counter=str(info['IERR']+info['OERR'])
					self.err_counter_selected+=1
				if newname in pool:
					item.collision='C'
					self.collision+=1
					oitem=Item(pool[newname])
					if oitem.collision!='C':
						oitem.collision='C'
						self.collision+=1
						oitem.name1=oitem.name0
					item.name1=item.name0
				else:
					pool[newname]=curr
				newpath = self.getFullNewPath(curr)
				if newname!=item.name0 and os.path.exists(newpath):
					item.duplication='D'
					self.duplication+=1
					item.name1=item.name0
			else:
				item.target1=item.target0
				item.name1=item.name0
			if items.iter_has_child(curr):
				self.naming_cb_r(curr)
			curr = items.iter_next(curr)

	def naming_cb_r_sym(self, parent): #must be after naming_cb_r, or this may use uncooked name1
		curr = items.iter_children(parent)
		while curr != None:
			item=Item(curr)
			if item.selected>0:
				if os.path.islink(item.fullpath):
					item.target1=self.naming_conv_symlink(curr)
			self.gen_naming_display(curr)
			if items.iter_has_child(curr):
				self.naming_cb_r_sym(curr)
			curr = items.iter_next(curr)

	def naming_conv_symlink(self, curr):
		a=self.pathsplit(os.readlink(Item(curr).fullpath))
		curr=items.iter_parent(curr)
		if not curr:
			return os.path.join(*a)
		curr=items.iter_children(curr)
		for k,v in enumerate(a):
			if v=='/':
				curr=None
			elif v=='..':
				curr=items.iter_parent(curr)
				if not curr:
					break
				curr=items.iter_parent(curr)
				if not curr:
					break
				curr=items.iter_children(curr)
			elif v=='.':
				pass
			else:
				while curr != None:
					item=Item(curr)
					if v==item.name0:
						a[k]=item.name1
						if items.iter_has_child(curr):
							curr=items.iter_children(curr)
						break
					curr = items.iter_next(curr)
				if curr == None:
					break
		return os.path.join(*a)

	def gen_naming_display(self, curr):
		item=Item(curr)
		item.naming_info=''.join((item.error_counter,item.collision,item.duplication))
		item.uname0=self.utf8er.conv(item.name0)
		if item.target0=='':
			item.lname0=item.uname0
		else:
			item.lname0="%s -> %s" % (item.uname0, self.utf8er.conv(item.target0))
		item.uname1=self.utf8er.conv(item.name1)
		if item.target1=='':
			item.lname1=item.uname1
		else:
			item.lname1="%s -> %s" % (item.uname1, self.utf8er.conv(item.target1))

	def getFullNewPath(self, ti):
		ret=[]
		if not ti:
			return ''
		item=Item(ti)
		ret.insert(0, item.name2)
		ti = items.iter_parent(ti)
		while ti!=None:
			if item.selected>=0:
				ret.insert(0, item.name1)
			else:
				ret.insert(0, item.name0)
			ti = items.iter_parent(ti)
		return os.path.join(*ret)

	def naming_items_toggle_cb(self, renderer, path):
		self.items_selection_mtime+=1
		iter=items.get_iter(path)
		item=Item(iter)
		if item.selected:
			item.selected=0

		else:
			item.selected=1
		self.naming_cb_r(items.iter_parent(iter))

	def naming_convert(self, obj):
		self.counter=0
		self.naming_convert_r(None)
		self.set_status('%d dirs/files converted' % self.counter)

	def naming_convert_r(self, parent):
		curr = items.iter_children(parent)
		while curr != None:
			if items.iter_has_child(curr):
				self.naming_convert_r(curr)
			item=Item(curr)
			if item.selected>0 and item.collision=='' and item.duplication=='' and (item.name0!=item.name1 or item.target0!=item.target1):
				os.chdir(os.path.dirname(item.fullpath))
				if os.path.islink(item.name0):
					os.unlink(item.name0)
					os.symlink(item.target1, item.name1)
				else:
					os.rename(item.name0, item.name1)
				item.name0=item.name1
				self.counter+=1
			curr = items.iter_next(curr)
		self.naming_cb_r_sym(None)

	#+Content
	def operation_content_tab_init(self):
		content_items = builder.get_object("content_items")
		toggle=Gtk.CellRendererToggle()
		toggle.connect("toggled", self.items_toggle_cb)
		content_items.insert_column_with_data_func(0, "#", toggle, self.items_toggle_render_cb, 0)

		renderer = Gtk.CellRendererText()
		items_path = Gtk.TreeViewColumn("Path", renderer, text=attr_map['uname0'])
		items_path.set_expand(True)
		content_items.append_column(items_path)
		content_items.set_expander_column(items_path)

		renderer = Gtk.CellRendererText()
		items_info = Gtk.TreeViewColumn("Info", renderer, text=attr_map['content_info'])
		content_items.append_column(items_info)

		content_items.set_model(items)

		content_items.connect("button-press-event", self.items_button_press)

		content_items.get_selection().connect("changed", self.content_items_select_cb)

		chk_content_ignore_nontext=builder.get_object('chk_content_ignore_nontext')
		chk_content_ignore_nontext.set_active(True)
		chk_content_ignore_nontext.connect('clicked', self.chk_content_ignore_nontext_cb)

		builder.get_object('content_convert').connect('clicked', self.content_convert)

		content_items.connect("draw", self.content_cb)

		self.operation_content_ignore_nontext_mtime=0

		self.operation_content_mtime=(0,0,0) #converter, items, chk_ignore_nontext

	def content_items_select_cb(self, obj):
		model, iter = obj.get_selected()
		if iter != None:
			item=Item(iter)
			fullpath = item.fullpath
			if os.path.isfile(fullpath):
				if not builder.get_object('chk_content_ignore_nontext').get_active() or self.filter_match(item, self.text_extensions):
					f=open(fullpath,'r')
					cnt=f.read()
					builder.get_object("content_origin").get_buffer().set_text(self.utf8er.conv(cnt))
					f.close()
					builder.get_object("content_result").get_buffer().set_text(self.utf8er.conv(self.converter.conv(cnt)))
				else:
					self.set_status("%s is ignored as it is not a text file" % fullpath)
			else:
				self.set_status("%s is not a file" % fullpath)

	def chk_content_ignore_nontext_cb(self, obj=None):
		self.operation_content_ignore_nontext_mtime+=1
		self.content_cb()

	def content_cb(self, obj1=None, obj2=None):
		mtime=(self.converter_mtime, self.items_mtime, self.operation_content_ignore_nontext_mtime)
		if self.operation_content_mtime==mtime:
			return
		self.operation_content_mtime=mtime
		self.err_counter=0
		self.err_counter_selected=0
		self.content_cb_r(None)
		self.set_status('Error: %d (Selected: %d)' % (self.err_counter, self.err_counter_selected))

	def content_cb_r(self, parent):
		curr = items.iter_children(parent)
		while curr != None:
			item=Item(curr)
			fullpath=item.fullpath
			item.content_info=''
			if item.selected>=0 and os.path.isfile(fullpath) and (not builder.get_object('chk_content_ignore_nontext').get_active() or self.filter_match(item, self.text_extensions)):
				self.converter.testconv_file(fullpath)
				i=self.converter.counter()
				err=i['IERR']+i['OERR']
				if err>0:
					item.content_info=str(err)
					self.err_counter+=1
					if item.selected>0:
						self.err_counter_selected+=1
			if items.iter_has_child(curr):
				self.content_cb_r(curr)
			curr = items.iter_next(curr)

	def content_convert(self, obj):
		self.counter=0
		self.content_convert_r(None)
		self.set_status('%d files converted' % self.counter)

	def content_convert_r(self, parent):
		curr = items.iter_children(parent)
		while curr != None:
			item=Item(curr)
			if item.selected>0 and (not builder.get_object('chk_content_ignore_nontext').get_active() or self.filter_match(item, self.text_extensions)):
				fullpath=item.fullpath
				self.converter.conv_file(fullpath, fullpath)
				self.counter+=1
			if items.iter_has_child(curr):
				self.content_convert_r(curr)
			curr = items.iter_next(curr)

	#+Meta Tag
	def operation_metatag_tab_init(self):
		metatag_items = builder.get_object("metatag_items")
		toggle=Gtk.CellRendererToggle()
		toggle.connect("toggled", self.items_toggle_cb)
		metatag_items.insert_column_with_data_func(0, "#", toggle, self.items_toggle_render_cb, 0)

		renderer = Gtk.CellRendererText()
		items_path = Gtk.TreeViewColumn("Path", renderer, text=attr_map['uname0'])
		items_path.set_expand(True)
		metatag_items.append_column(items_path)
		metatag_items.set_expander_column(items_path)

		renderer = Gtk.CellRendererText()
		items_info = Gtk.TreeViewColumn("Info", renderer, text=attr_map['metatag_info'])
		metatag_items.append_column(items_info)

		metatag_items.set_model(items)

		metatag_items.connect("button-press-event", self.items_button_press)
		metatag_items.connect("draw", self.metatag_cb)

		metatag_items.get_selection().connect("changed", self.metatag_items_select_cb)

		metatag_origin=builder.get_object('metatag_origin')
		metatag_origin.append_column(Gtk.TreeViewColumn("Key", Gtk.CellRendererText(), text=0))
		metatag_origin.append_column(Gtk.TreeViewColumn("Value", Gtk.CellRendererText(), text=1))

		metatag_result=builder.get_object('metatag_result')
		metatag_result.append_column(Gtk.TreeViewColumn("Key", Gtk.CellRendererText(), text=0))
		metatag_result.append_column(Gtk.TreeViewColumn("Value", Gtk.CellRendererText(), text=1))

		builder.get_object('chk_remove_comment').connect('clicked', self.metatag_items_select_cb)
		builder.get_object('chk_skip_title').connect('clicked', self.metatag_items_select_cb)
		builder.get_object('chk_skip_artist').connect('clicked', self.metatag_items_select_cb)
		builder.get_object('chk_skip_album').connect('clicked', self.metatag_items_select_cb)
		builder.get_object('chk_skip_comment').connect('clicked', self.metatag_items_select_cb)
		builder.get_object('chk_skip_genre').connect('clicked', self.metatag_items_select_cb)

		builder.get_object('metatag_convert').connect('clicked', self.metatag_convert)

		self.operation_metatag_mtime=(0,0) #converter, items

	def metatag_cb(self, obj1=None, obj2=None):
		mtime=(self.converter_mtime, self.items_mtime)
		if self.operation_metatag_mtime==mtime:
			return
		self.operation_metatag_mtime=mtime
		self.err_counter=0
		self.err_counter_selected=0
		self.metatag_info(None)
		builder.get_object("metatag_items").expand_all()
		self.set_status('Error: %d (Selected: %d)' % (self.err_counter, self.err_counter_selected))

	def metatag_info(self, parent):
		curr = items.iter_children(parent)
		while curr != None:
			item=Item(curr)
			item.metatag_info=''
			fullpath=item.fullpath
			if os.path.isfile(fullpath):
				mfile=taglib.taglib_file_new(fullpath)
				if mfile!=None:
					self.set_status(fullpath)
					if taglib.taglib_file_is_valid(mfile):
						self.converter.score_clear()
						self.converter.score_train(fullpath)

						tag=taglib.taglib_file_tag(mfile)

						title=self.latin1(taglib.taglib_tag_title(tag))
						artist=self.latin1(taglib.taglib_tag_artist(tag))
						album=self.latin1(taglib.taglib_tag_album(tag))
						comment=self.latin1(taglib.taglib_tag_comment(tag))
						genre=self.latin1(taglib.taglib_tag_genre(tag))

						title, artist, album, comment, genre=self.metatag_conv_tag(title, artist, album, comment, genre)

						taglib.taglib_tag_free_strings()

					taglib.taglib_file_free(mfile)
					info=self.converter.counter()
					err=info['IERR']+info['OERR']
					if err>0:
						item.metatag_info=str(err)
						self.err_counter+=1
						if item.selected>0:
							self.err_counter_selected+=1

			if items.iter_has_child(curr):
				self.metatag_info(curr)
			curr = items.iter_next(curr)

	def metatag_items_select_cb(self, obj):
		model, iter = builder.get_object('metatag_items').get_selection().get_selected()
		if iter != None:
			item=Item(iter)
			fullpath = item.fullpath

			origin=Gtk.TreeStore(str, str)
			metatag_origin=builder.get_object('metatag_origin')
			metatag_origin.set_model(origin)

			result=Gtk.TreeStore(str, str)
			metatag_result=builder.get_object('metatag_result')
			metatag_result.set_model(result)

			if item.selected<0:
				return

			if os.path.isfile(fullpath):
				mfile=taglib.taglib_file_new(fullpath)
				if mfile!=None:
					if taglib.taglib_file_is_valid(mfile):
						self.converter.score_clear()
						self.converter.score_train(fullpath)

						tag=taglib.taglib_file_tag(mfile)

						title=self.latin1(taglib.taglib_tag_title(tag))
						artist=self.latin1(taglib.taglib_tag_artist(tag))
						album=self.latin1(taglib.taglib_tag_album(tag))
						comment=self.latin1(taglib.taglib_tag_comment(tag))
						genre=self.latin1(taglib.taglib_tag_genre(tag))

						origin.append(None, ['Title', self.utf8er.conv(title)])
						origin.append(None, ['Artist', self.utf8er.conv(artist)])
						origin.append(None, ['Album', self.utf8er.conv(album)])
						origin.append(None, ['Comment', self.utf8er.conv(comment)])
						origin.append(None, ['Genre', self.utf8er.conv(genre)])
						origin.append(None, ['Year', str(taglib.taglib_tag_year(tag))])
						origin.append(None, ['Track', str(taglib.taglib_tag_track(tag))])

						title, artist, album, comment, genre=self.metatag_conv_tag(title, artist, album, comment, genre)

						result.append(None, ['Title', self.utf8er.conv(title)])
						result.append(None, ['Artist', self.utf8er.conv(artist)])
						result.append(None, ['Album', self.utf8er.conv(album)])
						result.append(None, ['Comment', self.utf8er.conv(comment)])
						result.append(None, ['Genre', self.utf8er.conv(genre)])
						result.append(None, ['Year', str(taglib.taglib_tag_year(tag))])
						result.append(None, ['Track', str(taglib.taglib_tag_track(tag))])

						taglib.taglib_tag_free_strings()

						metatag_origin.expand_all()
						metatag_result.expand_all()
					else:
						self.set_status("%s is not a valid file" % fullpath)
					taglib.taglib_file_free(mfile)
				else:
					self.set_status("%s is not a valid file" % fullpath)
			else:
				self.set_status("%s is not a file" % fullpath)

	def metatag_convert(self, obj):
		self.counter=0
		self.metatag_convert_r(None)
		self.set_status('%d files converted' % self.counter)

	def metatag_convert_r(self, parent):
		curr = items.iter_children(parent)
		while curr != None:
			item=Item(curr)
			fullpath=item.fullpath
			if item.selected>0 and os.path.isfile(fullpath):
				mfile=taglib.taglib_file_new(fullpath)
				if mfile!=None:
					if taglib.taglib_file_is_valid(mfile):
						self.converter.score_clear()
						self.converter.score_train(fullpath)

						tag=taglib.taglib_file_tag(mfile)

						title=self.latin1(taglib.taglib_tag_title(tag))
						artist=self.latin1(taglib.taglib_tag_artist(tag))
						album=self.latin1(taglib.taglib_tag_album(tag))
						comment=self.latin1(taglib.taglib_tag_comment(tag))
						genre=self.latin1(taglib.taglib_tag_genre(tag))

						title, artist, album, comment, genre=self.metatag_conv_tag(title, artist, album, comment, genre)

						if not builder.get_object('chk_skip_title').get_active():
							taglib.taglib_tag_set_title(tag, title)
						if not builder.get_object('chk_skip_artist').get_active():
							taglib.taglib_tag_set_artist(tag, artist)
						if not builder.get_object('chk_skip_album').get_active():
							taglib.taglib_tag_set_album(tag, album)
						if not builder.get_object('chk_skip_comment').get_active():
							taglib.taglib_tag_set_comment(tag, comment)
						if not builder.get_object('chk_skip_genre').get_active():
							taglib.taglib_tag_set_genre(tag, genre)

						mpeg=taglib.taglib_mpeg_file(mfile)
						if mpeg and builder.get_object('chk_use_id3v2_3').get_active():
							taglib.taglib_mpeg_file_save3(mpeg, 0xffff, False, 3)
						else:
							taglib.taglib_file_save(mfile)

						if mpeg and builder.get_object('chk_remove_id3v1').get_active():
							taglib.taglib_mpeg_file_strip(mpeg, 0x0001)

						taglib.taglib_tag_free_strings()
					taglib.taglib_file_free(mfile)
				self.counter+=1
			if items.iter_has_child(curr):
				self.metatag_convert_r(curr)
			curr = items.iter_next(curr)

	def metatag_conv_tag(self, title, artist, album, comment, genre):
		a=[title, artist, album, comment, genre]
		if builder.get_object('chk_skip_title').get_active():
			a[0]=''
		if builder.get_object('chk_skip_artist').get_active():
			a[1]=''
		if builder.get_object('chk_skip_album').get_active():
			a[2]=''
		if builder.get_object('chk_skip_comment').get_active():
			a[3]=''
		if builder.get_object('chk_skip_genre').get_active():
			a[4]=''
		a=self.converter.conv_list(a)
		if builder.get_object('chk_skip_title').get_active():
			a[0]=title
		if builder.get_object('chk_skip_artist').get_active():
			a[1]=artist
		if builder.get_object('chk_skip_album').get_active():
			a[2]=album
		if builder.get_object('chk_skip_comment').get_active():
			a[3]=comment
		if builder.get_object('chk_skip_genre').get_active():
			a[4]=genre

		if builder.get_object('chk_remove_comment').get_active():
			a[3]=''
		return a

	#helper
	def latin1(self, s):
		try:
			s=s.decode('utf-8').encode('latin1')
		except:
			pass
		return s

	def pathsplit(self, path):
		a=[path]
		while len(a[0])>1 and a[0]!='..':
			t=a.pop(0)
			p, q = os.path.split(t)
			a.insert(0, q)
			a.insert(0, p)
		return a

	def set_status(self, s):
		status=builder.get_object('status')
		status.remove_all(0)
		status.push(0, s)

	def items_toggle_render_cb(self, column, cell, model, iter, arg):
		item=Item(iter)
		if item.selected==-1:
			cell.set_visible(False)
		else:
			cell.set_visible(True)
			if item.selected==0:
				cell.set_active(False)
			else:
				cell.set_active(True)

	def items_toggle_cb(self, renderer, path):
		iter=items.get_iter(path)
		item=Item(iter)
		if item.selected:
			item.selected=0
		else:
			item.selected=1

if __name__ == "__main__":
	app = gBsdConv()
	Gtk.main()
