aboutsummaryrefslogtreecommitdiff
path: root/automount-trayicon.py
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2020-09-25 20:39:41 -0400
committerB Stack <bgstack15@gmail.com>2020-09-25 20:39:41 -0400
commitcee836f71e191e51dd48d34633bfd8c977ba3b8a (patch)
tree5cf0cbf47cea50276f2dc853500400554d04bd04 /automount-trayicon.py
parentprovide working trayicon now (diff)
downloadmyautomount-cee836f71e191e51dd48d34633bfd8c977ba3b8a.tar.gz
myautomount-cee836f71e191e51dd48d34633bfd8c977ba3b8a.tar.bz2
myautomount-cee836f71e191e51dd48d34633bfd8c977ba3b8a.zip
rename to myautomount
Add annotations to conf file, separate out initialization so daemon can be per-user. Use a sane name.
Diffstat (limited to 'automount-trayicon.py')
-rwxr-xr-xautomount-trayicon.py342
1 files changed, 0 insertions, 342 deletions
diff --git a/automount-trayicon.py b/automount-trayicon.py
deleted file mode 100755
index 84312e7..0000000
--- a/automount-trayicon.py
+++ /dev/null
@@ -1,342 +0,0 @@
-#!/usr/bin/python3
-# File: automount-trayicon.py
-# startdate: 2020-09-24 13:17
-# References:
-# https://gitlab.com/bgstack15/logout-manager/-/blob/master/src/usr/bin/logout-manager-trayicon
-# https://github.com/gapan/xdgmenumaker/blob/master/src/xdgmenumaker
-# https://github.com/seb-m/pyinotify/blob/master/python2/examples/stats_threaded.py
-# https://stackoverflow.com/questions/28279363/python-way-to-get-mounted-filesystems/28279434#28279434
-# timer https://python-gtk-3-tutorial.readthedocs.io/en/latest/spinner.html?highlight=timer#id1
-# vim: ts=3 sw=3 sts=3
-# Improve:
-# move out config to separate file (read main .conf, but in shell format)
-# add all headers
-# document
-# Dependencies:
-# autofs, root running the included stackrpms-automount
-
-import gi, os, fnmatch, sys, pyinotify, time, subprocess
-gi.require_version("Gtk","3.0")
-from gi.repository import Gtk, Gdk, GLib
-import xdg.DesktopEntry as dentry
-import xdg.Exceptions as exc
-
-showmount = 1 # show the "MOUNTED" value for each drive
-skip_sd_without_partitions = 1 # skip sdb sdc, etc. # NOT IMPLEMENTED YET.
-only_update_on_menuitem = 0 # if 1, then disables the mouseover poll. Prevents showmount=1 from working.
-AUTOMOUNT_BASEDIR="/media"
-AUTOMOUNT_BROWSEDIR="/browse"
-
-# 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))
-
-class Identity(pyinotify.ProcessEvent):
- activity_count = 0
- def process_default(self, event):
- print(self,"Does nothing with the event number",self.activity_count)
- self.activity_count += 1
- #print(event)
-
-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()
- if not only_update_on_menuitem:
- self.connect("query-tooltip", self.mouseover)
- # need these anyway, for when the icon is hidden.
- self.wm1 = pyinotify.WatchManager()
- self.s1 = pyinotify.Stats()
- self.identity=Identity(self.s1) # provides self.identity.activity_counter which increments for every change to the watched directory
- self.activity_count=0 # the cached value, for comparison. Update the menu when this changes!
- self.notifier1 = pyinotify.ThreadedNotifier(self.wm1, default_proc_fun=self.identity)
- self.notifier1.start()
- self.wm1.add_watch(AUTOMOUNT_BASEDIR, pyinotify.IN_CREATE | pyinotify.IN_DELETE, rec=True, auto_add=True)
-
- def mouseover(self, second_self, x, y, some_bool, tooltip):
- #print("Mouseover happened at",str(x)+",",y,tooltip, some_bool)
- need_showmount=0
- if self.identity.activity_count != self.activity_count:
- # then we need to update menu
- print("Mouseover, and files have changed!")
- self.activity_count = self.identity.activity_count
- need_showmount=1
- if need_showmount or showmount:
- self.reestablish_menu()
-
- def reestablish_menu(self,widget = None, silent=False):
- if not silent:
- print("Reestablishing the menu")
- 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([AUTOMOUNT_BASEDIR]):
- if not silent:
- print('{} {} {} {}'.format(entry.app.name.decode("utf-8"),entry.app.icon,entry.app.path,entry.app.command))
- self.add_menuitem(str(entry.app.name.decode("utf-8")),entry.app.path,entry.app.icon,self.execute,entry.app.command)
- self.add_separator_to_menu()
- if only_update_on_menuitem:
- self.add_menuitem("Update menu","",None,self.reestablish_menu,"re establish") # If you want a menu option for this
- self.add_menuitem("Hide until next disk change","","",self.hide,"hide")
- self.add_menuitem("Exit automount-trayicon","","system-logout",self.exit,"exit")
- self.menuitem_count = len(self.menuitems) - 2
- self.set_tooltip_text(str(self.menuitem_count)+" mount point"+("s" if self.menuitem_count > 1 else ""))
-
- def execute(self, widget):
- x=0 ; y=-1
- for n in widget.get_parent().get_children():
- x+=1
- if widget == n:
- y=x-1
- break
- if y > -1:
- print(y,self.menuitems[y])
- # use subprocess to open in background so failure to mount dialogs do not block the program
- subprocess.Popen(self.menuitems[y].split())
- #os.system(self.menuitems[y])
- else:
- print("Some kind of error?! How did this get called without a menu entry.")
- #print(repr(self.s1))
-
- def add_separator_to_menu(self):
- i=Gtk.SeparatorMenuItem.new()
- i.set_visible(True)
- self.traymenu.append(i)
-
- def add_menuitem(self,label_str,label_paren_str,icon_str,function_func,action_str):
- self.menuitems.append(action_str)
- full_label_str=label_str
- mounted_str=""
- # collection = [line.split()[1] for line in open("/etc/mtab") if line.split()[1].startswith('/browse') and line.split()[2] != "autofs"]
- if label_paren_str is not None and label_paren_str != "":
- if showmount:
- if label_paren_str in [line.split()[1] for line in open("/etc/mtab") if line.split()[1].startswith(AUTOMOUNT_BROWSEDIR) and line.split()[2] != "autofs"]:
- label_paren_str = "MOUNTED " + 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:
- # not achievable if we are popping up the menu in the single click.
- 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 hide(self, widget = None):
- print("Please hide self!")
- self.set_visible(False)
- self.start_timer()
-
- def start_timer(self):
- """ Start the timer """
- self.counter = 1000 # milliseconds, but hardly a precise timer. Do not use this for real calculations
- self.timeout_id = GLib.timeout_add(1,self.on_timeout, None)
-
- def stop_timer(self, reason_str):
- if self.timeout_id:
- GLib.source_remove(self.timeout_id)
- self.timeout_id = None
- #print("Timer was stopped!")
- self.check_reenable_now()
-
- def check_reenable_now(self):
- print("Need to check if anything has changed.")
- print(self.identity.activity_count,self.activity_count)
- if self.identity.activity_count != self.activity_count:
- self.set_visible(True)
- self.activity_count = self.identity.activity_count
- self.reestablish_menu()
- else:
- print("No changes...")
- self.hide()
-
- def on_timeout(self, *args, **kwargs):
- """ A timeout function """
- self.counter -= 1
- if self.counter <= 0:
- self.stop_timer("Reached time out")
- return False
- return True
-
- def exit(self, widget):
- self.notifier1.stop()
- quit()
-
- def __quit__(self):
- self.notifier1.stop()
- Gtk.StatusIcon.__quit__(self)
-
-# Normally call get_desktop_entries with (["/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
-
-# MAIN
-icon = MainIcon()
-Gtk.main()
bgstack15