From 2f8096b4c9a4b0a70e4e4a0236bcc7500422d705 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Tue, 11 Mar 2003 12:23:54 +0000 Subject: [PATCH] Split code into separate files. Don't show message box on updates if everything worked. Fixed some bugs. git-svn-id: https://rox.svn.sourceforge.net/svnroot/rox/trunk/MIME-Editor@2555 66de3db3-b00d-0410-b41b-f4738ad19bea --- editor.py | 15 +- fields.py | 334 +++++++++++++++++++++ override.py | 59 ++++ type.py | 942 +++++++++++++++++------------------------------------------- 4 files changed, 672 insertions(+), 678 deletions(-) create mode 100644 fields.py create mode 100644 override.py rewrite type.py (63%) diff --git a/editor.py b/editor.py index c349e3c..41666d3 100644 --- a/editor.py +++ b/editor.py @@ -2,6 +2,7 @@ import rox from rox import g import os import type +import fields edit_boxes = {} @@ -228,23 +229,23 @@ class EditBox(rox.Dialog): 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.user: + 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()) - grey = self.get_style().fg[g.STATE_INSENSITIVE] t = type.get_type(self.mime_type) self.model.clear() - for aspect, getter, klass in [('Name matching', t.get_globs, type.Glob), - ('Contents matching', t.get_magic, type.Magic), - ('XML namespace matching', t.get_xml, type.XML), + 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), ('Others', t.get_others, None), - ('Descriptions', t.get_comments, type.Comment)]: + ('Descriptions', t.get_comments, fields.Comment)]: iter = self.model.append(None) if klass: self.model.set(iter, 0, aspect, 1, klass) @@ -265,7 +266,7 @@ class EditBox(rox.Dialog): return path = model.get_path(iter) field = model.get_value(model.get_iter(path), 1) - if hasattr(field, 'add_sub_field'): + 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) diff --git a/fields.py b/fields.py new file mode 100644 index 0000000..1194a95 --- /dev/null +++ b/fields.py @@ -0,0 +1,334 @@ +import rox +from rox import g +from xml.dom import XML_NAMESPACE, Node +import type +import override +from override import FREE_NS + +class Invalid(Exception): + pass + +class Field: + "MIME_Type.get_* functions return a list of these." + def __init__(self, type, item = None): + self.type = type + if item is None: + self.item = self.get_blank_item() + self.user = True + self.new = True + else: + if len(item) == 2: + self.item = item[0] + else: + self.item = item[:-1] + self.user = item[-1] + self.new = False + assert self.user in (True, False) + + def can_edit(self): + return self.user + + def get_blank_item(self): + raise Exception("Can't create fields of this type yet") + + def __str__(self): + return "<%s>" % self.item + + def get_sub_fields(self): + return [] + + def add_subtree(self, model, iter, grey): + return + + def delete(self): + rox.alert('TODO') + + def edit(self): + "Returns True on success." + if not hasattr(self, 'add_edit_widgets'): + rox.alert('Sorry, MIME-Editor does not support editing fields of this type') + return + box = rox.Dialog() + self.box = box + box.set_has_separator(False) + vbox = g.VBox(False, 4) + vbox.set_border_width(4) + box.vbox.pack_start(vbox, True, True, 0) + self.add_edit_widgets(vbox) + box.add_button(g.STOCK_CANCEL, g.RESPONSE_CANCEL) + box.add_button(g.STOCK_OK, g.RESPONSE_OK) + box.set_default_response(g.RESPONSE_OK) + box.vbox.show_all() + while 1: + try: + resp = box.run() + if resp == g.RESPONSE_OK: + try: + self.commit_edit(box) + except Invalid, e: + rox.alert(str(e)) + continue + box.destroy() + return True + except: + rox.report_exception() + box.destroy() + return False + + def commit_edit(self, box): + raise Invalid('TODO: Not implemented') + + def delete_from_node(self, type_node): + raise Invalid("TODO: Can't delete/edit fields of this type yet") + + def add_to_node(self, node): + raise Invalid("TODO: Can't add/edit fields of this type yet") + + def delete(self): + assert self.user + doc, node = override.get_override_type(self.type.get_name()) + self.delete_from_node(node) + override.write_override(doc) + + def commit_edit(self, box): + doc, node = override.get_override_type(self.type.get_name()) + if not self.new: + self.delete_from_node(node) + self.add_to_node(node) + override.write_override(doc) + + +class Comment(Field): + def get_blank_item(self): + return (None, 'Unknown format') + + def __str__(self): + lang, data = self.item + if lang: + lang = '[' + lang + '] ' + else: + lang = '(default) ' + return lang + data + + def add_edit_widgets(self, vbox): + vbox.pack_start( + g.Label("Enter a brief description of the type, eg 'HTML Page'.\n" + "Leave the language blank unless this is a translation, in \n" + "which case enter the country code (eg 'fr')."), + False, True, 0) + hbox = g.HBox(False, 0) + vbox.pack_start(hbox, False, True, 0) + + self.lang = g.Entry() + self.lang.set_text(self.item[0] or '') + hbox.pack_start(g.Label('Language'), False, True, 0) + hbox.pack_start(self.lang, True, True, 0) + self.lang.set_activates_default(True) + + hbox = g.HBox(False, 0) + vbox.pack_start(hbox, False, True, 0) + + self.entry = g.Entry() + self.entry.set_text(self.item[1]) + hbox.pack_start(g.Label('Description'), False, True, 0) + hbox.pack_start(self.entry, True, True, 0) + self.entry.grab_focus() + self.entry.set_activates_default(True) + + def delete_from_node(self, node): + lang = self.item[0] or '' + for x in node.childNodes: + if x.nodeType != Node.ELEMENT_NODE: continue + if x.localName == 'comment' and x.namespaceURI == FREE_NS: + if (x.getAttributeNS(XML_NAMESPACE, 'lang') or '') == lang and \ + data(x) == self.item[1]: + x.parentNode.removeChild(x) + break + else: + raise Exception("Can't find this comment in Override.xml!") + + def add_to_node(self, node): + new_lang = self.lang.get_text() + new = self.entry.get_text() + if not new: + raise Invalid("Comment can't be empty") + comment = node.ownerDocument.createElementNS(FREE_NS, 'comment') + if new_lang: + comment.setAttributeNS(XML_NAMESPACE, 'xml:lang', new_lang) + data = node.ownerDocument.createTextNode(new) + comment.appendChild(data) + node.appendChild(comment) + +class Glob(Field): + def get_blank_item(self): + return '' + + def __str__(self): + return "Match '%s'" % self.item + + def add_edit_widgets(self, vbox): + vbox.pack_start( + g.Label("Enter a glob pattern which matches files of this type.\n" + "Special characters are:\n" + "? - any one character\n" + "* - zero or more characters\n" + "[abc] - any character between the brackets\n" + "Example: '*.html' matches all files ending in '.html'"), + False, True, 0) + self.entry = g.Entry() + self.entry.set_text(self.item) + vbox.pack_start(self.entry, False, True, 0) + self.entry.set_activates_default(True) + + def delete_from_node(self, node): + for x in node.childNodes: + if x.nodeType != Node.ELEMENT_NODE: continue + if x.localName == 'glob' and x.namespaceURI == FREE_NS: + if x.getAttributeNS(None, 'pattern') == self.item: + x.parentNode.removeChild(x) + break + else: + raise Exception("Can't find this pattern in Override.xml!") + + def add_to_node(self, node): + new = self.entry.get_text() + if not new: + raise Invalid("Pattern can't be empty") + glob = node.ownerDocument.createElementNS(FREE_NS, 'glob') + glob.setAttributeNS(None, 'pattern', new) + node.appendChild(glob) + +class Magic(Field): + def __init__(self, type, item = None): + if item: + prio, match, user = item + if prio is None or prio is '': + prio = 50 + else: + prio = int(prio) + item = (int(prio), match, user) + Field.__init__(self, type, item) + + def get_blank_item(self): + return (50, type.Match(None, True)) + + def __str__(self): + prio, match = self.item + return "Match with priority %d" % prio + + def add_sub_field(self): + rox.alert('Not implemented') + + def get_sub_fields(self): + return Match(self, self.item[1]).get_sub_fields() + + def add_edit_widgets(self, vbox): + vbox.pack_start( + g.Label("The priority is from 0 (low) to 100 (high).\n" + "High priority matches take precedence over low ones."), + False, True, 0) + prio = self.item[0] + self.adj = g.Adjustment(prio, lower = 0, upper = 100, step_incr = 1) + spinner = g.SpinButton(self.adj, 1, 0) + vbox.pack_start(spinner, False, True, 0) + spinner.set_activates_default(True) + + def add_to_node(self, node): + new = int(self.adj.value) + xmlroot = node.ownerDocument.createElementNS(FREE_NS, 'magic') + if new != 50: + xmlroot.setAttributeNS(None, 'priority', str(new)) + node.appendChild(xmlroot) + + def equals_node(self, node): + prio, match = self.item + node_prio = node.getAttributeNS(None, 'priority') + if node_prio is None or node_prio is '': + node_prio = 50 + else: + node_prio = int(node_prio) + if node_prio != prio: + return False + return match.equals_node(node) + + def delete_from_node(self, node): + for x in node.childNodes: + if x.nodeType != Node.ELEMENT_NODE: continue + if x.localName == 'magic' and x.namespaceURI == FREE_NS: + if self.equals_node(x): + x.parentNode.removeChild(x) + break + else: + raise Exception("Can't find this magic in Override.xml!") + +class Match(Field): + def __init__(self, magic, parent): + Field.__init__(self, magic.type, (parent, parent.user)) + self.magic = magic + + def get_sub_fields(self): + return [Match(self.magic, m) for m in self.item.matches] + + def __str__(self): + m = self.item + text = '%s at %s = %s' % (m.type, m.offset, m.value) + if m.mask: + text += ' masked with ' + m.mask + return text + +class XML(Field): + def get_blank_item(self): + return ('http://example.com', 'documentElement') + + def __str__(self): + return "<%s> with namespace '%s'" % (self.item[1], self.item[0]) + + def add_edit_widgets(self, vbox): + vbox.pack_start( + g.Label("Enter the namespace URI and element name of the root element."), + False, True, 0) + hbox = g.HBox(False, 0) + vbox.pack_start(hbox, False, True, 0) + + self.ns = g.Entry() + self.ns.set_text(self.item[0]) + hbox.pack_start(g.Label('Namespace'), False, True, 0) + hbox.pack_start(self.ns, True, True, 0) + self.ns.set_activates_default(True) + + hbox = g.HBox(False, 0) + vbox.pack_start(hbox, False, True, 0) + + self.entry = g.Entry() + self.entry.set_text(self.item[1]) + hbox.pack_start(g.Label('Local Name'), False, True, 0) + hbox.pack_start(self.entry, True, True, 0) + self.entry.set_activates_default(True) + + def delete_from_node(self, node): + ns = self.item[0] + localName = self.item[1] + for x in node.childNodes: + if x.nodeType != Node.ELEMENT_NODE: continue + if x.localName == 'root-XML' and x.namespaceURI == FREE_NS: + if (x.getAttributeNS(None, 'namespaceURI') == ns and \ + x.getAttributeNS(None, 'localName') == localName): + x.parentNode.removeChild(x) + break + else: + raise Exception("Can't find this root-XML in Override.xml!") + + def add_to_node(self, node): + new_ns = self.ns.get_text() + new = self.entry.get_text() + if (not new) and (not new_ns): + raise Invalid("Name and namespace can't both be empty") + if ' ' in new or ' ' in new_ns: + raise Invalid("Name and namespace can't contain spaces") + xmlroot = node.ownerDocument.createElementNS(FREE_NS, 'root-XML') + xmlroot.setAttributeNS(None, 'namespaceURI', new_ns) + xmlroot.setAttributeNS(None, 'localName', new) + node.appendChild(xmlroot) + +class Other(Field): + def can_edit(self): return False diff --git a/override.py b/override.py new file mode 100644 index 0000000..de3c8ea --- /dev/null +++ b/override.py @@ -0,0 +1,59 @@ +import os +import rox +from xml.dom import Node + +FREE_NS='http://www.freedesktop.org/standards/shared-mime-info' + +home_mime = os.path.join(os.environ['HOME'], '.mime') +user_override = os.path.join(home_mime, 'packages', 'Override.xml') + +def get_override(): + from xml.dom import minidom + if os.path.exists(user_override): + doc = minidom.parse(user_override) + else: + doc = minidom.Document() + node = doc.createElementNS(FREE_NS, 'mime-info') + doc.appendChild(node) + node.setAttributeNS(XMLNS_NAMESPACE, 'xmlns', FREE_NS) + return doc + +def get_override_type(type_name): + doc = get_override() + root = doc.documentElement + for c in root.childNodes: + if c.nodeType != Node.ELEMENT_NODE: continue + if c.localName == 'mime-type' and c.namespaceURI == FREE_NS: + if c.getAttributeNS(None, 'type') == type_name: + return doc, c + node = doc.createElementNS(FREE_NS, 'mime-type') + node.setAttributeNS(None, 'type', type_name) + root.appendChild(node) + return doc, node + +def write_override(doc): + home_packages = os.path.join(home_mime, 'packages') + if not os.path.isdir(home_packages): + os.makedirs(home_packages) + path = os.path.join(home_packages, 'Override.xml.new') + doc.writexml(file(path, 'w')) + os.rename(path, path[:-4]) + r, w = os.pipe() + child = os.fork() + if child == 0: + # Child + try: + os.close(r) + os.dup2(w, 1) + os.dup2(w, 2) + os.execlp('update-mime-database', 'update-mime-database', home_mime) + finally: + os._exit(1) + os.close(w) + message = os.fdopen(r, 'r').read() + pid, status = os.waitpid(child, 0) + if status or message.count('\n') != 3: + rox.info(message) + + import __main__ + __main__.box.update() diff --git a/type.py b/type.py dissimilarity index 63% index 4a4b177..037475f 100644 --- a/type.py +++ b/type.py @@ -1,671 +1,271 @@ -import os -import rox -from rox import g -from xml.parsers import expat -from xml.dom import XML_NAMESPACE, Node -import copy - -types = {} - -home_mime = os.path.join(os.environ['HOME'], '.mime') -user_override = os.path.join(home_mime, 'packages', 'Override.xml') - -FREE_NS='http://www.freedesktop.org/standards/shared-mime-info' - -def data(node): - return ''.join([text.nodeValue for text in node.childNodes - if text.nodeType == Node.TEXT_NODE]) - -class Invalid(Exception): - pass - -def get_type(name): - if name not in types: - types[name] = MIME_Type(name) - return types[name] - -class MIME_Type: - def __init__(self, name): - assert name not in types - self.media, self.subtype = name.split('/') - - self.comments = [] - self.globs = [] - self.magic = [] - self.xml = [] - self.others = [] - - def add_comment(self, lang, comment, user): - self.comments.append((lang, comment, user)) - - def add_xml(self, uri, name, user): - self.xml.append((uri, name, user)) - - def add_magic(self, prio, root, user): - self.magic.append((prio, root, user)) - - def add_other(self, element, user): - self.others.append((element, user)) - - def add_glob(self, pattern, user): - self.globs.append((pattern, user)) - - def get_comment(self): - best = None - for lang, comment, user in self.comments: - if not lang: - return comment - best = comment - return best or self.get_name() - - def get_name(self): - return self.media + '/' + self.subtype - - def make(self, klass, list): - return [klass(self, item) for item in list] - - def get_comments(self): return self.make(Comment, self.comments) - def get_globs(self): return self.make(Glob, self.globs) - def get_magic(self): return self.make(Magic, self.magic) - def get_xml(self): return self.make(XML, self.xml) - def get_others(self): return self.make(Other, self.others) - - def remove_user(self): - for list in ['comments', 'globs', 'magic', 'xml', 'others']: - setattr(self, list, - [x for x in getattr(self, list) if not x[-1]]) - -class Field: - "MIME_Type.get_* functions return a list of these." - def __init__(self, type, item = None): - self.type = type - if item is None: - self.item = self.get_blank_item() - self.user = True - self.new = True - else: - if len(item) == 2: - self.item = item[0] - else: - self.item = item[:-1] - self.user = item[-1] - self.new = False - assert self.user in (True, False) - - def get_blank_item(self): - raise Exception("Can't create fields of this type yet") - - def __str__(self): - return "<%s>" % self.item - - def get_sub_fields(self): - return [] - - def add_subtree(self, model, iter, grey): - return - - def __cmp__(self, b): - # Note: returns 1 (different) if same and both users set - return cmp(str(self), str(b)) or self.user or b.user - - def delete(self): - rox.alert('TODO') - - def edit(self): - "Returns True on success." - if not hasattr(self, 'add_edit_widgets'): - rox.alert('Sorry, MIME-Editor does not support editing fields of this type') - return - box = rox.Dialog() - self.box = box - box.set_has_separator(False) - vbox = g.VBox(False, 4) - vbox.set_border_width(4) - box.vbox.pack_start(vbox, True, True, 0) - self.add_edit_widgets(vbox) - box.add_button(g.STOCK_CANCEL, g.RESPONSE_CANCEL) - box.add_button(g.STOCK_OK, g.RESPONSE_OK) - box.set_default_response(g.RESPONSE_OK) - box.vbox.show_all() - while 1: - try: - resp = box.run() - if resp == g.RESPONSE_OK: - try: - self.commit_edit(box) - except Invalid, e: - rox.alert(str(e)) - continue - box.destroy() - return True - except: - rox.report_exception() - box.destroy() - return False - - def commit_edit(self, box): - raise Invalid('TODO: Not implemented') - - def delete_from_node(self, type_node): - raise Invalid("TODO: Can't delete/edit fields of this type yet") - - def add_to_node(self, node): - raise Invalid("TODO: Can't add/edit fields of this type yet") - - def delete(self): - assert self.user - doc, node = get_override_type(self.type.get_name()) - self.delete_from_node(node) - write_override(doc) - - def commit_edit(self, box): - doc, node = get_override_type(self.type.get_name()) - if not self.new: - self.delete_from_node(node) - self.add_to_node(node) - write_override(doc) - - -class Comment(Field): - def get_blank_item(self): - return (None, 'Unknown format') - - def __str__(self): - lang, data = self.item - if lang: - lang = '[' + lang + '] ' - else: - lang = '(default) ' - return lang + data - - def add_edit_widgets(self, vbox): - vbox.pack_start( - g.Label("Enter a brief description of the type, eg 'HTML Page'.\n" - "Leave the language blank unless this is a translation, in \n" - "which case enter the country code (eg 'fr')."), - False, True, 0) - hbox = g.HBox(False, 0) - vbox.pack_start(hbox, False, True, 0) - - self.lang = g.Entry() - self.lang.set_text(self.item[0] or '') - hbox.pack_start(g.Label('Language'), False, True, 0) - hbox.pack_start(self.lang, True, True, 0) - self.lang.set_activates_default(True) - - hbox = g.HBox(False, 0) - vbox.pack_start(hbox, False, True, 0) - - self.entry = g.Entry() - self.entry.set_text(self.item[1]) - hbox.pack_start(g.Label('Description'), False, True, 0) - hbox.pack_start(self.entry, True, True, 0) - self.entry.grab_focus() - self.entry.set_activates_default(True) - - def delete_from_node(self, node): - lang = self.item[0] or '' - for x in node.childNodes: - if x.nodeType != Node.ELEMENT_NODE: continue - if x.localName == 'comment' and x.namespaceURI == FREE_NS: - if (x.getAttributeNS(XML_NAMESPACE, 'lang') or '') == lang and \ - data(x) == self.item[1]: - x.parentNode.removeChild(x) - break - else: - raise Exception("Can't find this comment in Override.xml!") - - def add_to_node(self, node): - new_lang = self.lang.get_text() - new = self.entry.get_text() - if not new: - raise Invalid("Comment can't be empty") - comment = node.ownerDocument.createElementNS(FREE_NS, 'comment') - if new_lang: - comment.setAttributeNS(XML_NAMESPACE, 'xml:lang', new_lang) - data = node.ownerDocument.createTextNode(new) - comment.appendChild(data) - node.appendChild(comment) - -class Glob(Field): - def get_blank_item(self): - return '' - - def __str__(self): - return "Match '%s'" % self.item - - def add_edit_widgets(self, vbox): - vbox.pack_start( - g.Label("Enter a glob pattern which matches files of this type.\n" - "Special characters are:\n" - "? - any one character\n" - "* - zero or more characters\n" - "[abc] - any character between the brackets\n" - "Example: '*.html' matches all files ending in '.html'"), - False, True, 0) - self.entry = g.Entry() - self.entry.set_text(self.item) - vbox.pack_start(self.entry, False, True, 0) - self.entry.set_activates_default(True) - - def delete_from_node(self, node): - for x in node.childNodes: - if x.nodeType != Node.ELEMENT_NODE: continue - if x.localName == 'glob' and x.namespaceURI == FREE_NS: - if x.getAttributeNS(None, 'pattern') == self.item: - x.parentNode.removeChild(x) - break - else: - raise Exception("Can't find this pattern in Override.xml!") - - def add_to_node(self, node): - new = self.entry.get_text() - if not new: - raise Invalid("Pattern can't be empty") - glob = node.ownerDocument.createElementNS(FREE_NS, 'glob') - glob.setAttributeNS(None, 'pattern', new) - node.appendChild(glob) - -class Magic(Field): - def __init__(self, type, item = None): - if item: - prio, match, user = item - if prio is None or prio is '': - prio = 50 - else: - prio = int(prio) - item = (int(prio), match, user) - Field.__init__(self, type, item) - - def get_blank_item(self): - return (50, Match(None, True)) - - def __str__(self): - prio, match = self.item - return "Match with priority %d" % prio - - def add_sub_field(self): - rox.alert('Not implemented') - - def get_sub_fields(self): - return MatchField(self, self.item[1]).get_sub_fields() - - def add_edit_widgets(self, vbox): - vbox.pack_start( - g.Label("The priority is from 0 (low) to 100 (high).\n" - "High priority matches take precedence over low ones."), - False, True, 0) - prio = self.item[0] - self.adj = g.Adjustment(prio, lower = 0, upper = 100, step_incr = 1) - spinner = g.SpinButton(self.adj, 1, 0) - vbox.pack_start(spinner, False, True, 0) - spinner.set_activates_default(True) - - def __cmp__(self, b): - ret = Field.__cmp__(self, b) - if ret: return ret - return cmp(self.item[1], b.item[1]) - - def add_to_node(self, node): - new = int(self.adj.value) - xmlroot = node.ownerDocument.createElementNS(FREE_NS, 'magic') - if new != 50: - xmlroot.setAttributeNS(None, 'priority', str(new)) - node.appendChild(xmlroot) - - def equals_node(self, node): - prio, match = self.item - node_prio = node.getAttributeNS(None, 'priority') - if node_prio is None or node_prio is '': - node_prio = 50 - else: - node_prio = int(node_prio) - if node_prio != prio: - return False - return match.equals_node(node) - - def delete_from_node(self, node): - for x in node.childNodes: - if x.nodeType != Node.ELEMENT_NODE: continue - if x.localName == 'magic' and x.namespaceURI == FREE_NS: - if self.equals_node(x): - x.parentNode.removeChild(x) - break - else: - raise Exception("Can't find this magic in Override.xml!") - -class MatchField(Field): - def __init__(self, magic, parent): - Field.__init__(self, magic.type, (parent, parent.user)) - self.magic = magic - - def get_sub_fields(self): - return [MatchField(self.magic, m) for m in self.item.matches] - - def __str__(self): - m = self.item - text = '%s at %s = %s' % (m.type, m.offset, m.value) - if m.mask: - text += ' masked with ' + m.mask - return text - -class XML(Field): - def get_blank_item(self): - return ('http://example.com', 'documentElement') - - def __str__(self): - return "<%s> with namespace '%s'" % (self.item[1], self.item[0]) - - def add_edit_widgets(self, vbox): - vbox.pack_start( - g.Label("Enter the namespace URI and element name of the root element."), - False, True, 0) - hbox = g.HBox(False, 0) - vbox.pack_start(hbox, False, True, 0) - - self.ns = g.Entry() - self.ns.set_text(self.item[0]) - hbox.pack_start(g.Label('Namespace'), False, True, 0) - hbox.pack_start(self.ns, True, True, 0) - self.ns.set_activates_default(True) - - hbox = g.HBox(False, 0) - vbox.pack_start(hbox, False, True, 0) - - self.entry = g.Entry() - self.entry.set_text(self.item[1]) - hbox.pack_start(g.Label('Local Name'), False, True, 0) - hbox.pack_start(self.entry, True, True, 0) - self.entry.set_activates_default(True) - - def delete_from_node(self, node): - ns = self.item[0] - localName = self.item[1] - for x in node.childNodes: - if x.nodeType != Node.ELEMENT_NODE: continue - if x.localName == 'root-XML' and x.namespaceURI == FREE_NS: - if (x.getAttributeNS(None, 'namespaceURI') == ns and \ - x.getAttributeNS(None, 'localName') == localName): - x.parentNode.removeChild(x) - break - else: - raise Exception("Can't find this root-XML in Override.xml!") - - def add_to_node(self, node): - new_ns = self.ns.get_text() - new = self.entry.get_text() - if (not new) and (not new_ns): - raise Invalid("Name and namespace can't both be empty") - if ' ' in new or ' ' in new_ns: - raise Invalid("Name and namespace can't contain spaces") - xmlroot = node.ownerDocument.createElementNS(FREE_NS, 'root-XML') - xmlroot.setAttributeNS(None, 'namespaceURI', new_ns) - xmlroot.setAttributeNS(None, 'localName', new) - node.appendChild(xmlroot) - -class Other(Field): pass - -class FieldParser: - def __init__(self, type, attrs): - self.type = type - - def start(self, element, attrs): pass - def data(self, data): pass - def end(self): pass - -class CommentParser(FieldParser): - def __init__(self, type, attrs, user): - FieldParser.__init__(self, type, attrs) - self.lang = attrs.get(XML_NAMESPACE + ' lang', None) - self.comment = '' - self.user = user - - def data(self, data): - self.comment += data - - def end(self): - self.type.add_comment(self.lang, self.comment, self.user) - -class Match: - def __init__(self, parent, user): - self.parent = parent - self.matches = [] - self.user = user - - def __cmp__(self, b): - def child_cmp(): - for x, y in zip(self.matches, b.matches): - c = cmp(x, y) - if c: return c - return 0 - - if not self.parent: - return child_cmp() - - return cmp(self.type, b.type) or cmp(self.offset, b.offset) or \ - cmp(self.value, b.value) or cmp(self.mask, b.mask) or \ - child_cmp() - - def edit(self): - rox.alert("Editing of matches isn't currently supported") - - def equals_node(self, node): - if self.parent: - if node.localName != 'match' or node.namespaceURI != FREE_NS: - return False - def get(x, d): - a = node.getAttributeNS(None, x) - if a is None: return d - return a - offset = get('offset', '?') - type = get('type', '?') - value = get('value', '?') - mask = get('mask', None) - if self.offset != offset or self.type != type or \ - self.value != value or self.mask != mask: - return False - - kids = [] - for k in node.childNodes: - if k.nodeType != Node.ELEMENT_NODE: continue - if k.localName != 'match' or k.namespaceURI != FREE_NS: continue - kids.append(k) - - if len(self.matches) != len(kids): - return False - - for m, k in zip(self.matches, kids): - if not m.equals_node(k): return False - - return True - -class MagicParser(FieldParser): - def __init__(self, type, attrs, user): - FieldParser.__init__(self, type, attrs) - self.prio = int(attrs.get('priority', 50)) - self.match = Match(None, user) - self.user = user - - def start(self, element, attrs): - new = Match(self.match, self.user) - new.offset = attrs.get('offset', '?') - new.type = attrs.get('type', '?') - new.value = attrs.get('value', '?') - new.mask = attrs.get('mask', None) - self.match.matches.append(new) - self.match = new - - def end(self): - if self.match.parent: - self.match = self.match.parent - else: - self.type.add_magic(self.prio, self.match, self.user) - -class Scanner: - def __init__(self): - self.level = 0 - self.type = None - self.handler = None - - def parse(self, path, user): - parser = expat.ParserCreate(namespace_separator = ' ') - parser.StartElementHandler = self.start - parser.EndElementHandler = self.end - parser.CharacterDataHandler = self.data - self.user = user - parser.ParseFile(file(path)) - - def start(self, element, attrs): - self.level += 1 - if self.level == 1: - assert element == FREE_NS + ' mime-info' - elif self.level == 2: - assert element == FREE_NS + ' mime-type' - self.type = get_type(attrs['type']) - elif self.level == 3: - if element == FREE_NS + ' comment': - self.handler = CommentParser(self.type, attrs, - self.user) - elif element == FREE_NS + ' glob': - self.type.add_glob(attrs['pattern'], self.user) - elif element == FREE_NS + ' magic': - self.handler = MagicParser(self.type, attrs, - self.user) - elif element == FREE_NS + ' root-XML': - self.type.add_xml(attrs['namespaceURI'], - attrs['localName'], - self.user) - else: - self.type.add_other(element, self.user) - else: - assert self.handler - self.handler.start(element, attrs) - - def end(self, element): - if self.handler: - self.handler.end() - self.level -=1 - if self.level == 1: - self.type = None - elif self.level == 2: - self.handler = None - - def data(self, data): - if self.handler: - self.handler.data(data) - -def scan_file(path, user): - if not path.endswith('.xml'): return - if not os.path.exists(path): - return - scanner = Scanner() - try: - scanner.parse(path, user) - except: - rox.report_exception() - -def get_override(): - from xml.dom import minidom - if os.path.exists(user_override): - doc = minidom.parse(user_override) - else: - doc = minidom.Document() - node = doc.createElementNS(FREE_NS, 'mime-info') - doc.appendChild(node) - node.setAttributeNS(XMLNS_NAMESPACE, 'xmlns', FREE_NS) - return doc - -def get_override_type(type_name): - doc = get_override() - root = doc.documentElement - for c in root.childNodes: - if c.nodeType != Node.ELEMENT_NODE: continue - if c.localName == 'mime-type' and c.namespaceURI == FREE_NS: - if c.getAttributeNS(None, 'type') == type_name: - return doc, c - node = doc.createElementNS(FREE_NS, 'mime-type') - node.setAttributeNS(None, 'type', type_name) - root.appendChild(node) - return doc, node - -def write_override(doc): - home_packages = os.path.join(home_mime, 'packages') - if not os.path.isdir(home_packages): - os.makedirs(home_packages) - path = os.path.join(home_packages, 'Override.xml.new') - doc.writexml(file(path, 'w')) - os.rename(path, path[:-4]) - r, w = os.pipe() - child = os.fork() - if child == 0: - # Child - try: - os.close(r) - os.dup2(w, 1) - os.dup2(w, 2) - os.execlp('update-mime-database', 'update-mime-database', home_mime) - finally: - os._exit(1) - os.close(w) - rox.info(os.fdopen(r, 'r').read()) - os.waitpid(child, 0) - - import __main__ - __main__.box.update() - -def add_type(name): - doc = get_override() - - root = doc.documentElement - type_node = doc.createElementNS(FREE_NS, 'mime-type') - root.appendChild(type_node) - - type_node.setAttributeNS(None, 'type', name) - - write_override(doc) - import __main__ - __main__.box.show_type(name) - -def delete_type(name): - doc = get_override() - removed = False - for c in doc.documentElement.childNodes: - if c.nodeType != Node.ELEMENT_NODE: continue - if c.nodeName != 'mime-type': continue - if c.namespaceURI != FREE_NS: continue - if c.getAttributeNS(None, 'type') != name: continue - doc.documentElement.removeChild(c) - removed = True - if not removed: - rox.alert("No user-provided information about this type -- can't remove anything") - return - if not rox.confirm("Really remove all user-specified information about type %s?" % name, - g.STOCK_DELETE): - return - write_override(doc) - -# Type names defined even without Override.xml -system_types = None - -def init(): - "(Re)read the database." - global types, system_types - if system_types is None: - types = {} - for mime_dir in ['/usr/share/mime', '/usr/local/share/mime', home_mime]: - packages_dir = os.path.join(mime_dir, 'packages') - if not os.path.isdir(packages_dir): - continue - packages = os.listdir(packages_dir) - packages.sort() - for package in packages: - if package == 'Override.xml' and mime_dir is home_mime: continue - scan_file(os.path.join(packages_dir, package), False) - system_types = types.keys() - else: - for t in types.keys(): - if t not in system_types: - del types[t] - for t in types.values(): - t.remove_user() - scan_file(user_override, True) +import os +import rox +from rox import g +from xml.parsers import expat +from xml.dom import XML_NAMESPACE, Node +import fields +import override +from override import FREE_NS + +types = {} + +home_mime = os.path.join(os.environ['HOME'], '.mime') + +def data(node): + return ''.join([text.nodeValue for text in node.childNodes + if text.nodeType == Node.TEXT_NODE]) + +def get_type(name): + if name not in types: + types[name] = MIME_Type(name) + return types[name] + +class MIME_Type: + def __init__(self, name): + assert name not in types + self.media, self.subtype = name.split('/') + + self.comments = [] + self.globs = [] + self.magic = [] + self.xml = [] + self.others = [] + + def add_comment(self, lang, comment, user): + self.comments.append((lang, comment, user)) + + def add_xml(self, uri, name, user): + self.xml.append((uri, name, user)) + + def add_magic(self, prio, root, user): + self.magic.append((prio, root, user)) + + def add_other(self, element, user): + self.others.append((element, user)) + + def add_glob(self, pattern, user): + self.globs.append((pattern, user)) + + def get_comment(self): + best = None + for lang, comment, user in self.comments: + if not lang: + return comment + best = comment + return best or self.get_name() + + def get_name(self): + return self.media + '/' + self.subtype + + def make(self, klass, list): + return [klass(self, item) for item in list] + + def get_comments(self): return self.make(fields.Comment, self.comments) + def get_globs(self): return self.make(fields.Glob, self.globs) + def get_magic(self): return self.make(fields.Magic, self.magic) + def get_xml(self): return self.make(fields.XML, self.xml) + def get_others(self): return self.make(fields.Other, self.others) + + def remove_user(self): + for list in ['comments', 'globs', 'magic', 'xml', 'others']: + setattr(self, list, + [x for x in getattr(self, list) if not x[-1]]) + +class FieldParser: + def __init__(self, type, attrs): + self.type = type + + def start(self, element, attrs): pass + def data(self, data): pass + def end(self): pass + +class CommentParser(FieldParser): + def __init__(self, type, attrs, user): + FieldParser.__init__(self, type, attrs) + self.lang = attrs.get(XML_NAMESPACE + ' lang', None) + self.comment = '' + self.user = user + + def data(self, data): + self.comment += data + + def end(self): + self.type.add_comment(self.lang, self.comment, self.user) + +class Match: + def __init__(self, parent, user): + self.parent = parent + self.matches = [] + self.user = user + + def equals_node(self, node): + if self.parent: + if node.localName != 'match' or node.namespaceURI != FREE_NS: + return False + def get(x, d): + a = node.getAttributeNS(None, x) + if a is None or a == '': return d + return a + offset = get('offset', '?') + type = get('type', '?') + value = get('value', '?') + mask = get('mask', None) + if self.offset != offset or self.type != type or \ + self.value != value or self.mask != mask: + return False + + kids = [] + for k in node.childNodes: + if k.nodeType != Node.ELEMENT_NODE: continue + if k.localName != 'match' or k.namespaceURI != FREE_NS: continue + kids.append(k) + + if len(self.matches) != len(kids): + return False + + for m, k in zip(self.matches, kids): + if not m.equals_node(k): return False + + return True + +class MagicParser(FieldParser): + def __init__(self, type, attrs, user): + FieldParser.__init__(self, type, attrs) + self.prio = int(attrs.get('priority', 50)) + self.match = Match(None, user) + self.user = user + + def start(self, element, attrs): + new = Match(self.match, self.user) + new.offset = attrs.get('offset', '?') + new.type = attrs.get('type', '?') + new.value = attrs.get('value', '?') + new.mask = attrs.get('mask', None) + self.match.matches.append(new) + self.match = new + + def end(self): + if self.match.parent: + self.match = self.match.parent + else: + self.type.add_magic(self.prio, self.match, self.user) + +class Scanner: + def __init__(self): + self.level = 0 + self.type = None + self.handler = None + + def parse(self, path, user): + parser = expat.ParserCreate(namespace_separator = ' ') + parser.StartElementHandler = self.start + parser.EndElementHandler = self.end + parser.CharacterDataHandler = self.data + self.user = user + parser.ParseFile(file(path)) + + def start(self, element, attrs): + self.level += 1 + if self.level == 1: + assert element == FREE_NS + ' mime-info' + elif self.level == 2: + assert element == FREE_NS + ' mime-type' + self.type = get_type(attrs['type']) + elif self.level == 3: + if element == FREE_NS + ' comment': + self.handler = CommentParser(self.type, attrs, + self.user) + elif element == FREE_NS + ' glob': + self.type.add_glob(attrs['pattern'], self.user) + elif element == FREE_NS + ' magic': + self.handler = MagicParser(self.type, attrs, + self.user) + elif element == FREE_NS + ' root-XML': + self.type.add_xml(attrs['namespaceURI'], + attrs['localName'], + self.user) + else: + self.type.add_other(element, self.user) + else: + assert self.handler + self.handler.start(element, attrs) + + def end(self, element): + if self.handler: + self.handler.end() + self.level -=1 + if self.level == 1: + self.type = None + elif self.level == 2: + self.handler = None + + def data(self, data): + if self.handler: + self.handler.data(data) + +def scan_file(path, user): + if not path.endswith('.xml'): return + if not os.path.exists(path): + return + scanner = Scanner() + try: + scanner.parse(path, user) + except: + rox.report_exception() + +def add_type(name): + doc = get_override() + + root = doc.documentElement + type_node = doc.createElementNS(FREE_NS, 'mime-type') + root.appendChild(type_node) + + type_node.setAttributeNS(None, 'type', name) + + override.write_override(doc) + import __main__ + __main__.box.show_type(name) + +def delete_type(name): + doc = get_override() + removed = False + for c in doc.documentElement.childNodes: + if c.nodeType != Node.ELEMENT_NODE: continue + if c.nodeName != 'mime-type': continue + if c.namespaceURI != FREE_NS: continue + if c.getAttributeNS(None, 'type') != name: continue + doc.documentElement.removeChild(c) + removed = True + if not removed: + rox.alert("No user-provided information about this type -- can't remove anything") + return + if not rox.confirm("Really remove all user-specified information about type %s?" % name, + g.STOCK_DELETE): + return + override.write_override(doc) + +# Type names defined even without Override.xml +system_types = None + +def init(): + "(Re)read the database." + global types, system_types + if system_types is None: + types = {} + for mime_dir in ['/usr/share/mime', '/usr/local/share/mime', home_mime]: + packages_dir = os.path.join(mime_dir, 'packages') + if not os.path.isdir(packages_dir): + continue + packages = os.listdir(packages_dir) + packages.sort() + for package in packages: + if package == 'Override.xml' and mime_dir is home_mime: continue + scan_file(os.path.join(packages_dir, package), False) + system_types = types.keys() + else: + for t in types.keys(): + if t not in system_types: + del types[t] + for t in types.values(): + t.remove_user() + scan_file(override.user_override, True) -- 2.11.4.GIT