1 """This module allows applications to set themselves as the default handler for
2 a particular MIME type. This is generally not a good thing to do, because it
3 annoys users if programs fight over the defaults."""
8 from rox
import _
, mime
, choices
, basedir
10 SITE
='rox.sourceforge.net'
18 _IS_OURS
= 6 # Hidden column
20 def load_path(site
, dir, leaf
):
23 path
=basedir
.load_first_config(site
, dir, leaf
)
25 path
=choices
.load(dir, leaf
)
30 def save_path(site
, dir, leaf
, create
=1):
31 filer
=basedir
.load_first_config(SITE
, 'ROX-Filer')
33 if filer
and os
.path
.isdir(filer
):
34 path
=basedir
.save_config_path(site
, dir)
35 path
=os
.path
.join(path
, leaf
)
37 path
=choices
.save(dir, leaf
, create
)
41 class InstallList(rox
.Dialog
):
42 """Dialog to select installation of MIME type handlers"""
43 def __init__(self
, application
, itype
, dir, types
, info
=None, check
=True,
45 """Create the install list dialog.
46 application - path to application to install
47 itype - string describing the type of action to install
48 dir - directory in Choices to store links in
49 types - list of MIME types
50 info - optional message to display below list
51 check - if true (the default), check for existing entries"""
52 rox
.Dialog
.__init
__(self
, title
=_('Install %s') % itype
,
53 buttons
=(rox
.g
.STOCK_CANCEL
, rox
.g
.RESPONSE_CLOSE
,
54 rox
.g
.STOCK_OK
, rox
.g
.RESPONSE_ACCEPT
))
61 self
.aname
=os
.path
.basename(application
)
66 swin
= rox
.g
.ScrolledWindow()
67 swin
.set_size_request(-1, 160)
68 swin
.set_border_width(4)
69 swin
.set_policy(rox
.g
.POLICY_NEVER
, rox
.g
.POLICY_ALWAYS
)
70 swin
.set_shadow_type(rox
.g
.SHADOW_IN
)
71 vbox
.pack_start(swin
, True, True, 0)
73 self
.model
= rox
.g
.ListStore(str, str, str, int, rox
.g
.gdk
.Pixbuf
,
75 view
= rox
.g
.TreeView(self
.model
)
78 view
.set_search_column(1)
80 cell
= rox
.g
.CellRendererPixbuf()
81 column
= rox
.g
.TreeViewColumn('', cell
, pixbuf
= _ICON
)
82 view
.append_column(column
)
84 cell
= rox
.g
.CellRendererText()
85 column
= rox
.g
.TreeViewColumn(_('Type'), cell
, text
= _TNAME
)
86 view
.append_column(column
)
87 column
.set_sort_column_id(_TNAME
)
89 cell
= rox
.g
.CellRendererText()
90 column
= rox
.g
.TreeViewColumn(_('Name'), cell
, text
= _COMMENT
)
91 view
.append_column(column
)
92 column
.set_sort_column_id(_COMMENT
)
95 cell
= rox
.g
.CellRendererText()
96 column
= rox
.g
.TreeViewColumn(_('Current'), cell
, text
= _CURRENT
)
97 view
.append_column(column
)
98 column
.set_sort_column_id(_CURRENT
)
100 cell
= rox
.g
.CellRendererToggle()
101 cell
.set_property('activatable', True)
102 cell
.connect('toggled', self
.install_toggled
, self
.model
)
103 column
= rox
.g
.TreeViewColumn(_('Install?'), cell
, active
= _INSTALL
)
104 view
.append_column(column
)
105 column
.set_sort_column_id(_INSTALL
)
107 cell
= rox
.g
.CellRendererToggle()
108 cell
.connect('toggled', self
.uninstall_toggled
, self
.model
)
109 column
= rox
.g
.TreeViewColumn(_('Uninstall?'), cell
, active
= _UNINSTALL
,
110 activatable
= _IS_OURS
)
111 view
.append_column(column
)
112 column
.set_sort_column_id(_UNINSTALL
)
114 view
.get_selection().set_mode(rox
.g
.SELECTION_NONE
)
117 hbox
=rox
.g
.HBox(spacing
=4)
118 img
=rox
.g
.image_new_from_stock(rox
.g
.STOCK_DIALOG_INFO
,
119 rox
.g
.ICON_SIZE_DIALOG
)
122 lbl
=rox
.g
.Label(info
)
123 lbl
.set_line_wrap(True)
126 vbox
.pack_start(hbox
)
132 def install_toggled(self
, cell
, path
, model
):
133 """Handle the CellRedererToggle stuff for the install column"""
134 if type(path
) == str:
135 # Does this vary by pygtk version?
136 titer
=model
.iter_nth_child(None, int(path
))
138 titer
=model
.get_iter(path
)
139 model
.set_value(titer
, _INSTALL
, not cell
.get_active())
140 if not cell
.get_active():
141 model
.set_value(titer
, _UNINSTALL
, 0)
143 def uninstall_toggled(self
, cell
, path
, model
):
144 """Handle the CellRedererToggle stuff for the uninstall column"""
145 if type(path
) == str:
146 # Does this vary by pygtk version?
147 titer
=model
.iter_nth_child(None, int(path
))
149 titer
=model
.get_iter(path
)
150 avail
=model
.get_value(titer
, _IS_OURS
)
152 model
.set_value(titer
, _UNINSTALL
, not cell
.get_active())
153 if not cell
.get_active():
154 model
.set_value(titer
, _INSTALL
, 0)
156 model
.set_value(titer
, _UNINSTALL
, 0)
158 def load_types(self
):
159 """Load list of types into window"""
162 for tname
in self
.types
:
163 mime_type
=mime
.lookup(tname
)
165 old
=load_path(self
.site
, self
.dir,
167 (mime_type
.media
, mime_type
.subtype
))
168 if old
and os
.path
.islink(old
):
170 oname
=os
.path
.basename(old
)
187 icon
=mime_type
.get_icon(mime
.ICON_SIZE_SMALL
)
189 titer
=self
.model
.append()
190 self
.model
.set(titer
, _TNAME
, tname
,
191 _COMMENT
, mime_type
.get_comment(),
193 _UNINSTALL
, False, _IS_OURS
, can_un
)
195 self
.model
.set(titer
, _CURRENT
, oname
)
197 self
.model
.set(titer
, _ICON
, icon
)
200 def get_active(self
):
201 """Return list of types selected for installing"""
202 titer
=self
.model
.get_iter_first()
205 if self
.model
.get_value(titer
, _INSTALL
):
206 active
.append(self
.model
.get_value(titer
, _TNAME
))
207 titer
=self
.model
.iter_next(titer
)
211 def get_uninstall(self
):
212 """Return list of types selected for uninstalling"""
213 titer
=self
.model
.get_iter_first()
216 if self
.model
.get_value(titer
, _UNINSTALL
) and self
.model
.get_value(titer
, _IS_OURS
):
217 uninstall
.append(self
.model
.get_value(titer
, _TNAME
))
218 titer
=self
.model
.iter_next(titer
)
222 def _run_by_injector():
223 """Internal function."""
225 from zeroinstall
.injector
import basedir
226 for d
in basedir
.xdg_cache_dirs
:
227 if rox
.app_dir
.find(d
)==0:
228 # Applicaion is in a cache dir
230 elif rox
._roxlib
_dir
.find(d
)==0:
231 # ROX-Lib is in a cache dir, we are probably being run by the
239 def _install_at(path
, app_dir
, injint
):
240 """Internal function. Set one type."""
241 tmp
=path
+'.tmp%d' % os
.getpid()
242 if injint
and _run_by_injector(app_dir
):
244 f
.write('#!/bin/sh\n')
245 f
.write('0launch -c "%s" "$@"\n' % injint
)
249 os
.symlink(app_dir
, tmp
)
251 if os
.access(path
, os
.F_OK
):
255 def _install_type_handler(types
, dir, desc
, application
=None, overwrite
=True,
256 info
=None, injint
=None):
257 """Internal function. Does the work of setting MIME-types or MIME-thumb"""
262 application
=rox
.app_dir
263 if application
[0]!='/':
264 application
=os
.path
.abspath(application
)
266 win
=InstallList(application
, desc
, dir, types
, info
)
268 if win
.run()!=int(rox
.g
.RESPONSE_ACCEPT
):
273 types
=win
.get_active()
276 mime_type
= mime
.lookup(tname
)
278 sname
=save_path(SITE
, dir,
279 '%s_%s' % (mime_type
.media
, mime_type
.subtype
))
280 _install_at(sname
, application
, injint
)
282 types
=win
.get_uninstall()
285 mime_type
= mime
.lookup(tname
)
287 sname
=save_path(SITE
, dir,
288 '%s_%s' % (mime_type
.media
, mime_type
.subtype
))
293 run_action_msg
=_("""Run actions can be changed by selecting a file of the appropriate type in the Filer and selecting the menu option 'Set Run Action...'""")
294 def install_run_action(types
, application
=None, overwrite
=True, injint
=None):
295 """Install application as the run action for 1 or more types.
296 application should be the full path to the AppDir.
297 If application is None then it is the running program which will
298 be installed. If overwrite is False then existing run actions will
299 not be changed. The user is asked to confirm the setting for each
301 _install_type_handler(types
, "MIME-types", _("run action"),
302 application
, overwrite
, run_action_msg
,
305 def install_thumbnailer(types
, application
=None, overwrite
=True, injint
=None):
306 """Install application as the thumbnail handler for 1 or more types.
307 application should be the full path to the AppDir.
308 If application is None then it is the running program which will
309 be installed. If overwrite is False then existing thumbnailerss will
310 not be changed. The user is asked to confirm the setting for each
312 _install_type_handler(types
, "MIME-thumb", _("thumbnail handler"),
313 application
, overwrite
, _("""Thumbnail handlers provide support for creating thumbnail images of types of file. The filer can generate thumbnails for most types of image (JPEG, PNG, etc.) but relies on helper applications for the others."""),
316 def install_send_to_types(types
, application
=None, injint
=None):
317 """Install application in the SendTo menu for 1 or more types.
318 application should be the full path to the AppDir.
319 If application is None then it is the running program which will
320 be installed. The user is asked to confirm the setting for each
326 application
=rox
.app_dir
327 if application
[0]!='/':
328 application
=os
.path
.abspath(application
)
330 win
=InstallList(application
, _('type handler'), 'SendTo', types
,
331 _("""The application can handle files of these types. Click on OK to add it to the SendTo menu for the type of file, and also the customized File menu."""),
334 if win
.run()!=int(rox
.g
.RESPONSE_ACCEPT
):
338 types
=win
.get_active()
341 mime_type
=mime
.lookup(tname
)
343 sname
=save_path(SITE
, 'SendTo/.%s_%s' % (mime_type
.media
,
346 _install_at(sname
, application
, injint
)
348 types
=win
.get_uninstall()
351 mime_type
=mime
.lookup(tname
)
353 sname
=save_path(SITE
, 'SendTo/.%s_%s' % (mime_type
.media
,
360 def install_from_appinfo(appdir
= rox
.app_dir
, injint
=None):
361 """Read the AppInfo file from the AppDir and perform the installations
362 indicated. The elements to use are <CanThumbnail> and <CanRun>, each containing
363 a number of <MimeType type='...'/> elements.
364 appdir - Path to application (defaults to current app)
365 injint - Zero install injector interface, or None if none
369 app_info_path
= os
.path
.join(appdir
, 'AppInfo.xml')
370 ainfo
= rox
.AppInfo
.AppInfo(app_info_path
)
372 can_run
= ainfo
.getCanRun()
373 can_thumbnail
= ainfo
.getCanThumbnail()
374 if can_run
or can_thumbnail
:
375 install_run_action(can_run
, appdir
, injint
)
376 install_thumbnailer(can_thumbnail
, appdir
, injint
)
377 install_send_to_types(can_run
, appdir
, injint
)
379 raise Exception('Internal error: No actions found in %s. '
380 'Check your namespaces!' % app_info_path
)