aboutsummaryrefslogtreecommitdiff
path: root/automount-trayicon.py
diff options
context:
space:
mode:
Diffstat (limited to 'automount-trayicon.py')
-rwxr-xr-xautomount-trayicon.py255
1 files changed, 255 insertions, 0 deletions
diff --git a/automount-trayicon.py b/automount-trayicon.py
new file mode 100755
index 0000000..7e11879
--- /dev/null
+++ b/automount-trayicon.py
@@ -0,0 +1,255 @@
+#!/usr/bin/python3
+# startdate: 2020-09-24 13:17
+# References:
+# vm2:~/dev/logout-manager/src/usr/bin/logout-manager-trayicon
+# gapan/xdgmenumaker
+# vim: ts=3 sw=3 sts=3
+# IMPROVE:
+# use inotify for when to call reestablish_menu?
+# move out config to separate file (read main .conf, but in shell format)
+# add separator and "hide this icon" menu item like logout-manager-trayicon
+# fix tabbing
+# remove /sdb? (as option)
+# hook up the click action to actually executing "xdg-open /browse/sdb"
+
+import gi, os, fnmatch, sys
+gi.require_version("Gtk","3.0")
+from gi.repository import Gtk
+from gi.repository import Gdk
+import xdg.DesktopEntry as dentry
+import xdg.Exceptions as exc
+
+# FUNCTIONS
+
+class App:
+ '''
+ A class to keep individual app details in.
+ '''
+
+ def __init__(self, name, icon, command, path):
+ self.name = name
+ self.icon = icon
+ self.command = command
+ self.path = path
+
+ def __repr__(self):
+ return repr((self.name, self.icon, self.command,
+ self.path))
+
+class MenuEntry:
+ '''
+ A class for each menu entry. Includes the class category and app details
+ from the App class.
+ '''
+
+ def __init__(self, category, app):
+ self.category = category
+ self.app = app
+
+ def __repr__(self):
+ return repr((self.category, self.app.name, self.app.icon,
+ self.app.command, self.app.path))
+
+def remove_command_keys(command, desktopfile, icon):
+ # replace the %i (icon key) if it's there. This is what freedesktop has to
+ # say about it: "The Icon key of the desktop entry expanded as two
+ # arguments, first --icon and then the value of the Icon key. Should not
+ # expand to any arguments if the Icon key is empty or missing."
+ if icon:
+ command = command.replace('"%i"', '--icon {}'.format(icon))
+ command = command.replace("'%i'", '--icon {}'.format(icon))
+ command = command.replace('%i', '--icon {}'.format(icon))
+ # some KDE apps have this "-caption %c" in a few variations. %c is "The
+ # translated name of the application as listed in the appropriate Name key
+ # in the desktop entry" according to freedesktop. All apps launch without a
+ # problem without it as far as I can tell, so it's better to remove it than
+ # have to deal with extra sets of nested quotes which behave differently in
+ # each WM. This is not 100% failure-proof. There might be other variations
+ # of this out there, but we can't account for every single one. If someone
+ # finds one another one, I can always add it later.
+ command = command.replace('-caption "%c"', '')
+ command = command.replace("-caption '%c'", '')
+ command = command.replace('-caption %c', '')
+ # replace the %k key. This is what freedesktop says about it: "The
+ # location of the desktop file as either a URI (if for example gotten from
+ # the vfolder system) or a local filename or empty if no location is
+ # known."
+ command = command.replace('"%k"', desktopfile)
+ command = command.replace("'%k'", desktopfile)
+ command = command.replace('%k', desktopfile)
+ # removing any remaining keys from the command. That can potentially remove
+ # any other trailing options after the keys,
+ command = command.partition('%')[0]
+ return command
+
+def icon_strip(icon):
+ # strip the directory and extension from the icon name
+ icon = os.path.basename(icon)
+ main, ext = os.path.splitext(icon)
+ ext = ext.lower()
+ if ext == '.png' or ext == '.svg' or ext == '.svgz' or ext == '.xpm':
+ return main
+ return icon
+
+def get_entry_info(desktopfile, ico_paths=True):
+ # customized from gapan/xdgmenumaker
+ de = dentry.DesktopEntry(filename=desktopfile)
+
+ name = de.getName().encode('utf-8')
+
+ if True:
+ icon = de.getIcon()
+ # full resolution of path is not required in this GTK program.
+ #if ico_paths:
+ # icon = icon_full_path(icon)
+ #else:
+ # icon = icon_strip(icon)
+ else:
+ icon = None
+
+ command = de.getExec()
+ command = remove_command_keys(command, desktopfile, icon)
+
+ terminal = de.getTerminal()
+ if terminal:
+ command = '{term} -e {cmd}'.format(term=terminal_app, cmd=command)
+
+ path = de.getPath()
+ if not path:
+ path = None
+
+ categories = de.getCategories()
+ category = "Removable" # hardcoded xdg "category" for automount-trayicon
+
+ app = App(name, icon, command, path)
+ mentry = MenuEntry(category, app)
+ return mentry
+
+def desktopfilelist(params):
+ # Ripped entirely from gapan/xdgmenumaker
+ dirs = []
+ for i in params:
+ print("Checking dir",i)
+ i = i.rstrip('/')
+ if i not in dirs:
+ dirs.append(i)
+ filelist = []
+ df_temp = []
+ for d in dirs:
+ xdgdir = '{}/'.format(d) # xdg spec is to use "{}/applications" as this string, so use an applications subdir.
+ if os.path.isdir(xdgdir):
+ for root, dirnames, filenames in os.walk(xdgdir):
+ for i in fnmatch.filter(filenames, '*.desktop'):
+ # for duplicate .desktop files that exist in more
+ # than one locations, only keep the first occurence.
+ # That one should have precedence anyway (e.g.
+ # ~/.local/share/applications has precedence over
+ # /usr/share/applications
+ if i not in df_temp:
+ df_temp.append(i)
+ filelist.append(os.path.join(root, i))
+ return filelist
+
+class MainIcon(Gtk.StatusIcon):
+ def __init__(self):
+ Gtk.StatusIcon.__init__(self)
+ self.set_from_icon_name("media-removable")
+ self.traymenu = Gtk.Menu()
+ self.connect("button-press-event", self.on_button_press_event)
+ self.connect("popup-menu", self.context_menu)
+ self.reestablish_menu()
+
+ def reestablish_menu(self,widget = None):
+ try:
+ if self.menuitems:
+ del self.menuitems
+ except:
+ pass
+ try:
+ for i in self.traymenu.get_children():
+ self.traymenu.remove(i)
+ except:
+ pass
+ self.menuitems = []
+ for entry in get_desktop_entries(["/media"]):
+ print('Label {} icon {}'.format(entry.app.name,entry.app.icon))
+ self.add_action_to_menu(str(entry.app.name.decode("utf-8")),entry.app.path,entry.app.icon,self.dummy_action,entry.app.command)
+ #self.add_action_to_menu("foobar"+str(myrand),"media-floppy",self.dummy_action,"foo bar baz action "+str(myrand))
+ #self.add_action_to_menu("mmc","media-flash-sd-mmc",self.dummy_action,"flubber second action")
+ #self.add_action_to_menu("what is this?","media-tape",self.dummy_action,"call ALF!")
+ self.add_separator_to_menu()
+ self.add_action_to_menu("Update menu","",None,self.reestablish_menu,"re establish")
+ self.add_action_to_menu("Hide this icon","","system-logout",self.exit,"exit")
+ self.menuitem_count = len(self.menuitems) - 1
+ self.set_tooltip_text(str(self.menuitem_count)+" mount point"+("s" if self.menuitem_count > 1 else ""))
+
+ def UNUSED_transform_command(self, entry_command):
+ return entry_command.replace("xdg-open ","umount ")
+
+ def dummy_action(self, widget):
+ print("hello world")
+ x=0
+ y=-1
+ for n in widget.get_parent().get_children():
+ x+=1
+ if widget == n:
+ y=x-1
+ break
+ print(y)
+ print(self.menuitems[y])
+ def add_separator_to_menu(self):
+ i=Gtk.SeparatorMenuItem.new()
+ i.set_visible(True)
+ self.traymenu.append(i)
+ def add_action_to_menu(self,label_str,label_paren_str,icon_str,function_func,action_str):
+ self.menuitems.append(action_str)
+ full_label_str=label_str
+ if label_paren_str is not None and label_paren_str != "":
+ full_label_str += " (" + label_paren_str + ")"
+ i = Gtk.ImageMenuItem.new_with_mnemonic(full_label_str)
+ j = Gtk.Image.new_from_icon_name(icon_str,32)
+ j.show()
+ i.set_image(j)
+ i.set_always_show_image(True)
+ i.show()
+ i.connect("activate", function_func)
+ self.traymenu.append(i)
+ def on_button_press_event(self, b_unknown, event: Gdk.EventButton):
+ # for some reason the single click functionality prevents the double click functionality
+ if Gdk.EventType._2BUTTON_PRESS == event.type:
+ print("Double click!")
+ else:
+ print("Single click")
+ self.traymenu.popup(None, None,
+ self.position_menu,
+ self,
+ event.button,
+ Gtk.get_current_event_time())
+ def context_menu(self, widget, event_button, event_time):
+ self.traymenu.popup(None, None,
+ self.position_menu,
+ self,
+ event_button,
+ Gtk.get_current_event_time())
+ def exit(self, widget):
+ quit()
+
+#print(desktopfilelist(["/media"]))
+def get_desktop_entries(dir_array):
+ entries = []
+ for desktopfile in desktopfilelist(dir_array):
+ try:
+ entry = get_entry_info(desktopfile, True)
+ if entry is not None:
+ #print(entry)
+ entries.append(entry)
+ except exc.ParsingError as Ex:
+ print(Ex)
+ pass
+ return entries
+
+#for entry in get_desktop_entries(["/media"]):
+# print('label {} icon {}'.format(entry.app.name,entry.app.icon))
+# MAIN
+icon = MainIcon()
+Gtk.main()
bgstack15