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
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)