diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | examples/launchers | 15 | ||||
-rw-r--r-- | src/plugins/application.py | 147 |
4 files changed, 160 insertions, 5 deletions
@@ -33,7 +33,7 @@ PLUGINS = chase clock cpuinfo cpuonline cpu df discstats ipaddress \ pacman snmp snmp6 softirqs solar uname uptime users \ vmstat weather xdisplay xkb alsa dentrystate inodestate \ files hdparm tzclock ropty ping inotify random swaps \ - locks images + locks images application PLUGIN_EXAMPLES = chase clock cpu cpuinfo cpuonline df discstats \ ipaddress kmsg loadavg lunar mem moc network \ @@ -58,5 +58,4 @@ Terminal mode workspace and layout switching in xmonad fix so that ropty works with wall too -find icons by searching .desktop files diff --git a/examples/launchers b/examples/launchers index fdb358a..86bd54f 100644 --- a/examples/launchers +++ b/examples/launchers @@ -24,6 +24,8 @@ import sys import time from plugins.image import Image +from plugins.application import Application + OUTPUT, HEIGHT, YPOS, TOP = 0, 24, 24, True @@ -65,14 +67,21 @@ class LauncherSet: bar.invalidate() +def desktop(file): + application = Application(file) + icon = application.get_setting('Icon') + command = Application.strip_placeholders(application.get_setting('Exec')) + return (icon, 'sh', '-c', command) + + launchers = [ [ ('accessories-calculator', 'mate-calc') , ('accessories-character-map', 'mucharmap') , ('scanner', 'simple-scan') , ('meld', 'meld') ] , [ ('dia', 'dia') - , ('inkscape', 'inkscape') - , ('gimp', 'gimp') + , desktop ('inkscape') + , desktop ('gimp') , ('calligrakrita', 'krita') , ('blender', 'blender') , ('/usr/share/pixmaps/openscad.png', 'openscad') @@ -139,5 +148,5 @@ def unhandled_event(e): elif button == SCROLL_DOWN: launchers[x].scroll_down() -# TODO support for droping files onto the launchers to open with those files would be nice +# TODO support for dropping files onto the launchers to open with those files would be nice diff --git a/src/plugins/application.py b/src/plugins/application.py new file mode 100644 index 0000000..6af7227 --- /dev/null +++ b/src/plugins/application.py @@ -0,0 +1,147 @@ +# -*- python -*- +''' +xpybar – xmobar replacement written in python +Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +''' + + +class Application: + ''' + Applications and .desktop files + + @variable desktop_file:str The .desktop file for the application + @variable settings:dict<str?, dict<str, dict<str?, str>>> Section → key → locale → value map of settings + ''' + + + def __init__(self, file): + ''' + Constructor + + @param file:str The .desktop file if it contains '/', otherwise the name of the application + ''' + import os + if '/' not in file: + import pwd + directories = [] + home = pwd.getpwuid(os.getuid()).pw_dir + directories.append('%s/.local/share/applications' % home) + if 'HOME' in os.environ: + if not os.environ['HOME'] == home: + home = os.environ['HOME'] + directories.append('%s/.local/share/applications' % home) + directories += ['/usr/local/share/applications', '/usr/share/applications', '/share/applications'] + directories = [d for d in directories if os.path.exists(d) and os.path.isdir(d)] + + file = ['%s/%s.desktop' % (d, file) for d in directories] + file = [f for f in file if os.path.exists(f) and os.path.isfile(f)] + if len(file) == 0: + raise Exception('Application not found') + file = file[0] + + self.desktop_file = file + with open(self.desktop_file, 'rb') as file: + data = file.read() + data = [l for l in data.decode('utf-8', 'replace').replace('\r', '\n').split('\n') if not l == ''] + + section = None + self.settings = {} + for line in data: + line = line.strip() + if line.startswith('[') and line.endswith(']'): + section = line[1 : -1] + elif line.startswith(';') or line.startswith('#'): + pass + elif (':' in line) or ('=' in line): + line_e = line.split('=') + line_c = line.split(':') + if len(line_e[0]) < len(line_c[0]): + first = '=' + line = line_e + else: + first = ':' + line = line_c + key, value = line[0].rstrip(), first.join(line[1:]).lstrip() + locale = None + if ('[' in key) and key.endswith(']'): + key = key[:-1].split('[') + key, locale = '['.join(key[:-1]), key[-1] + if section not in self.settings: + self.settings[section] = {} + section_map = self.settings[section] + if key not in section_map: + section_map[key] = {} + key_map = section_map[key] + if locale not in key_map: + key_map[locale] = value + + self.locales = [None] + if 'LANG' in os.environ: + locale = os.environ['LANG'] + elif 'LOCALE' in os.environ: + locale = os.environ['LOCALE'] + else: + return + locale = locale.split(' ')[0].split('.')[0].split('_') + for i in range(len(locale)): + self.locales.append('_'.join(locale[:i])) + self.locales.reverse() + + + def get_setting(self, key, section = 'Desktop Entry', locales = None): + ''' + Read a setting in the application's .desktop file + + @param key:str The key + @param section:str? The section where the key is located + @param locale:list<str?>? Acceptable locales in order of preference, + `None` to use the default for your locale + ''' + map = self.settings + if section not in map: + return None + map = map[section] + if key not in map: + return None + map = map[key] + if locales is None: + locales = self.locales + for locale in locales: + if locale in map: + return map[locale] + return None + + + @staticmethod + def strip_placeholders(text): + ''' + Remove placeholders form an exec string + + @param text:str The exec string with placeholders + @return :str The exec string without placeholders + ''' + buf, esc = '', False + for c in text: + if esc: + esc = False + if c == '%': + buf += c + elif c == '%': + esc = True + else: + buf += c + return buf + |