Scroll to show newly added types.
[mime-editor.git] / editor.py
blob68a1e06f558967c228b7b7f08bbb6226cc1d88f0
1 import rox
2 from rox import g
3 import os
4 import type
6 edit_boxes = {}
8 ADD = 1
9 DELETE = 2
10 EDIT = 3
12 class Editor(rox.Dialog):
13 def __init__(self):
14 rox.Dialog.__init__(self)
15 self.set_default_size(600, 400)
16 self.set_has_separator(False)
18 self.add_type = None
20 self.add_button(g.STOCK_ADD, ADD)
21 self.add_button(g.STOCK_DELETE, DELETE)
22 self.add_button(g.STOCK_PROPERTIES, EDIT)
23 self.add_button(g.STOCK_CLOSE, g.RESPONSE_CANCEL)
25 self.set_default_response(g.RESPONSE_CANCEL)
27 swin = g.ScrolledWindow()
28 swin.set_border_width(4)
29 swin.set_policy(g.POLICY_NEVER, g.POLICY_ALWAYS)
30 swin.set_shadow_type(g.SHADOW_IN)
31 self.vbox.pack_start(swin, True, True, 0)
33 self.mime_model = g.TreeStore(str, str)
34 view = g.TreeView(self.mime_model)
35 self.view = view
36 swin.add(view)
37 view.set_search_column(1)
39 cell = g.CellRendererText()
40 column = g.TreeViewColumn('MIME-Type', cell, text = 0)
41 view.append_column(column)
42 column.set_sort_column_id(0)
44 cell = g.CellRendererText()
45 column = g.TreeViewColumn('Description', cell, text = 1)
46 view.append_column(column)
47 column.set_sort_column_id(1)
49 view.connect('row-activated', self.activate)
51 self.vbox.show_all()
53 def response(self, resp):
54 if resp == EDIT:
55 self.edit_selected(view.get_selection())
56 elif resp == DELETE:
57 self.delete_type(view)
58 elif resp == ADD:
59 if self.add_type:
60 self.add_type.present()
61 else:
62 self.add_type = NewType()
63 def destroyed(widget): self.add_type = None
64 self.add_type.connect('destroy', destroyed)
65 self.add_type.show()
66 else:
67 self.destroy()
68 self.connect('response', response)
70 def changed(selection):
71 model, iter = selection.get_selected()
72 self.set_response_sensitive(EDIT, iter != None)
73 self.set_response_sensitive(DELETE, iter != None)
74 selection = view.get_selection()
75 selection.connect('changed', changed)
76 changed(selection)
78 def delete_type(self, view):
79 model, iter = view.get_selection().get_selected()
80 if not iter:
81 rox.alert('Nothing selected')
82 return
83 type_name = model.get_value(iter, 0)
84 type.delete_type(type_name)
86 def update(self):
87 self.set_title('Scanning... please wait')
88 g.gdk.flush()
90 type.init()
92 self.mime_model.clear()
94 medias = {}
96 def items_cmp(a, b):
97 return cmp(a[1], b[1])
98 def caps(a):
99 return a[:1].upper() + a[1:]
100 items = [(t, caps(t.get_comment())) for t in type.types.values()]
101 items.sort(items_cmp)
102 for t, c in items:
103 iter = self.mime_model.append(None)
104 self.mime_model.set(iter, 1, c, 0, t.get_name())
106 self.set_title('MIME-Editor')
108 for b in edit_boxes.values():
109 if b.mime_type in type.types:
110 b.update()
111 else:
112 b.destroy()
114 def activate(self, view, path, column):
115 iter = self.mime_model.get_iter(path)
116 type_name = self.mime_model.get_value(iter, 0)
117 self.edit(type_name)
119 def edit(self, type_name):
120 if type_name in edit_boxes:
121 edit_boxes[type_name].present()
122 else:
123 box = EditBox(type_name)
124 edit_boxes[type_name] = box
125 def destroy(dialog): del edit_boxes[type_name]
126 box.connect('destroy', destroy)
127 box.show()
129 def edit_selected(self, selection):
130 model, iter = selection.get_selected()
131 if not iter:
132 rox.alert('You need to select a type to edit first')
133 else:
134 type_name = model.get_value(iter, 0)
135 self.edit(type_name)
137 def show_type(self, type_name):
138 model = self.mime_model
139 iter = model.get_iter_first()
140 while iter:
141 name = model.get_value(iter, 0)
142 if name == type_name:
143 path = model.get_path(iter)
144 self.view.set_cursor(path, None, False)
145 return
146 iter = model.iter_next(iter)
148 class EditBox(rox.Dialog):
149 def __init__(self, type_name):
150 rox.Dialog.__init__(self)
151 self.set_has_separator(False)
152 self.set_default_size(-1, 400)
153 self.mime_type = type_name
155 self.set_title('MIME-Editor: %s' % type_name)
157 swin = g.ScrolledWindow()
158 swin.set_border_width(4)
159 swin.set_shadow_type(g.SHADOW_IN)
160 self.vbox.pack_start(swin, True, True, 0)
162 self.model = g.TreeStore(str, object)
163 view = g.TreeView(self.model)
164 swin.add(view)
166 cell = g.CellRendererText()
167 column = g.TreeViewColumn(type_name, cell, text = 0)
168 view.append_column(column)
170 swin.set_policy(g.POLICY_NEVER, g.POLICY_AUTOMATIC)
171 self.update()
173 self.vbox.show_all()
174 view.expand_all()
176 self.add_button(g.STOCK_ADD, ADD)
177 self.add_button(g.STOCK_DELETE, DELETE)
178 self.add_button(g.STOCK_PROPERTIES, EDIT)
179 self.add_button(g.STOCK_CLOSE, g.RESPONSE_CANCEL)
180 def response(w, resp):
181 if resp == ADD:
182 self.add_field(view)
183 elif resp == DELETE:
184 self.delete_field(view)
185 elif resp == EDIT:
186 self.edit_field(view)
187 else:
188 self.destroy()
189 self.connect('response', response)
191 def changed(selection):
192 model, iter = selection.get_selected()
193 on_field = False
194 if iter != None:
195 path = model.get_path(iter)
196 on_field = len(path) > 1
197 self.set_response_sensitive(ADD, iter != None)
198 self.set_response_sensitive(EDIT, on_field)
199 self.set_response_sensitive(DELETE, on_field)
200 selection = view.get_selection()
201 selection.connect('changed', changed)
202 changed(selection)
204 def activate(view, path, column):
205 iter = self.model.get_iter(path)
206 field = self.model.get_value(iter, 1)
207 if len(path) == 1:
208 self.add_new_field(field)
209 else:
210 field.edit()
211 view.connect('row-activated', activate)
213 self.set_default_response(g.RESPONSE_CANCEL)
215 def update(self):
216 t = type.get_type(self.mime_type)
217 self.model.clear()
218 for aspect, getter, klass in [('Name matching', t.get_globs, type.Glob),
219 ('Contents matching', t.get_magic, type.Magic),
220 ('XML namespace matching', t.get_xml, type.XML),
221 ('Others', t.get_others, type.Other),
222 ('Descriptions', t.get_comments, type.Comment)]:
223 iter = self.model.append(None)
224 self.model.set(iter, 0, aspect, 1, klass)
225 fields = getter()
226 fields.sort(lambda a, b: cmp(str(a), str(b)))
227 last = None
228 for field in fields:
229 if field == last: continue
230 last = field
231 f = self.model.append(iter)
232 self.model.set(f, 0, str(field), 1, field)
233 field.add_subtree(self.model, f)
235 def add_new_field(self, type):
236 rox.alert("TODO: Add field of type '%s'" % type)
238 def add_field(self, view):
239 model, iter = view.get_selection().get_selected()
240 if not iter:
241 rox.alert('You need to select a group, so I know what kind of thing to add')
242 return
243 path = model.get_path(iter)
244 field = model.get_value(model.get_iter(path[:1]), 1)
245 self.add_new_field(field)
247 def delete_field(self, view):
248 model, iter = view.get_selection().get_selected()
249 if not iter:
250 rox.alert("Nothing selected!")
251 field = model.get_value(iter, 1)
252 assert field
253 field.delete()
255 def edit_field(self, view):
256 model, iter = view.get_selection().get_selected()
257 if not iter:
258 rox.alert("Nothing selected!")
259 field = model.get_value(iter, 1)
260 assert field
261 field.edit()
263 class NewType(rox.Dialog):
264 def __init__(self):
265 rox.Dialog.__init__(self)
266 self.set_title('Add new MIME type')
268 self.add_button(g.STOCK_CANCEL, g.RESPONSE_CANCEL)
269 self.add_button(g.STOCK_ADD, g.RESPONSE_OK)
270 self.set_default_response(g.RESPONSE_OK)
272 vbox = g.VBox(False, 4)
273 self.vbox.pack_start(vbox, True, True, 0)
274 vbox.set_border_width(4)
276 hbox = g.HBox(False, 4)
277 vbox.pack_start(hbox, False, True, 0)
278 label = g.Label("Media type:")
279 hbox.pack_start(label, False, True, 0)
280 combo = g.Combo()
281 combo.set_popdown_strings(["text", "application", "image", "audio",
282 "video", "message", "model"])
283 hbox.pack_start(combo, True, True, 0)
285 hbox = g.HBox(False, 4)
286 vbox.pack_start(hbox, False, True, 0)
287 label = g.Label("Subtype:")
288 hbox.pack_start(label, False, True, 0)
289 entry = g.Entry()
290 entry.set_text('x-my-type')
291 hbox.pack_start(entry, True, True, 0)
292 entry.set_activates_default(True)
294 label = g.Label("Note: the 'text' group should only be used for types that can be "
295 "viewed in a normal text editor. For example, HTML is text/html, "
296 "but Word documents are in the 'application' group.\n"
297 "Unoffical types should start with 'x-'.")
298 label.set_line_wrap(True)
299 vbox.pack_start(label, False, True, 0)
301 self.vbox.show_all()
303 def response(w, resp):
304 if resp == g.RESPONSE_OK:
305 type_name = combo.entry.get_text() + '/' + entry.get_text()
306 if type_name.count('/') != 1 or type_name.count(' '):
307 rox.alert("Invalid MIME type name '%s'" % type_name)
308 return
309 if type_name in type.types:
310 rox.alert("Type '%s' already exists!" % type_name)
311 import __main__
312 __main__.box.show_type(type_name)
313 return
314 type.add_type(type_name)
315 self.destroy()
316 self.connect('response', response)