first changes
This commit is contained in:
128
i3/scripts/dmenu/modules/Application.py
Normal file
128
i3/scripts/dmenu/modules/Application.py
Normal file
@@ -0,0 +1,128 @@
|
||||
from .Module import Module as _Module
|
||||
import locale as _locale
|
||||
import subprocess as _subprocess
|
||||
import shlex as _shlex
|
||||
from pathlib import Path as _Path
|
||||
from os import listdir as _listdir
|
||||
from os.path import isfile as _isfile, join as _join
|
||||
|
||||
|
||||
class Application(_Module):
|
||||
|
||||
def __init__(self):
|
||||
self.db_type = "Application"
|
||||
self._home = str(_Path.home())
|
||||
self._lang = _locale.getlocale()[0].split("_")[0].strip()
|
||||
self._dirs = ["/usr/share/applications", self._home + "/.local/share/applications"]
|
||||
self._references = {}
|
||||
|
||||
def build_db(self):
|
||||
"""Function which adds entries to the database.
|
||||
returns dict with:
|
||||
Name, json, disabled, type, "file" """
|
||||
db = {}
|
||||
for d in self._dirs:
|
||||
for fi in _listdir(d):
|
||||
if _isfile(_join(d, fi)):
|
||||
fc = self._scan_file(_join(d, fi))
|
||||
for k, v in fc.items():
|
||||
db.update({k: {
|
||||
"Name": k,
|
||||
"json": v,
|
||||
"disabled": (("NoDisplay" in v and v["NoDisplay"] == "true") or
|
||||
("Hidden" in v and v["Hidden"] == "true")),
|
||||
"file": v['file'],
|
||||
"type": self.db_type
|
||||
}})
|
||||
return db
|
||||
|
||||
def update_db(self, database: dict):
|
||||
"""Function which updates entries in the database.
|
||||
returns dict with modified/deleted/added database entries"""
|
||||
entries = self.build_db()
|
||||
dbremove = []
|
||||
# dbKey == id, dbValue dict of fileds
|
||||
for dbKey, dbValue in database:
|
||||
if dbValue["type"] == self.db_type:
|
||||
if dbValue["Name"] not in entries:
|
||||
dbremove.append(dbKey)
|
||||
else:
|
||||
# update all values managed by this module
|
||||
for k, v in entries[dbValue["Name"]]:
|
||||
dbValue[k] = v
|
||||
# remove no more existing entries
|
||||
for i in dbremove:
|
||||
database.pop(i)
|
||||
return database
|
||||
|
||||
def build_menu(self, items: dict):
|
||||
"""Builds the menu entries.
|
||||
return list of tuples (id, name, hits)"""
|
||||
# we only get entries of our app type so no problem here
|
||||
# json is already unjsonified
|
||||
NAME_PREFIX="run "
|
||||
NAME_POSTFIX=" (%PATH%)"
|
||||
entries = []
|
||||
for r in items:
|
||||
identifier=(NAME_PREFIX + r['Name'] + NAME_POSTFIX).replace("%PATH%", r['json']['Exec'])
|
||||
entries.append((r['ID'], identifier, r['hits']))
|
||||
if ("Terminal" not in r["json"]):
|
||||
r['json']["Terminal"] = "false"
|
||||
self._references[identifier] = (r['json']["Exec"], r['json']["Terminal"], r['file'], r['Name'])
|
||||
|
||||
return entries
|
||||
|
||||
def call(self, cmd: str):
|
||||
"""Handles the selected entry"""
|
||||
# find the entry
|
||||
reference = list(filter(lambda x: cmd.startswith(x), self._references.keys()))
|
||||
entry = reference[0]
|
||||
cmd = cmd.replace(entry, "").lstrip()
|
||||
# Just call the program
|
||||
_subprocess.Popen(_shlex.split(self._expand_fieldcodes(self._references[entry][0], cmd, reference)),
|
||||
encoding="utf-8",
|
||||
shell=(self._references[entry][1] == "true"))
|
||||
|
||||
def _scan_file(self, fi):
|
||||
entries = {}
|
||||
with open(fi, encoding="utf-8", mode="r") as f:
|
||||
data = {}
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith("["): # and line.rstrip() != "[Desktop Entry]":
|
||||
data["header"] = line.strip("[]")
|
||||
if "Name" in data and "Exec" in data:
|
||||
data['file'] = fi
|
||||
entries[data["Name"]] = data
|
||||
data = {}
|
||||
if "=" in line:
|
||||
s = line.split("=")
|
||||
if "[" in s[0] and "]" in s[0]:
|
||||
if "[" + self._lang + "]" in s[0]:
|
||||
data[s[0].replace("[" + self._lang + "]", "").strip()] = s[1]
|
||||
elif s[0] != "Name" or "Name" not in data.keys():
|
||||
data[s[0]] = s[1]
|
||||
|
||||
if "Name" in data:
|
||||
data['file'] = fi
|
||||
entries[data["Name"]] = data
|
||||
return entries
|
||||
|
||||
def _expand_fieldcodes(self, entry: str, cmd: str, reference):
|
||||
fieldcodes = ['%f', '%F', '%u', '%U', '%i', '%c', '%k'] # todo implement %i as expand icon-key
|
||||
for fc in range(0, len(fieldcodes)):
|
||||
fieldcodes[fc] = fieldcodes[fc] in entry
|
||||
if True in fieldcodes:
|
||||
if '%k' in entry:
|
||||
entry = entry.replace('%k', '"' + reference[2] + "'")
|
||||
if '%c' in entry:
|
||||
entry = entry.replace('%c', '"' + reference[3] + "'")
|
||||
if '%i' in entry:
|
||||
entry = entry.replace(' %i', "")
|
||||
if '%f' in entry or '%F' in entry or '%u' in entry or '%U' in entry:
|
||||
entry = entry.replace('%f', cmd)
|
||||
entry = entry.replace('%F', cmd)
|
||||
entry = entry.replace('%u', cmd)
|
||||
entry = entry.replace('%U', cmd)
|
||||
cmd = ""
|
||||
return (entry + " " + cmd).rstrip()
|
118
i3/scripts/dmenu/modules/MPC.py
Normal file
118
i3/scripts/dmenu/modules/MPC.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from .Module import Module as _Module
|
||||
import locale as _locale
|
||||
from pathlib import Path as _Path
|
||||
from Dmenu import Dmenu
|
||||
import subprocess as _subprocess
|
||||
|
||||
|
||||
class MPC(_Module):
|
||||
|
||||
def __init__(self):
|
||||
self.db_type = "MPC"
|
||||
self._home = str(_Path.home())
|
||||
self._lang = _locale.getlocale()[0].split("_")[0].strip()
|
||||
self._dirs = ["/usr/share/applications", self._home + "/.local/share/applications"]
|
||||
self.replace = True
|
||||
|
||||
def build_db(self):
|
||||
"""Function which adds entries to the database.
|
||||
returns dict with:
|
||||
Name, json, disabled, type, "file" """
|
||||
|
||||
# we only add an generic Submenu entry.
|
||||
db = {"Media Player Control [MPC]": {
|
||||
"Name": "Media Player Control [MPC]",
|
||||
"json": "",
|
||||
"disabled": False,
|
||||
"file": "none",
|
||||
"type": self.db_type
|
||||
}}
|
||||
return db
|
||||
|
||||
def update_db(self, database: dict):
|
||||
"""Function which updates entries in the database.
|
||||
returns dict with modified/deleted/added database entries"""
|
||||
return self.build_db()
|
||||
|
||||
def build_menu(self, items: dict):
|
||||
"""Builds the menu entries.
|
||||
return list of tuples (id, name, hits)"""
|
||||
# we only get entries of our app type so no problem here
|
||||
# json is already unjsonified
|
||||
entries = []
|
||||
for r in items:
|
||||
entries.append((r['ID'], r['Name'], r['hits']))
|
||||
|
||||
return entries
|
||||
|
||||
def gatherInfo(self):
|
||||
out = {}
|
||||
infoprocess = _subprocess.run("mpc", stdout=_subprocess.PIPE, shell=True)
|
||||
info = infoprocess.stdout.decode("utf8").split("\n")
|
||||
if len(info) == 4:
|
||||
status = [x.split(":") for x in info[2].split(" ")]
|
||||
|
||||
out["song"] = info[0]
|
||||
out["playing"] = "[playing]" in info[1]
|
||||
|
||||
else:
|
||||
status = [x.split(":") for x in info[0].split(" ")]
|
||||
|
||||
out["replace"] = self.replace
|
||||
status = [x for x in status if len(x) == 2]
|
||||
|
||||
for i in status:
|
||||
i[0] = i[0].strip()
|
||||
i[1] = i[1].strip()
|
||||
if i[1] == "off":
|
||||
out[i[0]] = False
|
||||
elif i[1] == "on":
|
||||
out[i[0]] = True
|
||||
else:
|
||||
out[i[0]] = i[1]
|
||||
return out
|
||||
|
||||
def buildStatusMenu(self):
|
||||
info = self.gatherInfo()
|
||||
if info["replace"]:
|
||||
playlists = ['MODE: REPLACE']
|
||||
else:
|
||||
playlists = ['MODE: APPEND']
|
||||
|
||||
if info["random"]:
|
||||
playlists += ['RANDOM: on']
|
||||
else:
|
||||
playlists += ['RANDOM: off']
|
||||
|
||||
return (len(playlists), playlists)
|
||||
|
||||
def handleStatusMenu(self, text: str):
|
||||
if text.startswith("MODE: "):
|
||||
self.replace = not self.replace
|
||||
elif text.startswith("RANDOM: "):
|
||||
_subprocess.run("mpc random", shell=True)
|
||||
|
||||
def call(self, cmd: str):
|
||||
"""Handles the selected entry"""
|
||||
# our menu entry was selected, time to create and show the submenu
|
||||
while True:
|
||||
infolen, playlists = self.buildStatusMenu()
|
||||
|
||||
playprocess = _subprocess.run("mpc lsplaylists", stdout=_subprocess.PIPE, shell=True)
|
||||
playlists += playprocess.stdout.decode("utf8").split("\n")
|
||||
menu = Dmenu(playlists)
|
||||
ex = menu.run()
|
||||
|
||||
if ex[0] == 0:
|
||||
if ex[1].rstrip() in playlists[0:infolen]:
|
||||
self.handleStatusMenu(ex[1].rstrip())
|
||||
continue
|
||||
# user selected an entry
|
||||
# mpc clear(?) -> mpc load ex[1] -> mpc play
|
||||
command = "mpc load '" + ex[1].rstrip() + "'"
|
||||
if self.replace:
|
||||
command = "mpc clear; " + command + "; mpc play"
|
||||
_subprocess.run(command, shell=True)
|
||||
break
|
||||
else:
|
||||
break
|
25
i3/scripts/dmenu/modules/Module.py
Normal file
25
i3/scripts/dmenu/modules/Module.py
Normal file
@@ -0,0 +1,25 @@
|
||||
class Module(object):
|
||||
"""Abstract class, implement: build_db, update_db, build_menu, call"""
|
||||
|
||||
def __init__(self, obj):
|
||||
pass
|
||||
|
||||
def build_db():
|
||||
"""Function which adds entries to the database.
|
||||
returns dict with:
|
||||
Name, json, disabled, type, "file" """
|
||||
raise NotImplementedError("Should have implemented this")
|
||||
|
||||
def update_db(database: dict):
|
||||
"""Function which updates entries in the database.
|
||||
returns dict with modified/deleted/added database entries"""
|
||||
raise NotImplementedError("Should have implemented this")
|
||||
|
||||
def build_menu(items: dict):
|
||||
"""Builds the menu entries.
|
||||
return list of tuples (id, name, hits)"""
|
||||
raise NotImplementedError("Should have implemented this")
|
||||
|
||||
def call(entry: str):
|
||||
"""Handles the selected entry"""
|
||||
raise NotImplementedError("Should have implemented this")
|
2
i3/scripts/dmenu/modules/__init__.py
Normal file
2
i3/scripts/dmenu/modules/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from .Application import Application
|
||||
from .MPC import MPC
|
BIN
i3/scripts/dmenu/modules/__pycache__/Application.cpython-36.pyc
Normal file
BIN
i3/scripts/dmenu/modules/__pycache__/Application.cpython-36.pyc
Normal file
Binary file not shown.
BIN
i3/scripts/dmenu/modules/__pycache__/MPC.cpython-36.pyc
Normal file
BIN
i3/scripts/dmenu/modules/__pycache__/MPC.cpython-36.pyc
Normal file
Binary file not shown.
BIN
i3/scripts/dmenu/modules/__pycache__/Module.cpython-36.pyc
Normal file
BIN
i3/scripts/dmenu/modules/__pycache__/Module.cpython-36.pyc
Normal file
Binary file not shown.
BIN
i3/scripts/dmenu/modules/__pycache__/__init__.cpython-36.pyc
Normal file
BIN
i3/scripts/dmenu/modules/__pycache__/__init__.cpython-36.pyc
Normal file
Binary file not shown.
Reference in New Issue
Block a user