Skip to content

Instantly share code, notes, and snippets.

@tung
Created February 3, 2016 11:44
Show Gist options
  • Save tung/19cf304700c4c1f85e81 to your computer and use it in GitHub Desktop.
Save tung/19cf304700c4c1f85e81 to your computer and use it in GitHub Desktop.
Tomboy-like recent note list for Gnote Unity launcher icon right-click menu
#!/usr/bin/env python3
#
# gnote-unity.py3
#
# Shows a Tomboy-like list of recent notes when the Gnote launcher is right-clicked.
# Clicking on a note opens it.
# Notes are sorted by last modification time and update dynamically.
#
# To use, just run this at login time, e.g. set under Startup Applications.
#
from gi.repository import Unity, GObject, Dbusmenu
from datetime import datetime
from dateutil.tz import tzlocal, tzutc
import glob, os
g_gnote_path = '/usr/bin/gnote'
g_gnote_home = os.getenv('HOME') + '/.local/share/gnote'
g_note_list = None
g_note_list_max = 8
g_gnote_home_mtime = None
g_launcher = Unity.LauncherEntry.get_for_desktop_id("gnote.desktop")
g_qlist = None
g_qlist_items = []
class Note:
def __init__(self, filename, title, date):
self.filename = filename
self.title = title
self.date = date
def get_tag(tag_name, line):
open_tag = '<' + tag_name + '>'
close_tag = '</' + tag_name + '>'
start = line.find(open_tag)
if start != -1:
tag_content = line[start + len(open_tag):]
end = tag_content.find(close_tag)
if end != -1:
tag_content = tag_content[:end]
return tag_content
return None
def insert_recent_note(nlist, nlist_max, entry):
if entry.title == 'New Note Template' or entry.title == 'Using Links in Gnote':
return
insert_pos = None
for pos in range(0, len(nlist)):
if entry.date > nlist[pos].date:
insert_pos = pos
break
if insert_pos == None:
if len(nlist) < nlist_max:
nlist.append(entry)
else:
nlist.insert(insert_pos, entry)
if len(nlist) > nlist_max:
del nlist[nlist_max]
def find_recent_notes():
global g_gnote_home_mtime
global g_note_list
global g_gnote_home
global g_note_list_max
# Only update note list if the directory was modified since last time.
check_path_stat = os.stat(g_gnote_home)
check_path_mtime = datetime.fromtimestamp(check_path_stat.st_mtime, tzlocal())
if g_gnote_home_mtime != None and check_path_mtime <= g_gnote_home_mtime:
return
g_gnote_home_mtime = check_path_mtime
# Check all the notes for updates.
g_note_list = []
for filename in glob.iglob(g_gnote_home + '/*.note'):
# Short-circuit if the file modified time is older than all the list notes.
filestat = os.stat(filename)
mtime = datetime.fromtimestamp(filestat.st_mtime, tzlocal())
if len(g_note_list) >= g_note_list_max and mtime < g_note_list[-1].date:
continue
# Parse the note and maybe insert it.
title = None
last_change_date = None
with open(filename, encoding='utf-8') as note_file:
for note_line in note_file:
if not title:
title = get_tag('title', note_line)
if not last_change_date:
last_change_date_str = get_tag('last-change-date', note_line)
if last_change_date_str:
# Gnote stores times in UTC
last_change_date = datetime.strptime(last_change_date_str, '%Y-%m-%dT%H:%M:%S.%fZ')
last_change_date = last_change_date.replace(tzinfo=tzutc())
if title and last_change_date:
entry = Note(filename, title, last_change_date)
insert_recent_note(g_note_list, g_note_list_max, entry)
break
def open_note(a, b):
global g_gnote_path
note_title = a.property_get(Dbusmenu.MENUITEM_PROP_LABEL)
if os.fork() == 0:
os.execl(g_gnote_path, g_gnote_path, '--open-note', note_title)
exit(1)
def init_quicklist():
global g_note_list_max
global g_launcher
global g_qlist
global g_qlist_items
g_qlist = Dbusmenu.Menuitem.new()
for i in range(g_note_list_max):
item = Dbusmenu.Menuitem.new()
item.connect('item-activated', open_note)
g_qlist.child_append(item)
g_qlist_items.append(item)
g_launcher.set_property('quicklist', g_qlist)
def update_quicklist():
global g_note_list
global g_note_list_max
global g_qlist_items
find_recent_notes()
for i in range(len(g_note_list)):
g_qlist_items[i].property_set(Dbusmenu.MENUITEM_PROP_LABEL, g_note_list[i].title)
g_qlist_items[i].property_set_bool(Dbusmenu.MENUITEM_PROP_VISIBLE, True)
if len(g_note_list) < g_note_list_max:
for i in range(len(g_note_list), g_note_list_max):
g_qlist_items[i].property_set_bool(Dbusmenu.MENUITEM_PROP_VISIBLE, False)
return True
if __name__ == '__main__':
init_quicklist()
update_quicklist()
GObject.timeout_add_seconds(5, update_quicklist)
GObject.MainLoop().run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment