Ubuntu – n indicator to quickly access recently used files

filesindicatorsoftware-recommendationsystem-trayunity

I am looking for a tray icon indicator that – upon click on the icon – shows me the list of my recently used files. This would be a great way to quickly access these files.

Best Answer

Have your recently used files in the panel

With the script below, you can have an arbitrary number of recently used items at hand in the panel, e.g. 6 items:

enter image description here

...or 20 items:

enter image description here

...depending on your settings.

How it works

The setup exists of two items:

  1. an icon, named (exactly) recent.png
  2. a script

Both need to be in one and the same folder. after that, simply run the script.

How to set up

Possibly, you need to install python3-gi:

sudo apt-get install python3-gi

Then:

  1. Copy the script below into an empty file, save it as recused.py

    #!/usr/bin/env python3
    import signal
    import gi
    gi.require_version('Gtk', '3.0')
    gi.require_version('AppIndicator3', '0.1')
    from gi.repository import Gtk, AppIndicator3, GObject
    import time
    from threading import Thread
    import os
    import subprocess
    
    # --- set the number of recently used files to appear below
    n = 20
    # ---
    
    home = os.environ["HOME"]
    recdata = os.path.join(home, ".local/share/recently-used.xbel")
    currpath = os.path.dirname(os.path.realpath(__file__))
    
    class Indicator():
        def __init__(self):
            self.app = 'show_recent'
            iconpath = os.path.join(currpath, "recent.png")
            self.indicator = AppIndicator3.Indicator.new(
                self.app, iconpath,
                AppIndicator3.IndicatorCategory.OTHER)
            self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)       
            self.indicator.set_menu(self.create_menu())
            # the thread:
            self.update = Thread(target=self.check_recent)
            # daemonize the thread to make the indicator stopable
            self.update.setDaemon(True)
            self.update.start()
    
        def get_files(self):
            # create the list of recently used files
            used = [l for l in open(recdata) if \
                    all([
                        '<bookmark href="file://' in l,
                        not "/tmp" in l,
                        "." in l,
                         ])]
            relevant = [l.split('="') for l in set(used)]
            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_menu(self):
            # creates the (initial) menu
            self.menu = Gtk.Menu()
            # separator
            menu_sep = Gtk.SeparatorMenuItem()
            self.menu.append(menu_sep)
            # item_quit.show() 
            self.menu.show_all()
            return self.menu
    
        def open_file(self, *args):
            # opens the file with the default application
            index = self.menu.get_children().index(self.menu.get_active())
            selection = self.menu_items2[index]
            subprocess.Popen(["xdg-open", selection])
    
        def set_new(self):
            # update the list, appearing in the menu
            for i in self.menu.get_children():
                self.menu.remove(i)
            for file in self.menu_items2:
                sub = Gtk.MenuItem(file)
                self.menu.append(sub)
                sub.connect('activate', self.open_file)
            # separator
            menu_sep = Gtk.SeparatorMenuItem()
            self.menu.append(menu_sep)
            # quit
            item_quit = Gtk.MenuItem('Quit')
            item_quit.connect('activate', self.stop)
            self.menu.append(item_quit)
            self.menu.show_all()
    
        def check_recent(self):
            self.menu_items1 = []
            while True:
                time.sleep(3)
                self.menu_items2 = self.get_files()
                if self.menu_items2 != self.menu_items1:
                    GObject.idle_add(
                        self.set_new, 
                        priority=GObject.PRIORITY_DEFAULT
                        )
                self.menu_items1 = self.menu_items2
    
        def stop(self, source):
            Gtk.main_quit()
    
    Indicator()
    # this is where we call GObject.threads_init()
    GObject.threads_init()
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    Gtk.main()
    
  2. In the head section of the script, set the number of items to show:

    # --- set the number of recently used files to appear below
    n = 20
    # ---
    
  3. In one and the same folder save the icon below as (exactly) recent.png

    enter image description here

    (right-click on it -> save as)

  4. Test- run the script by the command:

    python3 /path/to/recused.py
    
  5. If all works fine, add to Startup Applications: Dash > Startup Applications > Add. Add the command:

    /bin/bash -c "sleep 15 && python3 /path/to/recused.py"
    

Notes

  • This script was based on this previous answer, in case you are interested in an alternative, be it that that one was application-specific, and used a periodically updated .desktop file.
  • The script was tested on 16.04, but should work on earlier versions as well.
  • Due to a bug in the recently-used.xbelfile, sometimes a file is mentioned twice; once with, and once without extension. I solved that by filtering the latter out. The consequence is that files without extension won't appear in the list. If that is an issue, let me know, we can try to find another fix in that case.
  • If a file does not exist (anymore), the indicator simply (obviously) does not open the file. Most likely, I will polish the script a bit more to filter out those outdated entries from the recently-used.xbel file.

Explanation

Many applications, editing files and using a Gtk window, keep track of opened files in the file: ~/.local/share/recently-used.xbel. "Records" include the date & time, the application and the file that was opened.

The script reads the .xbel file, sorts the items by date/time, includes the first n- (depending on your settings) files in the menu of the indicator. The menu is updated (only if necessary) every 3 seconds as it is. Subsequently, when an item is selected, the file is opened with the command:

xdg-open <file>

Thus the selected file will be opened with the default application. It is very well possible to make sure the file is opened with the actual application it was last opened. That takes a somewhat more sophisticated parsing however. I will add that as an option to the planned ppa version on Launchpad.

The same goes for an option window to set a few options, like the number of files to show etc.

Note

The indicator is now merged with a few other things in this one.