Exemplo n.º 1
0
class DesktopEntryDict(SectionedConfigDict, Application):
	'''Base class for L{DesktopEntryFile}, defines most of the logic.

	The following keys are supported:
	  - C{%f}: a single file path
	  - C{%F}: a list of file paths
	  - C{%u}: a single URL
	  - C{%U}: a list of URLs
	  - C{%i}: the icon as defined in the desktop entry, if any,
	    prefixed with C{--icon}
	  - C{%c}: the name from the desktop entry
	  - C{%k}: the file path for the desktop entry file

	See L{parse_exec()} for interpolating these keys. If the command
	does not contain any keys, the file paths or URLs to open are
	just appended to the command.

	@ivar key: the name of the ".desktop" file, this is the key needed
	to lookup the application through the L{ApplicationManager}.
	@ivar name: the 'Name' field from the desktop entry
	@ivar comment: the 'Comment' field from the desktop entry
	@ivar cmd: the command and arguments as a tuple, based on the
	'Exec' key (still contains the keys for interpolation)
	@ivar tryexeccmd: the command to check in L{tryexec()}, from the
	'TryExe' key in the desktop entry, if C{None} fall back to first
	item of C{cmd}
	'''

	__repr__ = Application.__repr__

	_definitions = (
		# Data types for all keys are defined in spec - see freedesktop.org
		# Don't define all keys in the spec, just define the ones that we might use
		('Type',		String('Application')),
		('Version',		Numeric(1.0)),
		('GenericName',	LocaleString(None)),
		('Name',		LocaleString(None)),
		('Comment',		LocaleString(None)),
		('Exec',		String(None)),
		('TryExec',		String(None)),
		('Icon',		LocaleString(None)),
		('MimeType',	String(None)),
		('Terminal',	Boolean(False)),
		('NoDisplay',	Boolean(False)),
	)

	def __init__(self):
		SectionedConfigDict.__init__(self)
		self['Desktop Entry'].define(self._definitions)
		self.encoding = zim.fs.ENCODING
		if self.encoding == 'mbcs':
			self.encoding = 'utf-8'

	@property
	def key(self):
		return '__anon__' # no mapping to .desktop file

	def isvalid(self):
		'''Check if all the fields that are required according to the
		spcification are set. Assumes we only use desktop files to
		describe applications (and not links or dirs, which are also
		covered by the spec).
		@returns: C{True} if all required fields are set
		'''
		entry = self['Desktop Entry']
		if entry.get('Type') == 'Application' \
		and entry.get('Version') == 1.0 \
		and entry.get('Name') \
		and entry.get('Exec'):
			return True
		else:
			logger.error('Invalid desktop entry: %s %s', self.key, entry)
			return False

	@property
	def name(self):
		# TODO: localisation of application name
		return self['Desktop Entry']['Name']

	@property
	def comment(self):
		# TODO: localisation of application name
		return self['Desktop Entry']['Comment'] or ''

	@property
	def nodisplay(self):
		return self['Desktop Entry'].get('NoDisplay', False)

	@property
	def tryexeccmd(self):
		return self['Desktop Entry'].get('TryExec')

	@property
	def cmd(self):
		return split_quoted_strings(self['Desktop Entry']['Exec'])

	def get_pixbuf(self, size):
		'''Get the application icon as a C{gtk.gdk.Pixbuf}.
		@param size: the icon size as gtk constant
		@returns: a pixbuf object or C{None}
		'''
		icon = self['Desktop Entry'].get('Icon', None)
		if not icon:
			return None

		if isinstance(icon, File):
			icon = icon.path

		w, h = gtk.icon_size_lookup(size)

		if '/' in icon or '\\' in icon:
			if zim.fs.isfile(icon):
				return gtk.gdk.pixbuf_new_from_file_at_size(icon, w, h)
			else:
				return None
		else:
			theme = gtk.icon_theme_get_default()
			try:
				pixbuf = theme.load_icon(icon.encode('utf-8'), w, 0)
			except Exception, error:
				#~ logger.exception('Foo')
				return None
			return pixbuf
Exemplo n.º 2
0
class DesktopEntryDict(SectionedConfigDict, Application):
    '''Base class for L{DesktopEntryFile}, defines most of the logic.

	The following keys are supported:
	  - C{%f}: a single file path
	  - C{%F}: a list of file paths
	  - C{%u}: a single URL
	  - C{%U}: a list of URLs
	  - C{%i}: the icon as defined in the desktop entry, if any,
	    prefixed with C{--icon}
	  - C{%c}: the name from the desktop entry
	  - C{%k}: the file path for the desktop entry file

	See L{parse_exec()} for interpolating these keys. If the command
	does not contain any keys, the file paths or URLs to open are
	just appended to the command.

	@ivar key: the name of the ".desktop" file, this is the key needed
	to lookup the application through the L{ApplicationManager}.
	@ivar name: the 'Name' field from the desktop entry
	@ivar comment: the 'Comment' field from the desktop entry
	@ivar cmd: the command and arguments as a tuple, based on the
	'Exec' key (still contains the keys for interpolation)
	@ivar tryexeccmd: the command to check in L{tryexec()}, from the
	'TryExe' key in the desktop entry, if C{None} fall back to first
	item of C{cmd}
	'''

    __repr__ = Application.__repr__

    _definitions = (
        # Data types for all keys are defined in spec - see freedesktop.org
        # Don't define all keys in the spec, just define the ones that we might use
        ('Type', String('Application')),
        ('Version', Numeric(1.0)),
        ('GenericName', LocaleString(None)),
        ('Name', LocaleString(None)),
        ('Comment', LocaleString(None)),
        ('Exec', String(None)),
        ('TryExec', String(None)),
        ('Icon', IconString(None)),
        ('MimeType', String(None)),
        ('Terminal', Boolean(False)),
        ('NoDisplay', Boolean(False)),
    )

    def __init__(self):
        SectionedConfigDict.__init__(self)
        self['Desktop Entry'].define(self._definitions)

    @property
    def key(self):
        return '__anon__'  # no mapping to .desktop file

    def isvalid(self):
        '''Check if all the fields that are required according to the
		specification are set. Assumes we only use desktop files to
		describe applications (and not links or dirs, which are also
		covered by the spec).
		@returns: C{True} if all required fields are set
		'''
        entry = self['Desktop Entry']
        if entry.get('Type') == 'Application' \
        and entry.get('Version') == 1.0 \
        and entry.get('Name') \
        and entry.get('Exec'):
            return True
        else:
            logger.error('Invalid desktop entry: %s %s', self.key, entry)
            return False

    @property
    def name(self):
        # TODO: localisation of application name
        return self['Desktop Entry']['Name']

    @property
    def comment(self):
        # TODO: localisation of application name
        return self['Desktop Entry']['Comment'] or ''

    @property
    def nodisplay(self):
        return self['Desktop Entry'].get('NoDisplay', False)

    @property
    def tryexeccmd(self):
        return self['Desktop Entry'].get('TryExec')

    @property
    def cmd(self):
        return split_quoted_strings(self['Desktop Entry']['Exec'])

    def get_pixbuf(self, size):
        '''Get the application icon as a C{GdkPixbuf.Pixbuf}.
		@param size: the icon size as gtk constant
		@returns: a pixbuf object or C{None}
		'''
        icon = self['Desktop Entry'].get('Icon', None)
        if not icon:
            return None

        if isinstance(icon, File):
            icon = icon.path

        w, h = strip_boolean_result(Gtk.icon_size_lookup(size))

        if '/' in icon or '\\' in icon:
            if os.path.isfile(icon):
                return GdkPixbuf.Pixbuf.new_from_file_at_size(icon, w, h)
            else:
                return None
        else:
            theme = Gtk.IconTheme.get_default()
            try:
                pixbuf = theme.load_icon(icon, w, 0)
            except Exception as error:
                #~ logger.exception('Foo')
                return None
            return pixbuf

    def parse_exec(self, args=None):
        '''Parse the 'Exec' string and interpolate the arguments
		according to the keys of the desktop spec.
		@param args: list of either URLs or L{File} objects
		@returns: the full command to execute as a tuple
		'''
        assert args is None or isinstance(args, (list, tuple))

        def uris(args):
            uris = []
            for arg in args:
                if isinstance(arg, (File, Dir)):
                    uris.append(arg.uri)
                else:
                    uris.append(str(arg))
            return uris

        cmd = split_quoted_strings(self['Desktop Entry']['Exec'])
        if args is None or len(args) == 0:
            if '%f' in cmd:
                cmd.remove('%f')
            elif '%F' in cmd:
                cmd.remove('%F')
            elif '%u' in cmd:
                cmd.remove('%u')
            elif '%U' in cmd:
                cmd.remove('%U')
        elif '%f' in cmd:
            assert len(args) == 1, 'application takes one file name'
            i = cmd.index('%f')
            cmd[i] = str(args[0])
        elif '%F' in cmd:
            i = cmd.index('%F')
            for arg in reversed(list(map(str, args))):
                cmd.insert(i, str(arg))
            cmd.remove('%F')
        elif '%u' in cmd:
            assert len(args) == 1, 'application takes one url'
            i = cmd.index('%u')
            cmd[i] = uris(args)[0]
        elif '%U' in cmd:
            i = cmd.index('%U')
            for arg in reversed(uris(args)):
                cmd.insert(i, str(arg))
            cmd.remove('%U')
        else:
            cmd.extend(list(map(str, args)))

        if '%i' in cmd:
            if 'Icon' in self['Desktop Entry'] \
            and self['Desktop Entry']['Icon']:
                i = cmd.index('%i')
                cmd[i] = self['Desktop Entry']['Icon']
                cmd.insert(i, '--icon')
            else:
                cmd.remove('%i')

        if '%c' in cmd:
            i = cmd.index('%c')
            cmd[i] = self.name

        if '%k' in cmd:
            i = cmd.index('%k')
            if hasattr(self, 'file'):
                cmd[i] = self.file.path
            else:
                cmd[i] = ''

        return tuple(cmd)

    _cmd = parse_exec  # To hook into Application.spawn and Application.run

    def update(self, E=(), **F):
        '''Same as C{dict.update()}'''
        self['Desktop Entry'].update(E, **F)