import rox
from rox import g
import os
import type
import fields

edit_boxes = {}

ADD = 1
DELETE = 2
EDIT = 3

class Editor(rox.Dialog):
	def __init__(self):
		rox.Dialog.__init__(self)
		self.set_default_size(600, 400)
		self.set_has_separator(False)

		self.add_type = None

		self.add_button(g.STOCK_ADD, ADD)
		self.add_button(g.STOCK_DELETE, DELETE)
		self.add_button(g.STOCK_PROPERTIES, EDIT)
		self.add_button(g.STOCK_CLOSE, g.RESPONSE_CANCEL)

		self.set_default_response(g.RESPONSE_CANCEL)

		swin = g.ScrolledWindow()
		swin.set_border_width(4)
		swin.set_policy(g.POLICY_NEVER, g.POLICY_ALWAYS)
		swin.set_shadow_type(g.SHADOW_IN)
		self.vbox.pack_start(swin, True, True, 0)

		self.mime_model = g.TreeStore(str, str)
		view = g.TreeView(self.mime_model)
		self.view = view
		swin.add(view)
		view.set_search_column(1)

		cell = g.CellRendererText()
		column = g.TreeViewColumn(_('MIME-Type'), cell, text = 0)
		view.append_column(column)
		column.set_sort_column_id(0)

		cell = g.CellRendererText()
		column = g.TreeViewColumn(_('Description'), cell, text = 1)
		view.append_column(column)
		column.set_sort_column_id(1)

		view.connect('row-activated', self.activate)

		self.vbox.show_all()

		def response(self, resp):
			if resp == EDIT:
				self.edit_selected(view.get_selection())
			elif resp == DELETE:
				self.delete_type(view)
			elif resp == ADD:
				if self.add_type:
					self.add_type.present()
				else:
					self.add_type = NewType()
					def destroyed(widget): self.add_type = None
					self.add_type.connect('destroy', destroyed)
					self.add_type.show()
			else:
				self.destroy()
		self.connect('response', response)

		def changed(selection):
			model, iter = selection.get_selected()
			self.set_response_sensitive(EDIT, iter != None)
			self.set_response_sensitive(DELETE, iter != None)
		selection = view.get_selection()
		selection.connect('changed', changed)
		changed(selection)
	
	def delete_type(self, view):
		model, iter = view.get_selection().get_selected()
		if not iter:
			rox.alert(_('Nothing selected'))
			return
		type_name = model.get_value(iter, 0)
		type.delete_type(type_name)
	
	def update(self):
		self.set_title(_('Scanning... please wait'))
		g.gdk.flush()

		type.init()

		self.mime_model.clear()

		medias = {}
		
		def items_cmp(a, b):
			return cmp(a[1], b[1])
		def caps(a):
			return a[:1].upper() + a[1:]
		items = [(t, caps(t.get_comment())) for t in type.types.values()]
		items.sort(items_cmp)
		for t, c in items:
			iter = self.mime_model.append(None)
			self.mime_model.set(iter, 1, c, 0, t.get_name())

		self.set_title('MIME-Editor')

		for b in edit_boxes.values():
			if b.mime_type in type.types:
				b.update()
			else:
				b.destroy()
	
	def activate(self, view, path, column):
		iter = self.mime_model.get_iter(path)
		type_name = self.mime_model.get_value(iter, 0)
		self.edit(type_name)
	
	def edit(self, type_name):
		if type_name in edit_boxes:
			edit_boxes[type_name].present()
		else:
			box = EditBox(type_name)
			edit_boxes[type_name] = box
			def destroy(dialog): del edit_boxes[type_name]
			box.connect('destroy', destroy)
			box.show()
	
	def edit_selected(self, selection):
		model, iter = selection.get_selected()
		if not iter:
			rox.alert(_('You need to select a type to edit first'))
		else:
			type_name = model.get_value(iter, 0)
			self.edit(type_name)
	
	def show_type(self, type_name):
		model = self.mime_model
		iter = model.get_iter_first()
		while iter:
			name = model.get_value(iter, 0)
			if name == type_name:
				path = model.get_path(iter)
				self.view.set_cursor(path, None, False)
				self.edit(type_name)
				return
			iter = model.iter_next(iter)

class EditBox(rox.Dialog):
	def __init__(self, type_name):
		rox.Dialog.__init__(self)
		self.set_has_separator(False)
		self.set_default_size(-1, 400)
		self.mime_type = type_name

		self.set_title('MIME-Editor: %s' % type_name)

		swin = g.ScrolledWindow()
		swin.set_border_width(4)
		swin.set_shadow_type(g.SHADOW_IN)
		self.vbox.pack_start(swin, True, True, 0)

		self.model = g.TreeStore(str, object, g.gdk.Color)
		view = g.TreeView(self.model)
		self.view = view
		swin.add(view)

		cell = g.CellRendererText()
		column = g.TreeViewColumn(type_name, cell, text = 0, foreground_gdk = 2)
		view.append_column(column)

		swin.set_policy(g.POLICY_NEVER, g.POLICY_AUTOMATIC)
		self.update()

		self.add_button(g.STOCK_ADD, ADD)
		self.add_button(g.STOCK_DELETE, DELETE)
		self.add_button(g.STOCK_PROPERTIES, EDIT)
		self.add_button(g.STOCK_CLOSE, g.RESPONSE_CANCEL)
		def response(w, resp):
			if resp == ADD:
				self.add_field(view)
			elif resp == DELETE:
				self.delete_field(view)
			elif resp == EDIT:
				self.edit_field(view)
			else:
				self.destroy()
		self.connect('response', response)

		def changed(selection):
			model, iter = selection.get_selected()
			on_field = False
			if iter != None:
				path = model.get_path(iter)
				on_field = len(path) > 1
			self.set_response_sensitive(ADD, iter != None)
			self.set_response_sensitive(EDIT, on_field)
			self.set_response_sensitive(DELETE, on_field)
		selection = view.get_selection()
		selection.connect('changed', changed)
		def may_change(path):
			"Prevent selecting greyed-out lines"
			iter = self.model.get_iter(path)
			if self.model.get_value(iter, 2):
				return False
			return True
		selection.set_select_function(may_change)
		changed(selection)

		def activate(view, path, column):
			iter = self.model.get_iter(path)
			if self.model.get_value(iter, 2):
				return	# Shaded
			field = self.model.get_value(iter, 1)
			if len(path) == 1:
				self.add_new_field(field)
			else:
				field.edit()
		view.connect('row-activated', activate)

		self.set_default_response(g.RESPONSE_CANCEL)

		label = g.Label(_("Shaded entries are provided by system packages and cannot "
				"be edited or deleted. However, any information you add will "
				"take precedence over them."))
		label.set_line_wrap(True)
		self.vbox.pack_start(label, False, True, 0)

		self.vbox.show_all()

	def update(self):
		grey = self.get_style().fg[g.STATE_INSENSITIVE]
		def build(parent, fields):
			fields.sort(lambda a, b: cmp(str(a), str(b)))
			for field in fields:
				iter = self.model.append(parent)
				if field.can_edit():
					self.model.set(iter, 0, str(field), 1, field)
				else:
					self.model.set(iter, 0, str(field), 1, field, 2, grey)
				build(iter, field.get_sub_fields())
		t = type.get_type(self.mime_type)
		self.model.clear()
		for aspect, getter, klass in [(_('Name matching'), t.get_globs, fields.Glob),
					     (_('Contents matching'), t.get_magic, fields.Magic),
					     (_('XML namespace matching'), t.get_xml, fields.XML),
					     (_('Directory structure matching'), t.get_tree_magic, fields.TreeMagic),
					     (_('Others'), t.get_others, None),
					     (_('Descriptions'), t.get_comments, fields.Comment)]:
			iter = self.model.append(None)
			if klass:
				self.model.set(iter, 0, aspect, 1, klass)
			else:
				self.model.set(iter, 0, aspect, 1, klass, 2, grey)
			build(iter, getter())
		self.view.expand_all()
	
	def add_new_field(self, klass):
		if not klass: return
		new = klass(type.get_type(self.mime_type))
		new.edit()
	
	def add_field(self, view):
		model, iter = view.get_selection().get_selected()
		if not iter:
			rox.alert(_('You need to select a group, so I know what kind of thing to add'))
			return
		path = model.get_path(iter)
		field = model.get_value(model.get_iter(path), 1)
		if isinstance(field, fields.Field) and hasattr(field, 'add_sub_field'):
			field.add_sub_field()	# For magic
		else:
			field = model.get_value(model.get_iter(path[:1]), 1)
			self.add_new_field(field)
	
	def delete_field(self, view):
		model, iter = view.get_selection().get_selected()
		if not iter:
			rox.alert(_("Nothing selected!"))
		field = model.get_value(iter, 1)
		assert field
		field.delete()
	
	def edit_field(self, view):
		model, iter = view.get_selection().get_selected()
		if not iter:
			rox.alert(_("Nothing selected!"))
		field = model.get_value(iter, 1)
		assert field
		field.edit()

class NewType(rox.Dialog):
	def __init__(self):
		rox.Dialog.__init__(self)
		self.set_position(g.WIN_POS_MOUSE)
		self.set_title(_('Add new MIME type'))
		self.set_has_separator(False)

		self.add_button(g.STOCK_CANCEL, g.RESPONSE_CANCEL)
		self.add_button(g.STOCK_ADD, g.RESPONSE_OK)
		self.set_default_response(g.RESPONSE_OK)

		tips = g.Tooltips()
		self.tips = tips	# (pygtk refcount bug again)

		vbox = g.VBox(False, 4)
		self.vbox.pack_start(vbox, True, True, 0)
		vbox.set_border_width(4)

		def pair(label, widget):
			hbox = g.HBox(False, 0)
			vbox.pack_start(hbox, False, True, 0)
			hbox.pack_start(g.Label(label + ': '), False, True, 0)
			hbox.pack_start(widget, True, True, 0)

		combo = g.Combo()
		combo.set_popdown_strings(["text", "application", "image", "audio",
					   "video", "message", "model"])
		pair(_('Media type'), combo)
		tips.set_tip(combo.entry, _("Choose what sort of files this type describes.\n"
			     "Note that the 'text' group should only be used "
			     "for types that can be viewed in a normal text "
			     "editor. For example, HTML is text/html, "
			     "but Word documents are in the 'application' group."))

		entry = g.Entry()
		entry.set_text('x-my-type')
		pair(_('Subtype'), entry)
		entry.set_activates_default(True)
		tips.set_tip(entry, _("Note: unoffical types should start with 'x-'"))

		self.comment = g.Entry()
		pair(_('Description'), self.comment)
		tips.set_tip(self.comment, _("A brief description of this type. For example:\n"
					   "OpenOffice Spreadsheet"))
		self.comment.set_activates_default(True)

		self.glob = g.Entry()
		self.glob.set_text('*.myapp')
		pair(_('For files named'), self.glob)
		self.glob.set_activates_default(True)
		tips.set_tip(self.glob,
				_("A rule to spot which files should have this type. Use '*' to "
				"mean 'anything'. For example, to match anything ending in '.app' "
				"use:\n"
				"*.app\n"
				"You can leave this blank, or add extra patterns later."))

		self.vbox.pack_start(g.Label(_('Hold the pointer over any field\n'
					     'for more information.')), False, True, 2)

		self.vbox.show_all()
					
		def response(w, resp):
			if resp == int(g.RESPONSE_OK):
				type_name = combo.entry.get_text() + '/' + entry.get_text()
				if type_name.count('/') != 1 or type_name.count(' '):
					rox.alert(_("Invalid MIME type name '%s'") % type_name)
					return
				if type_name in type.types:
					rox.alert(_("Type '%s' already exists!") % type_name)
					import __main__
					__main__.box.show_type(type_name)
					return
				if not self.comment.get_text():
					rox.alert(_("Please provide a description for this type"))
					return
				type.add_type(type_name, self.comment.get_text(),
						self.glob.get_text())
			self.destroy()
		self.connect('response', response)
