13 class Editor(rox
.Dialog
):
15 rox
.Dialog
.__init
__(self
)
16 self
.set_default_size(600, 400)
17 self
.set_has_separator(False)
21 self
.add_button(g
.STOCK_ADD
, ADD
)
22 self
.add_button(g
.STOCK_DELETE
, DELETE
)
23 self
.add_button(g
.STOCK_PROPERTIES
, EDIT
)
24 self
.add_button(g
.STOCK_CLOSE
, g
.RESPONSE_CANCEL
)
26 self
.set_default_response(g
.RESPONSE_CANCEL
)
28 swin
= g
.ScrolledWindow()
29 swin
.set_border_width(4)
30 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_ALWAYS
)
31 swin
.set_shadow_type(g
.SHADOW_IN
)
32 self
.vbox
.pack_start(swin
, True, True, 0)
34 self
.mime_model
= g
.TreeStore(str, str)
35 view
= g
.TreeView(self
.mime_model
)
38 view
.set_search_column(1)
40 cell
= g
.CellRendererText()
41 column
= g
.TreeViewColumn('MIME-Type', cell
, text
= 0)
42 view
.append_column(column
)
43 column
.set_sort_column_id(0)
45 cell
= g
.CellRendererText()
46 column
= g
.TreeViewColumn('Description', cell
, text
= 1)
47 view
.append_column(column
)
48 column
.set_sort_column_id(1)
50 view
.connect('row-activated', self
.activate
)
54 def response(self
, resp
):
56 self
.edit_selected(view
.get_selection())
58 self
.delete_type(view
)
61 self
.add_type
.present()
63 self
.add_type
= NewType()
64 def destroyed(widget
): self
.add_type
= None
65 self
.add_type
.connect('destroy', destroyed
)
69 self
.connect('response', response
)
71 def changed(selection
):
72 model
, iter = selection
.get_selected()
73 self
.set_response_sensitive(EDIT
, iter != None)
74 self
.set_response_sensitive(DELETE
, iter != None)
75 selection
= view
.get_selection()
76 selection
.connect('changed', changed
)
79 def delete_type(self
, view
):
80 model
, iter = view
.get_selection().get_selected()
82 rox
.alert('Nothing selected')
84 type_name
= model
.get_value(iter, 0)
85 type.delete_type(type_name
)
88 self
.set_title('Scanning... please wait')
93 self
.mime_model
.clear()
98 return cmp(a
[1], b
[1])
100 return a
[:1].upper() + a
[1:]
101 items
= [(t
, caps(t
.get_comment())) for t
in type.types
.values()]
102 items
.sort(items_cmp
)
104 iter = self
.mime_model
.append(None)
105 self
.mime_model
.set(iter, 1, c
, 0, t
.get_name())
107 self
.set_title('MIME-Editor')
109 for b
in edit_boxes
.values():
110 if b
.mime_type
in type.types
:
115 def activate(self
, view
, path
, column
):
116 iter = self
.mime_model
.get_iter(path
)
117 type_name
= self
.mime_model
.get_value(iter, 0)
120 def edit(self
, type_name
):
121 if type_name
in edit_boxes
:
122 edit_boxes
[type_name
].present()
124 box
= EditBox(type_name
)
125 edit_boxes
[type_name
] = box
126 def destroy(dialog
): del edit_boxes
[type_name
]
127 box
.connect('destroy', destroy
)
130 def edit_selected(self
, selection
):
131 model
, iter = selection
.get_selected()
133 rox
.alert('You need to select a type to edit first')
135 type_name
= model
.get_value(iter, 0)
138 def show_type(self
, type_name
):
139 model
= self
.mime_model
140 iter = model
.get_iter_first()
142 name
= model
.get_value(iter, 0)
143 if name
== type_name
:
144 path
= model
.get_path(iter)
145 self
.view
.set_cursor(path
, None, False)
148 iter = model
.iter_next(iter)
150 class EditBox(rox
.Dialog
):
151 def __init__(self
, type_name
):
152 rox
.Dialog
.__init
__(self
)
153 self
.set_has_separator(False)
154 self
.set_default_size(-1, 400)
155 self
.mime_type
= type_name
157 self
.set_title('MIME-Editor: %s' % type_name
)
159 swin
= g
.ScrolledWindow()
160 swin
.set_border_width(4)
161 swin
.set_shadow_type(g
.SHADOW_IN
)
162 self
.vbox
.pack_start(swin
, True, True, 0)
164 self
.model
= g
.TreeStore(str, object, g
.gdk
.Color
)
165 view
= g
.TreeView(self
.model
)
169 cell
= g
.CellRendererText()
170 column
= g
.TreeViewColumn(type_name
, cell
, text
= 0, foreground_gdk
= 2)
171 view
.append_column(column
)
173 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_AUTOMATIC
)
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
):
184 self
.delete_field(view
)
186 self
.edit_field(view
)
189 self
.connect('response', response
)
191 def changed(selection
):
192 model
, iter = selection
.get_selected()
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 def may_change(path
):
203 "Prevent selecting greyed-out lines"
204 iter = self
.model
.get_iter(path
)
205 if self
.model
.get_value(iter, 2):
208 selection
.set_select_function(may_change
)
211 def activate(view
, path
, column
):
212 iter = self
.model
.get_iter(path
)
213 if self
.model
.get_value(iter, 2):
215 field
= self
.model
.get_value(iter, 1)
217 self
.add_new_field(field
)
220 view
.connect('row-activated', activate
)
222 self
.set_default_response(g
.RESPONSE_CANCEL
)
224 label
= g
.Label("Shaded entries are provided by system packages and cannot "
225 "be edited or deleted. However, any information you add will "
226 "take precedence over them.")
227 label
.set_line_wrap(True)
228 self
.vbox
.pack_start(label
, False, True, 0)
233 grey
= self
.get_style().fg
[g
.STATE_INSENSITIVE
]
234 def build(parent
, fields
):
235 fields
.sort(lambda a
, b
: cmp(str(a
), str(b
)))
237 iter = self
.model
.append(parent
)
239 self
.model
.set(iter, 0, str(field
), 1, field
)
241 self
.model
.set(iter, 0, str(field
), 1, field
, 2, grey
)
242 build(iter, field
.get_sub_fields())
243 t
= type.get_type(self
.mime_type
)
245 for aspect
, getter
, klass
in [('Name matching', t
.get_globs
, fields
.Glob
),
246 ('Contents matching', t
.get_magic
, fields
.Magic
),
247 ('XML namespace matching', t
.get_xml
, fields
.XML
),
248 ('Others', t
.get_others
, None),
249 ('Descriptions', t
.get_comments
, fields
.Comment
)]:
250 iter = self
.model
.append(None)
252 self
.model
.set(iter, 0, aspect
, 1, klass
)
254 self
.model
.set(iter, 0, aspect
, 1, klass
, 2, grey
)
255 build(iter, getter())
256 self
.view
.expand_all()
258 def add_new_field(self
, klass
):
260 new
= klass(type.get_type(self
.mime_type
))
263 def add_field(self
, view
):
264 model
, iter = view
.get_selection().get_selected()
266 rox
.alert('You need to select a group, so I know what kind of thing to add')
268 path
= model
.get_path(iter)
269 field
= model
.get_value(model
.get_iter(path
), 1)
270 if isinstance(field
, fields
.Field
) and hasattr(field
, 'add_sub_field'):
271 field
.add_sub_field() # For magic
273 field
= model
.get_value(model
.get_iter(path
[:1]), 1)
274 self
.add_new_field(field
)
276 def delete_field(self
, view
):
277 model
, iter = view
.get_selection().get_selected()
279 rox
.alert("Nothing selected!")
280 field
= model
.get_value(iter, 1)
284 def edit_field(self
, view
):
285 model
, iter = view
.get_selection().get_selected()
287 rox
.alert("Nothing selected!")
288 field
= model
.get_value(iter, 1)
292 class NewType(rox
.Dialog
):
294 rox
.Dialog
.__init
__(self
)
295 self
.set_position(g
.WIN_POS_MOUSE
)
296 self
.set_title('Add new MIME type')
297 self
.set_has_separator(False)
299 self
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
300 self
.add_button(g
.STOCK_ADD
, g
.RESPONSE_OK
)
301 self
.set_default_response(g
.RESPONSE_OK
)
304 self
.tips
= tips
# (pygtk refcount bug again)
306 vbox
= g
.VBox(False, 4)
307 self
.vbox
.pack_start(vbox
, True, True, 0)
308 vbox
.set_border_width(4)
310 def pair(label
, widget
):
311 hbox
= g
.HBox(False, 0)
312 vbox
.pack_start(hbox
, False, True, 0)
313 hbox
.pack_start(g
.Label(label
+ ': '), False, True, 0)
314 hbox
.pack_start(widget
, True, True, 0)
317 combo
.set_popdown_strings(["text", "application", "image", "audio",
318 "video", "message", "model"])
319 pair('Media type', combo
)
320 tips
.set_tip(combo
.entry
, "Choose what sort of files this type describes.\n"
321 "Note that the 'text' group should only be used "
322 "for types that can be viewed in a normal text "
323 "editor. For example, HTML is text/html, "
324 "but Word documents are in the 'application' group.")
327 entry
.set_text('x-my-type')
328 pair('Subtype', entry
)
329 entry
.set_activates_default(True)
330 tips
.set_tip(entry
, "Note: unoffical types should start with 'x-'")
332 self
.comment
= g
.Entry()
333 pair('Description', self
.comment
)
334 tips
.set_tip(self
.comment
, "A brief description of this type. For example:\n"
335 "OpenOffice Spreadsheet")
336 self
.comment
.set_activates_default(True)
338 self
.glob
= g
.Entry()
339 self
.glob
.set_text('*.myapp')
340 pair('For files named', self
.glob
)
341 self
.glob
.set_activates_default(True)
342 tips
.set_tip(self
.glob
,
343 "A rule to spot which files should have this type. Use '*' to "
344 "mean 'anything'. For example, to match anything ending in '.app' "
347 "You can leave this blank, or add extra patterns later.")
349 self
.vbox
.pack_start(g
.Label('Hold the pointer over any field\n'
350 'for more information.'), False, True, 2)
354 def response(w
, resp
):
355 if resp
== g
.RESPONSE_OK
:
356 type_name
= combo
.entry
.get_text() + '/' + entry
.get_text()
357 if type_name
.count('/') != 1 or type_name
.count(' '):
358 rox
.alert("Invalid MIME type name '%s'" % type_name
)
360 if type_name
in type.types
:
361 rox
.alert("Type '%s' already exists!" % type_name
)
363 __main__
.box
.show_type(type_name
)
365 if not self
.comment
.get_text():
366 rox
.alert("Please provide a description for this type")
368 type.add_type(type_name
, self
.comment
.get_text(),
369 self
.glob
.get_text())
371 self
.connect('response', response
)