Started edit globs box.
[mime-editor.git] / editor.py
blobf8a10fa8e1dc4e9ef813ced07d913761da30fe02
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, klass):
236 rox.alert("TODO: Add field of type '%s'" % type)
237 new = klass()
238 new.edit()
240 def add_field(self, view):
241 model, iter = view.get_selection().get_selected()
242 if not iter:
243 rox.alert('You need to select a group, so I know what kind of thing to add')
244 return
245 path = model.get_path(iter)
246 field = model.get_value(model.get_iter(path[:1]), 1)
247 self.add_new_field(field)
249 def delete_field(self, view):
250 model, iter = view.get_selection().get_selected()
251 if not iter:
252 rox.alert("Nothing selected!")
253 field = model.get_value(iter, 1)
254 assert field
255 field.delete()
257 def edit_field(self, view):
258 model, iter = view.get_selection().get_selected()
259 if not iter:
260 rox.alert("Nothing selected!")
261 field = model.get_value(iter, 1)
262 assert field
263 field.edit()
265 class NewType(rox.Dialog):
266 def __init__(self):
267 rox.Dialog.__init__(self)
268 self.set_title('Add new MIME type')
270 self.add_button(g.STOCK_CANCEL, g.RESPONSE_CANCEL)
271 self.add_button(g.STOCK_ADD, g.RESPONSE_OK)
272 self.set_default_response(g.RESPONSE_OK)
274 vbox = g.VBox(False, 4)
275 self.vbox.pack_start(vbox, True, True, 0)
276 vbox.set_border_width(4)
278 hbox = g.HBox(False, 4)
279 vbox.pack_start(hbox, False, True, 0)
280 label = g.Label("Media type:")
281 hbox.pack_start(label, False, True, 0)
282 combo = g.Combo()
283 combo.set_popdown_strings(["text", "application", "image", "audio",
284 "video", "message", "model"])
285 hbox.pack_start(combo, True, True, 0)
287 hbox = g.HBox(False, 4)
288 vbox.pack_start(hbox, False, True, 0)
289 label = g.Label("Subtype:")
290 hbox.pack_start(label, False, True, 0)
291 entry = g.Entry()
292 entry.set_text('x-my-type')
293 hbox.pack_start(entry, True, True, 0)
294 entry.set_activates_default(True)
296 label = g.Label("Note: the 'text' group should only be used for types that can be "
297 "viewed in a normal text editor. For example, HTML is text/html, "
298 "but Word documents are in the 'application' group.\n"
299 "Unoffical types should start with 'x-'.")
300 label.set_line_wrap(True)
301 vbox.pack_start(label, False, True, 0)
303 self.vbox.show_all()
305 def response(w, resp):
306 if resp == g.RESPONSE_OK:
307 type_name = combo.entry.get_text() + '/' + entry.get_text()
308 if type_name.count('/') != 1 or type_name.count(' '):
309 rox.alert("Invalid MIME type name '%s'" % type_name)
310 return
311 if type_name in type.types:
312 rox.alert("Type '%s' already exists!" % type_name)
313 import __main__
314 __main__.box.show_type(type_name)
315 return
316 type.add_type(type_name)
317 self.destroy()
318 self.connect('response', response)