diff --git a/readme b/readme
index b5061d8..0db9b4d 100644
--- a/readme
+++ b/readme
@@ -83,3 +83,33 @@ Deprecated in favor of sshplus.py
A sshmenu like alternative for appindicator menu, which supports sshmenu configuration file, thus becoming a drop in replacement on Ubuntu's unity launcher.
+ v1.1 Release notes: rnijenhu: Compartible with previous version but it will also
+ check /etc/sshplus/sshplus.cfg for more menus,
+ with this cfg file following options/functions becomes available as well:
+ + Support for multiple (global) menu files, just create
+ /etc/sshplus/sshplus.cfg (or a link to the network version)
+ + Simpel access control, which user is able too see the menu
+ + CLI option to install sshplus in the sys or user startup folder
+ + Support for icons before label/folder/task items
+ + Added a 'recently used' menu with auto refresh
+ + customizable text and icon for the indicator
+ + command availibility checking, if not within PATH the icon
+ displays an error (command not blocked).
+ Usefull when you got an application installed on several machines
+ but not on all
+ + folder IP checking, before a menu is available a specified IP
+ must be reachable. Especially for (local) ssh calls to machines
+ that are only available when a VPN is connected. A refresh after
+ setting up the connection will make the menu available.
+ This is (a little) time consuming,
+ use it wisely (and is it implemeted for folders only).
+ + Support for env vars in the config files
+ + menu files can be read from json as well, for humans I would recommend
+ the previous file format
+
+ sample files are provided in the subfolder 'sshplus'
+
+
+
+
+
diff --git a/sshplus.py b/sshplus.py
index 76448f7..b77f54b 100755
--- a/sshplus.py
+++ b/sshplus.py
@@ -28,6 +28,8 @@
# 3. Launch sshplus.py
# 4. Or better yet, add it to gnome startup programs list so it's run on login.
+#todo: global section ins sshplus.cfg is not working
+
import gobject
import gtk
import appindicator
@@ -36,17 +38,26 @@
import sys
import shlex
import re
+import ConfigParser
+#import subprocess
+import socket
+import getpass
+import json
-_VERSION = "1.0"
-_SETTINGS_FILE = os.getenv("HOME") + "/.sshplus"
-_SSHMENU_FILE = os.getenv("HOME") + "/.sshmenu"
+_USER=getpass.getuser()
+_VERSION = "1.1"
+_BIN_PATH=os.path.realpath(os.path.dirname(__file__))
+_ETC_CONFIG = "/etc/sshplus/sshplus.cfg"
+_SYS_CONFIG = "%s/sshplus/sshplus.cfg"%(_BIN_PATH)
+_SETTINGS_FILE = "%s/.sshplus"%(os.getenv("HOME"))
+_SSHMENU_FILE = "%s/.sshmenu"%(os.getenv("HOME"))
_ABOUT_TXT = """A simple application starter as appindicator.
To add items to the menu, edit the file .sshplus in your home directory. Each entry must be on a new line in this format:
-NAME|COMMAND|ARGS
+NAME|(ICON|)COMMAND|ARGS
If the item is clicked in the menu, COMMAND with arguments ARGS will be executed. ARGS can be empty. To insert a separator, add a line which only contains "sep". Lines starting with "#" will be ignored. You can set an unclickable label with the prefix "label:". Items from sshmenu configuration will be automatically added (except nested items). To insert a nested menu, use the prefix "folder:menu name". Subsequent items will be inserted in this menu, until a line containing an empty folder name is found: "folder:". After that, subsequent items get inserted in the parent menu. That means that more than one level of nested menus can be created.
@@ -62,34 +73,137 @@
SSH Ex|gnome-terminal|-x ssh user@1.2.3.4
# to mark the end of items inside "Home", specify and empty folder:
folder:
-# this item appears in the main menu
-SSH Ex|gnome-terminal|-x ssh user@1.2.3.4
+# this item appears in the main menu with icon SSH
+SSH Ex|SSH|gnome-terminal|-x ssh user@1.2.3.4
label:RDP connections
RDP Ex|rdesktop|-T "RDP-Server" -r sound:local 1.2.3.4
-Copyright 2011 Anil Gulecha
+Copyright 2011 Anil Gulecha
+Copyright 2016 rnijenhu, http://www.tenijenhuis.net
Incorporating changes from simplestarter, Benjamin Heil, http://www.bheil.net
+
Released under GPL3, http://www.gnu.org/licenses/gpl-3.0.html"""
+
+_ICONS={}
+_RECENT={}
+_RECENT_DATA=[]
+_RECENT_COUNTER=0
+_SEPARATOR={'name':'sep','type':'seperator'}
+_FOLDER_POP={'name':'FOLDER','type':'folder', 'caption':'', 'cmd':''}
+_MENU_TITLE="Launch"
+_MENU_ICON="gnome-netstatus-tx"
+_MENUS=[]
+
+def read_config(configfile):
+ global _ICONS
+ global _RECENT
+ global _RECENT_DATA
+ global _RECENT_COUNTER
+ global _MENU_TITLE
+ global _MENU_ICON
+
+ if not os.path.exists(configfile):
+ print("Config file not found: %s\n"%(configfile))
+ return []
+
+ config = ConfigParser.ConfigParser({
+ 'title' : _MENU_TITLE,
+ 'icon' : _MENU_ICON
+ })
+ config.read(configfile)
+
+ menufiles=[]
+
+ #read the config and update the settings
+ sections=config.sections()
+
+ for section in sections:
+ if section.startswith("menu"):
+ menufiles.append(dict(config.items(section)))
+ elif section.startswith("icons"):
+ #read the icons we can use
+ _ICONS=dict(config.items(section))
+ elif section.startswith("sshplus"):
+ _MENU_TITLE=config.get("sshplus","title")
+ _MENU_ICON=config.get("sshplus","icon").lower()
+ elif section.startswith("recent"):
+ #read the history files
+ _RECENT=dict(config.items(section))
+ else:
+ print ("Unknown section ignored: %s\n"%(section))
+
+ #for ubuntu this will work, others not I guess. maybe we should provide it
+ if not 'broken' in _ICONS:
+ _ICONS['broken']="/usr/share/icons/gnome/16x16/status/dialog-warning.png"
+ if not 'missing' in _ICONS:
+ _ICONS['missing']="/usr/share/icons/gnome/16x16/status/messagebox_critical.png"
+
+ if 'file' in _RECENT:
+ _RECENT['file']=os.path.expandvars(_RECENT['file'])
+
+ if _MENU_ICON in _ICONS:
+ _MENU_ICON=os.path.expandvars(_ICONS[_MENU_ICON])
+
+ _RECENT_DATA=[]
+ _RECENT_COUNTER=0
+
+ return menufiles
+
+def notify(msg1,msg2):
+ pynotify.init("sshplus")
+ pynotify.Notification(msg1,msg2).show()
+
+def refresh():
+ newmenu = build_menu()
+ ind.set_menu(newmenu)
+
def menuitem_response(w, item):
- if item == '_about':
+ global _RECENT_DATA
+ global _RECENT
+ global _RECENT_COUNTER
+
+ if 'cmd' in item and item['cmd']=='_about':
show_help_dlg(_ABOUT_TXT)
- elif item == '_refresh':
- newmenu = build_menu()
- ind.set_menu(newmenu)
- pynotify.init("sshplus")
- pynotify.Notification("SSHplus refreshed", "Menu list was refreshed from %s" % _SETTINGS_FILE).show()
- elif item == '_quit':
+ elif 'cmd' in item and item['cmd']=='_refresh':
+ refresh()
+ notify("SSHplus refreshed", "Menu list was refreshed from %s"%(_SETTINGS_FILE))
+ elif 'cmd' in item and item['cmd']=='_quit':
sys.exit(0)
- elif item == 'folder':
+ elif 'type' in item and item['type']=='folder':
pass
else:
- print item
- os.spawnvp(os.P_NOWAIT, item['cmd'], [item['cmd']] + item['args'])
+ myargs=[item['cmd']] + item['args']
+ for i,s in enumerate(myargs):
+ myargs[i]=os.path.expandvars(s)
+
+ p = os.spawnvp(os.P_NOWAIT, os.path.expandvars(item['cmd']), myargs)
os.wait3(os.WNOHANG)
+ if not isinstance( p, int ):
+ notify(' '.join(myargs),p)
+
+ #build the recent file
+ if 'file' in _RECENT:
+ #clean up the (same) previous entry, we will add a new one at the top
+ for i,d in enumerate(_RECENT_DATA):
+ if 'name' in d and d['name']==item['name']:
+ del _RECENT_DATA[i]
+
+ #append at the beginning and write the recent file
+ d=[item]+_RECENT_DATA
+ with open(_RECENT['file'], 'w') as outfile:
+ json.dump(d, outfile)
+ _RECENT_DATA=d
+ _RECENT_COUNTER=_RECENT_COUNTER+1
+
+ #after x updates do a menu refresh
+ if 'refresh' in _RECENT:
+ if int(_RECENT['refresh'])>0 and _RECENT_COUNTER >= int(_RECENT['refresh']):
+ _RECENT_COUNTER=0
+ refresh()
def show_help_dlg(msg, error=False):
if error:
@@ -103,21 +217,100 @@ def show_help_dlg(msg, error=False):
md.run()
finally:
md.destroy()
-
-def add_separator(menu):
- separator = gtk.SeparatorMenuItem()
- separator.show()
- menu.append(separator)
-
-def add_menu_item(menu, caption, item=None):
- menu_item = gtk.MenuItem(caption)
- if item:
- menu_item.connect("activate", menuitem_response, item)
+
+def which(program):
+
+ def is_exe(fpath):
+ return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+ fpath, fname = os.path.split(program)
+ if fpath:
+ if is_exe(program):
+ return program
+ else:
+ for path in os.environ["PATH"].split(os.pathsep):
+ path = path.strip('"')
+ exe_file = os.path.join(path, program)
+ if is_exe(exe_file):
+ return exe_file
+
+ return None
+
+def ping(ip):
+
+ ret = os.system("ping -c2 -W1 %s"%(ip))
+ if ret == 0:
+ return True
+
+ print "IP not found: %s\n"%(ip)
+ return False
+
+def add_menu_item2(menu, app):
+
+ menu_item=None
+ menu_item_sens=True
+
+ #get the type
+ if 'type' not in app:
+ print "Error: app without type element:%s\n"%(app)
+ return gtk.MenuItem("????")
+
+ #get the menu entry caption
+ if 'caption' in app:
+ caption=app['caption']
+ else:
+ caption="???????"
+
+ if app['type']=='seperator':
+ menu_item = gtk.SeparatorMenuItem()
+
+ #create the menu entry with icon
+ elif 'icon' in app:
+ menu_item = gtk.ImageMenuItem(caption)
+ img = gtk.Image()
+ iconame=app['icon'].lower()
+
+ #execute items: check if the cmd exists, if not display the missing icon
+ if app['type']=="execute" and not which(app['cmd']):
+ print("Command not found in path: %s\n"%(app['cmd']))
+ img.set_from_file(os.path.expandvars(_ICONS['missing']))
+
+ #folder items: check if the ip exists, if not disable the the folder
+ elif app['type']=="folder" and 'ip' in app and not ping(app['ip']):
+ print("IP not found: %s\n"%(app['ip']))
+ menu_item_sens=False
+ img.set_from_file(os.path.expandvars(_ICONS[iconame]))
+
+ #add the icon, when the icon exists
+ elif iconame in _ICONS and os.path.exists(os.path.expandvars(_ICONS[iconame])):
+ img.set_from_file(os.path.expandvars(_ICONS[iconame]))
+
+ #if nothing is found but a icons was requested: use the broken icon
+ else:
+ img.set_from_file(os.path.expandvars(_ICONS['broken']))
+
+ img.show()
+ menu_item.set_image(img)
+
+ #create the menu entry without icon
else:
- menu_item.set_sensitive(False)
+ menu_item = gtk.MenuItem(caption)
+
+ #add the menu action
+ if app['type'] == "label":
+ menu_item_sens=False
+ elif app['type'] != "seperator":
+ menu_item.connect("activate", menuitem_response, app)
+
+ #set the sensitivity
+ menu_item.set_sensitive(menu_item_sens)
+
+ #enable the menu in the main menu
menu_item.show()
menu.append(menu_item)
- return menu_item
+
+ return menu_item
+
def get_sshmenuconfig():
if not os.path.exists(_SSHMENU_FILE):
@@ -148,8 +341,10 @@ def get_sshmenuconfig():
elif re.search("items:",line):
app_list.append({
'name': 'FOLDER',
+ 'type': 'folder',
+ 'caption': 'SSHmenu',
'cmd': "SSHmenu",
- 'args':"",
+ 'args':[],
})
stackMenuIndex.append(len(app_list) - 1)
@@ -158,8 +353,10 @@ def get_sshmenuconfig():
app_list[stackMenuIndex.pop()]["cmd"] = smtitle
app_list.append({
'name': 'FOLDER',
+ 'type': 'folder',
+ 'caption': '',
'cmd': "",
- 'args':"",
+ 'args':[],
})
smflag = 0
@@ -171,6 +368,8 @@ def get_sshmenuconfig():
app_list.append({
'name': smtitle,
'cmd': 'gnome-terminal',
+ 'caption': smtitle,
+ 'type': 'execute',
'args': arglist,
})
smflag=0
@@ -179,39 +378,133 @@ def get_sshmenuconfig():
print "error in line:" + line
return []
-def get_sshplusconfig():
- if not os.path.exists(_SETTINGS_FILE):
- return []
+def get_sshplusconfig(settings_file):
+ #init values
+ _settings_file=os.path.expandvars(settings_file)
app_list = []
- f = open(_SETTINGS_FILE, "r")
+
+ #does the file exists, if not return nothing
+ if not os.path.exists(_settings_file):
+ return []
+ else:
+ print("Load menu: %s\n"%(_settings_file))
+
+
+ #load the menus from a json file
+ _settings_file_name, _settings_file_ext = os.path.splitext(_settings_file)
+ if _settings_file_ext == ".json":
+ with open(_settings_file) as json_data:
+ try:
+ d=json.load(json_data)
+
+ except ValueError, e :
+ print("Failed to load, syntax error \n")
+ return []
+
+ if not isinstance(d,list):
+ d=[]
+
+ return d
+
+ #load the user settings
+ elif _settings_file_ext =="" and os.path.basename(_settings_file) == ".sshplus":
+ print "User setting file: %s\n"%(_settings_file)
+
+ #quit if the extension isn't known (eg conf for now)
+ elif _settings_file_ext != ".conf":
+ print "Unknown file format: %s\n"%(_settings_file_ext)
+ sys.exit(0)
+
+ #read the settings from the orginal macro format
+ f = open(_settings_file, "r")
try:
for line in f.readlines():
line = line.rstrip()
if not line or line.startswith('#'):
continue
+
+ #process SEPERATORS
elif line == "sep":
- app_list.append('sep')
+ app_list.append(_SEPARATOR)
+
+ #process LABEL items
elif line.startswith('label:'):
- app_list.append({
- 'name': 'LABEL',
- 'cmd': line[6:],
- 'args': ''
- })
+ if line.count(':')==1:
+ app_list.append({
+ 'name': 'LABEL',
+ 'cmd': line[6:],
+ 'caption': line[6:],
+ 'args': [],
+ 'type':'label'
+ })
+
+ elif line.count(':')==2:
+ name, icon, cmd = line.split(':', 2)
+ app_list.append({
+ 'name': 'LABEL',
+ 'icon': icon.lower(),
+ 'cmd': cmd,
+ 'caption': cmd,
+ 'args': [],
+ 'type':'label'
+ })
+
+ #process FOLDER items
elif line.startswith('folder:'):
- app_list.append({
- 'name': 'FOLDER',
- 'cmd': line[7:],
- 'args': ''
- })
+ if line.count(':')==1:
+ app_list.append({
+ 'name': 'FOLDER',
+ 'caption': line[7:],
+ 'cmd': line[7:],
+ 'args': [],
+ 'type':'folder'
+ })
+ elif line.count(':')==2:
+ name, icon, cmd = line.split(':', 2)
+ app_list.append({
+ 'name': 'FOLDER',
+ 'icon': icon.lower(),
+ 'caption': cmd,
+ 'cmd': cmd,
+ 'args': [],
+ 'type':'folder'
+ })
+ elif line.count(':')==3:
+ name, icon, ip, cmd = line.split(':', 3)
+ app_list.append({
+ 'name': 'FOLDER',
+ 'icon': icon.lower(),
+ 'ip': ip,
+ 'caption': cmd,
+ 'cmd': cmd,
+ 'args': [],
+ 'type':'folder'
+ })
+
+ #process EXECUTE items
else:
try:
- name, cmd, args = line.split('|', 2)
- app_list.append({
- 'name': name,
- 'cmd': cmd,
- 'args': [n.replace("\n", "") for n in shlex.split(args)],
- })
+ if line.count('|') == 2 :
+ name, cmd, args = line.split('|', 2)
+ app_list.append({
+ 'name': name,
+ 'caption': name,
+ 'cmd': cmd,
+ 'type':'execute',
+ 'args': [n.replace("\n", "") for n in shlex.split(args)],
+ })
+ elif line.count('|') == 3 :
+ name, icon, cmd, args = line.split('|', 3)
+ app_list.append({
+ 'icon': icon.lower(),
+ 'name': name,
+ 'caption': name,
+ 'cmd': cmd,
+ 'type':'execute',
+ 'args': [n.replace("\n", "") for n in shlex.split(args)],
+ })
+ else: print "The following line has an invalid amount of separators and will be ignored:\n%s" % line
except ValueError:
print "The following line has errors and will be ignored:\n%s" % line
finally:
@@ -219,49 +512,171 @@ def get_sshplusconfig():
return app_list
def build_menu():
- if not os.path.exists(_SETTINGS_FILE) and not os.path.exists(_SSHMENU_FILE) :
- show_help_dlg("ERROR: No .sshmenu or .sshplus file found in home directory\n\n%s" % \
- _ABOUT_TXT, error=True)
- sys.exit(1)
+ global _RECENT_DATA
+
+ app_list=[]
+
+ #read the recent items
+ if 'file' in _RECENT:
+ if 'count' in _RECENT and int(_RECENT['count'])>0:
+ _RECENT_DATA=get_sshplusconfig(_RECENT['file'])
+
+ #strip down to the needed amount
+ if 'name' in _RECENT:
+ name=_RECENT['name']
+ else:
+ name="Recently Used:"
+ label={
+ 'cmd' : name,
+ 'name' : 'LABEL',
+ 'type' : 'label',
+ 'caption' : name,
+ 'args': []}
+
+ if 'count' in _RECENT and len(_RECENT_DATA)>0 and len(_RECENT_DATA)<=_RECENT['count']:
+ app_list=[ _SEPARATOR, label, _SEPARATOR]+ _RECENT_DATA[0:int(_RECENT['count'])]+ [_FOLDER_POP,_SEPARATOR]
+ else:
+ app_list=[ _SEPARATOR, label, _SEPARATOR]+ _RECENT_DATA + [_FOLDER_POP,_SEPARATOR]
+
+ #read the user menu
+ u_list=get_sshplusconfig(_SETTINGS_FILE)
+ if u_list != []:
+ app_list = app_list + ulist;
+
+
+ #load the global menus
+ for menu in _MENUS :
+
+ if 'file' in menu:
+ m_list=get_sshplusconfig(menu['file'])
+ else:
+ print "Menu item found without file key: %s\n"%(menu)
+ continue
+
+ if m_list !=[]:
+
+ #check if the menu is allowed for this user
+ if 'users' in menu:
+ users=menu['users'].split(',')
+
+ if not _USER in users:
+ continue
+
+ if 'name' in menu:
+ name=menu['name']
+ else:
+ name=menu['file']
+
+ folder={
+ 'cmd' : menu['name'],
+ 'name' : 'FOLDER',
+ 'type' : 'folder',
+ 'caption' : menu['name'],
+ 'args': []}
+
+ if 'icon' in menu:
+ folder['icon']=menu['icon']
+
+ if 'ip' in menu:
+ folder['ip']=menu['ip']
+
+ if 'decoration' in menu and menu['decoration'].lower()=="false" :
+ app_list=app_list + m_list
+ else:
+ app_list=app_list + [ _SEPARATOR, _FOLDER_POP, folder, _SEPARATOR] + m_list
+ else:
+ print "File not found or empty: %s\n"%(menu['file'])
- app_list = get_sshplusconfig()
#Add sshmenu config items if any
app_list2 = get_sshmenuconfig()
if app_list2 != []:
- app_list = app_list + ["sep",{'name': 'LABEL','cmd': "SSHmenu",'args': ''}] + app_list2
+ app_list = app_list + [_SEPARATOR,{'name': 'LABEL','cmd': "SSHmenu",'args': ''}] + app_list2
menu = gtk.Menu()
menus = [menu]
+
+ if len(app_list) <=0:
+ show_help_dlg("ERROR: No menus found in $HOME/.sshmenu $HOME/.sshplus or /etc/sshplus/sshplus.cfg \n\n%s" % \
+ _ABOUT_TXT, error=True)
+ sys.exit(1)
+
+
+
+ #to make this work: add FOLDER|LABEL|COMMAND to the dict and let add_menu_item sort out what to do in each case
+ # or juist provide the sec arg with 'name'
+
for app in app_list:
- if app == "sep":
- add_separator(menus[-1])
- elif app['name'] == "FOLDER" and not app['cmd']:
+ if app['name'] == "FOLDER" and not app['cmd']:
if len(menus) > 1:
menus.pop()
- elif app['name'] == "FOLDER":
- menu_item = add_menu_item(menus[-1], app['cmd'], 'folder')
- menus.append(gtk.Menu())
- menu_item.set_submenu(menus[-1])
- elif app['name'] == "LABEL":
- add_menu_item(menus[-1], app['cmd'], None)
- else:
- add_menu_item(menus[-1], app['name'], app)
-
- add_separator(menu)
- add_menu_item(menu, 'Refresh', '_refresh')
- add_menu_item(menu, 'About', '_about')
- add_separator(menu)
- add_menu_item(menu, 'Quit', '_quit')
+ else:
+ menu_item = add_menu_item2(menus[-1], app)
+ if app['name'] == "FOLDER":
+ menus.append(gtk.Menu())
+ menu_item.set_submenu(menus[-1])
+
+
+ add_menu_item2(menus[-1], _SEPARATOR)
+ add_menu_item2(menu, {'name': '_refresh','caption': 'Refresh','cmd': '_refresh','type':'execute','args': []})
+ add_menu_item2(menu, {'name': '_about','caption': 'About','cmd': '_about','type':'execute','args': []})
+ add_menu_item2(menus[-1], _SEPARATOR)
+ add_menu_item2(menu, {'name': '_quit','caption': 'Quit','cmd': '_quit','type':'execute','args': []})
return menu
+def write_desktopfile(desktopfile):
+ file = open(desktopfile, "w")
+ file.write("[Desktop Entry]\n")
+ file.write("Type=Application\n")
+ file.write("Name=sshplus\n")
+ file.write("Exec=%s\n"%(os.path.realpath(__file__)))
+ #file.write("Icon=\n")
+ file.write("Comment=SSHplus Menu indicator\n")
+ file.write("Hidden=false\n")
+ file.write("NoDisplay=false\n")
+ file.write("X-GNOME-Autostart-enabled=true\n")
+ file.close()
+
+
if __name__ == "__main__":
- ind = appindicator.Indicator("sshplu", "gnome-netstatus-tx",
+
+
+ #display help
+ if "--help" in sys.argv :
+ print( "\n\n"+
+ " .sshplus.py without arguments just creates the menu\n"+
+ " .sshplus.py --start-on-login creates the autostart for the user (only)\n"+
+ " .sshplus.py --start-on-login-sys creates the autostart for the system, root privileges required\n"+
+ "\n"+
+ " FILES: /etc/sshplus/sshplus.cfg /$HOME/.sshplus\n")
+ sys.exit()
+
+ #create startup files
+ if "--start-on-login" in sys.argv :
+ write_desktopfile(os.path.expandvars("$HOME/.config/autostart/sshplus.py.desktop"))
+ sys.exit()
+ if "--start-on-login-sys" in sys.argv :
+ write_desktopfile("/etc/xdg/autostart/sshplus.py.desktop")
+ sys.exit()
+
+ #read the config file, it will set the SETTING_FILE to list
+ _MENUS=read_config(_ETC_CONFIG)
+ if not _MENUS:
+ _MENUS=read_config(_SYS_CONFIG)
+
+ #enable the icons in menus: http://www.pygtk.org/pygtk2reference/class-gtksettings.html
+ window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ window.get_settings().set_long_property('gtk-menu-images', True, '')
+
+ #create and display the indicator
+ ind = appindicator.Indicator("sshplu", _MENU_ICON,
appindicator.CATEGORY_APPLICATION_STATUS)
- ind.set_label("Launch")
+
+ ind.set_label(_MENU_TITLE)
ind.set_status(appindicator.STATUS_ACTIVE)
appmenu = build_menu()
ind.set_menu(appmenu)
gtk.main()
+
diff --git a/sshplus/icons/avatar2.png b/sshplus/icons/avatar2.png
new file mode 100755
index 0000000..c3b3e97
Binary files /dev/null and b/sshplus/icons/avatar2.png differ
diff --git a/sshplus/icons/icon-connected-eye.png b/sshplus/icons/icon-connected-eye.png
new file mode 100644
index 0000000..59b754e
Binary files /dev/null and b/sshplus/icons/icon-connected-eye.png differ
diff --git a/sshplus/menus.conf b/sshplus/menus.conf
new file mode 100644
index 0000000..8c9b057
--- /dev/null
+++ b/sshplus/menus.conf
@@ -0,0 +1,27 @@
+# Application launchers in a folder
+term|gnome-terminal|
+sep
+folder:FOLDER:Applications
+Show top|gnome-terminal|-x top
+Show htop|gnome-terminal|-x htop
+Kill VLC|pkill| -9 vlc
+sep
+folder:
+
+sep
+
+# label: adds labels to the menu
+label:SSH:SSH connections
+admin@machine|SSH|gnome-terminal|-x ssh -X admin@machine
+admin@machine|SSH|gnome-terminal|-x ssh -X admin@machine
+admin@machine|gnome-terminal|-x ssh -X admin@machine
+admin@machine|gnome-terminal|-x ssh -X admin@machine
+
+folder:
+folder: RDP
+notebook1 HP|RDP|rdesktop|-z -r clipboard:PRIMARYCLIPBOARD -u user -k en-us -r sound:local -g 1320x680 -T “machine1″ machine1
+notebook2 HP|RDP|rdesktop|-z -r clipboard:PRIMARYCLIPBOARD -u user -k en-us -r sound:local -g 1320x680 -T “machine2″ machine2
+notebook3 HP|rdesktop|-z -r clipboard:PRIMARYCLIPBOARD -u user -k en-us -r sound:local -g 1320x680 -T “machine3″ machine3
+notebook4 HP (opt)|/opt/rdesktop/bin/rdesktop|-z -r clipboard:PRIMARYCLIPBOARD -u user -k en-us -r sound:local -g 1320x680 -T “machine4″ machine4
+folder:
+
diff --git a/sshplus/sshplus.cfg b/sshplus/sshplus.cfg
new file mode 100644
index 0000000..e8417c9
--- /dev/null
+++ b/sshplus/sshplus.cfg
@@ -0,0 +1,67 @@
+#copy this file in /etc/sshplus/ (or create a link to this file on the network)
+
+#################################################################################
+#declare the icons which can be used in the .conf and the sshplus files.
+#be sure these are available for all machines, otherwise put them in the sshplus/icon folder
+#################################################################################
+[icons]
+#checkout file:///usr/share/icons/gnome/ for more icons
+BROKEN=/usr/share/icons/gnome/16x16/status/messagebox_warning.png
+MISSING=/usr/share/icons/gnome/16x16/status/dialog-error.png
+IP_DISABLED=/usr/share/icons/gnome/16x16/status/gnome-netstatus-error.png
+FOLDER=/usr/share/icons/gnome/16x16/places/stock_folder.png
+FOLDER_REMOTE=/usr/share/icons/gnome/16x16/places/folder-remote.png
+FILEMANAGER=/usr/share/icons/gnome/16x16/apps/system-file-manager.png
+SSH=/usr/share/icons/gnome/16x16/apps/gnome-terminal.png
+RDP=/usr/share/icons/gnome/16x16/apps/gnome-remote-desktop.png
+
+#################################################################################
+#the main config item, mainly to configure the indicator
+#################################################################################
+[sshplus]
+#the indicator title, leaf it empty when none is required
+title=sshplus menu
+#use a gnome default icon name, the icon full path or a value from [icons]
+icon=gnome-netstatus-tx
+
+#################################################################################
+#recently launched commands
+#################################################################################
+[recent]
+#Menu label, required
+name=Recently Used
+#The amount of recently used tasks to display [0-10], required
+count=5
+#After these amount of task executions a refresh is performed [1-99], optional
+refresh=5
+#location of the recently used history file
+file=/tmp/sshplus.$USER.json
+
+
+#################################################################################
+#a list of menu's, in general these are references to multiple .sshplus files,
+#################################################################################
+
+#create a folder item with icon FOLDER, enable it when ip is available. Only user vpnadmin can use it
+[menu1]
+name=Remote menu
+user=vpnadmin
+icon=FOLDER
+ip=192.168.1.1
+file=/etc/sshplus/remote.conf
+
+#add tasks to the main menu without a parent folder
+[menu2]
+name=Generic menu
+decoration=false
+file=/etc/sshplus/generic.conf
+
+[menu3]
+name=Radio streams
+file=/etc/sshplus/radio.conf
+
+[menu4]
+name=Network devices
+user=admin
+file=/etc/sshplus/devices.conf
+