Adding a dynamic "recently used" section to the launcher of an application
The full integration of an application with the mentioned dynamic quicklist -entries most likely needs to be done from inside of the application. After all, the most direct information on used files comes from the application itself.
However, since editing the source code is outside the scope of what we're doing, that would not be the road to take.
Then what?
That doesn't mean we cannot achieve pretty much exactly the same result, maybe even in a more flexible and general way, from "outside". All the information we need is available in the dynamically updated file: ~/.local/share/recently-used.xbel
, from which we can retrieve the complete history of opened files, the corresponding date & time information and the application that was used.
Furthermore, to add a dynamically updated section to a launcher can very well be done as part of the "traditional" (static) section. The key of the solution is then to create a process that takes care of the above actions without adding a noticeable burden to your system.
As mentioned in the link from the question, some background process would be needed anyway to keep track of the changes and pass the instructions.
The script below is pretty much doing exactly that.
The solution; a background script
The values in the script below are specifically set for LibreOffice
and its documents. Without any editing, it can be used to add a recently used -section to the LibreOffice-Writer
launcher. It will show the last 10 used documents, opened by any of the LibreOffice
-modules.
The solution can however be used to add a "recently used" section to many applicatiosn with a .desktop
file in /usr/share/applications
. Since the file ~/.local/share/recently-used.xbel
is Gtk
related, most likely, applications with a Gtk
window will be our potential candidates (that is, if the application opens and edits files). Furthermore, the number of files to show is arbitrary.
How it looks
The solution adds a section to the targeted launcher in the Unity launcher, showing an arbitrary number of recently used files, e.g.:
show the last seven files:

or the last ten files:

With the same ease however, we can give the gedit
launcher a dynamic section, showing the last seven files, opened with gedit
(see image further below)
How to use
Assuming you have LibreOffice preinstalled (the downloaded version does not have a referring .desktop
file in /usr/share/applications
which is needed by the script, but somewhere else, please mention if you need to setup the separately downloaded LO version)
Copy the script below into an empty file, save it as dynamic_recent.py
For LibreOffice
, the process name is soffice
, already set correctly in the script.
#!/usr/bin/env python3
import subprocess
import os
import time
import shutil
# --- set the number of docs to show in recently used
n = 7
# --- set the process name of the targeted application
application = "soffice"
#--- ONLY change the value below into "xdg-open" if you do not use LO preinstalled
# else the value should be the same as in application = (above)
open_cmd = "soffice"
# --- set the targeted .desktop file (e.g. "gedit.desktop")
target = "libreoffice-writer.desktop"
# --- don't change anything below
home = os.environ["HOME"]+"/.local/share"
loc = home+"/applications/"+target
recdata = home+"/recently-used.xbel"
def runs(app):
try:
# see if the application is running
app = subprocess.check_output(["pgrep", app]).decode("utf-8")
except subprocess.CalledProcessError:
return False
else:
return True
def get_lines():
# retrieve information from the records:
# -> get bookmark line *if* the application is in the exec= line
with open(recdata) as infile:
db = []
for l in infile:
if '<bookmark href="file://' in l:
sub = l
elif 'exec="''+application in l:
db.append(sub)
# fix bug in xbel -file in 15.04
relevant = [l.split('="') for l in set(db) if all([not "/tmp" in l, "." in l])]
relevant = [[it[1][7:-7], it[-2][:-10]] for it in relevant]
relevant.sort(key=lambda x: x[1])
return [item[0].replace("%20", " ") for item in relevant[::-1][:n]]
def create_section(line):
# create shortcut section
name = line.split("/")[-1]
return [[
"[Desktop Action "+name+"]",
"Name="+name,
"Exec="+open_cmd+" '"+line+"'",
"\n",
], name]
def setup_dirs():
# copy the global .desktop file to /usr/share/applications/
glo = "/usr/share/applications/"+target
if not os.path.exists(loc):
shutil.copy(glo,loc)
def edit_launcher(newdyn, target, actionlist):
# read the current .desktop file
ql = [list(item) for item in list(enumerate(open(loc).read().splitlines()))]
# find the Actions= line
currlinks = [l for l in ql if "Actions=" in l[1]]
# split the line (if it exists) by the divider as delimiter
linkline = currlinks[0][1].split("divider1")[0] if currlinks else None
# define the shortcut sections, belonging to the dynamic section (below the divider)
lowersection = [l for l in ql if "[Desktop Action divider1]" in l]
# compose the new Actions= line
addlinks = (";").join(actionlist) + ";"
if linkline:
newlinks = linkline + addlinks
ql[currlinks[0][0]][1] = newlinks
# get rid of the "dynamic" section
ql = ql[:lowersection[0][0]] if lowersection else ql
# define the new file
ql = [it[1] for it in ql]+newdyn
with open(loc, "wt") as out:
for l in ql:
out.write(l+"\n")
else:
newlinks = "Acrions="+addlinks
setup_dirs()
lines1 = []
while True:
time.sleep(2)
# if the application does not run, no need for a check of .xbel
if runs(application):
lines2 = get_lines()
# (only) if the list of recently used changed: edit the quicklist
if lines1 != lines2:
actionlist = ["divider1"]
newdyn = [
"[Desktop Action divider1]",
"Name=" + 37*".",
"\n",
]
for line in lines2:
data = create_section(line)
actionlist.append(data[1])
section = data[0]
for l in section:
newdyn.append(l)
edit_launcher(newdyn, target, actionlist)
lines1 = lines2
In the head section of the script, you can set a number of options:
# --- set the number of docs to show in recently used
n = 7
# --- set the process name of the targeted application
application = "soffice"
#--- ONLY change the value below into "xdg-open" if you do not use LO preinstalled
# else the value should be the same as in application = (above)
open_cmd = "soffice"
# --- set the targeted .desktop file (e.g. "gedit.desktop")
target = "libreoffice-writer.desktop"
Most of the options speak for themselves, if you want to add the dynamic section to the LO-Writer
launcher, leave everything as it is. If not, set the appropriate launcher.
Test- run the script by running from a terminal:
python3 /path/to/dynamic_recent.py
The script copied the global .desktop
file to ~/.local/share/applications
(in this case ~/.local/share/applications/libreoffice-writer.desktop
). Drag the local copy to the launcher (else you'd need to log out/in).
If all works fine, add it to Startup Applications: Dash > Startup Applications > Add. Add the command:
python3 /path/to/dynamic_recent.py
To use it on other applications
As mentioned, you can easily use the script to add a dynamic "recently used" section to other application's launcher(s). To do so, see the gedit
example setting for the head section of the script:
# --- set the number of docs to show in recently used
n = 7
# --- set the process name of the targeted application
application = "gedit"
#--- ONLY change the value below into "xdg-open" if you do not use LO preinstalled
# else the value should be the same as in application = (above)
open_cmd = "gedit"
# --- set the targeted .desktop file (e.g. "gedit.desktop")
target = "gedit.desktop"

How it works
The script periodically looks through the file ~/.local/share/recently-used.xbel
to find matching files, opened with LibreOffice
(processname: soffice
)
It uses a pretty fast algorithm to do so, "shooting" through the file in a single pass, to retrieve the needed lines (two per "record"). The result is that the script is very low on juice.
Once the relevant lines are retrieved from the file, the lines are sorted by date/time, creating a "top ten" (or any other number) of most recently used files of the corresponding application.
- ONLY if this list is changed, the
.desktop
file is updated.
I could notice nor measure any additional load to my system, running the script in the background.
Tested on 14.04 / 15.10
How to restore the original launcher
Simply remove the local copy of the launcher in ~/.local/share/applications
Notes
In case you use Unity Quicklist Editor to edit your launchers (quicklists), you should avoid editing launchers with a dynamically updated "last used" -section from this answer. The edits you make with the QUicklist Editor will instantly be overwritten by the script.
You can edit your quicklist manually, but make sure you add new item before (on the left side of) divider1
in the Actions=
- line
Actions=Window;Document;
divider1;aap.sh;Todo;pscript_2.py;currdate;bulkmail_llJacob;verhaal;test doc;
All items on the right of divider1
belong to the dynamically updated section.
Major edit
Some major improvements were just made:
- The script now only checks the
.xbel
file while the targeted application runs (since there won't be changes on the recently used list if the application does not run). The script already was low on juice, but now, only keeping an eye on if the application runs, means even less to your system.
- In 15.04+, it turns out the
.xbel
file had double mentions of new files; one with, and one without extension. The effect of that is now eliminated.