Topic: My little adeskmenu project.
I found an old version of adeskmenu on launchpad and figured it would be nice to take advantage of tint2's new launcher feature. So with a help from MoD from silver.irc we re-worked the script to just open the menu when clicked. Here is a screenshot.
All you need is the single page menu script and a .desktop pointing to it.
Here is the code for the menu script.
#!/usr/bin/python
# -*- coding: utf-8 -*-
##
# ADesk Menu
# - v0.2 : add signal , get cursor position and show menu whith it
# - v0.1 : check if 'Terminal=true' in .desktop file, always above other windows
#
# by ADcomp <david.madbox@gmail.com>
# http://www.ad-comp.be/
#
# This program is distributed under the terms of the GNU General Public License
# For more info see http://www.gnu.org/licenses/gpl.txt
#####################################################################################
#
# 7/25/11
#
# This script was edited from the original by sliphot and MoD from silver irc.
# I thhought it might come in handy with tint2s's new launcher feature.
# We re-worked it so it will open the menu in a designated area of the screen when the script is executed.
# by default it opens in the top left corner of the screen. To adjust go to line 369 and adjust x= and y=
# Make this script executable and move it to /usr/bin after that make a .desktop file pointing to it.
# Point tint2 to the .desktop file and presto! tint2 desktop menu. It can be a little slow to load.
# It helps to run it once in a terminal with the -s option. it saves a config file of all your .desktop files.
# If you install a new app after you have ran the -s option you will have to run it again to update the config.
#
#####################################################################################
MENU_WIDTH = 380
import os
import sys
import gtk
import signal
cat_name = {'accessories':('Utility','Accessories'),
'development':('Development'),
'engineering':(),
'games':('Game'),
'graphics':('Graphics'),
'internet':('Internet', 'Network'),
'multimedia':('AudioVideo', 'Audio', 'Video', 'Multimedia'),
'office':('Office'),
'other':('Other'),
'science':(),
'system':('System', 'Administration'),
'settings':('Settings', 'X-XFCE'),
'utilities':()
}
def exec_cmd(cmd=None):
if cmd and not cmd=='':
os.system('%s &' % cmd)
# Find the Name / Command / Icon from .desktop file
def info_desktop(file):
cmd, icon, name, category = None, None, None, None
terminal = False
try:
cfile = open(file,"r")
for line in cfile:
if 'NoDisplay=true' in line or 'OnlyShowIn=KDE' in line:
icon, category = None, None
break
elif 'Terminal=true' in line:
terminal = True
elif '=' in line:
words = line.split('=')
if words[0] == 'Name':
name = words[1].replace('\n','')
# print 'Name =', name
elif words[0] == 'Icon':
icon = words[1].replace('\n','')
# print 'Icon =', icon
elif words[0] == 'Exec':
cmd = words[1].replace('\n','')
cmd = cmd.split('%')[0]
# print 'Exec :', cmd
elif words[0] == 'Categories':
tab = words[1].replace('\n','')
tab = tab.split(';')
#~ category = '***************************'
#~ if not 'X-XFCE' in tab:
for cat in tab:
found = False
for c_name in cat_name:
if cat in cat_name[c_name]:
category = c_name
found = True
break
if found:
break
# if command is 'console only', launch it with terminal ..
if terminal:
cmd = "x-terminal-emulator -e %s" % cmd
cfile.close()
except:
#~ print("# Error : parsing %s" % file)
pass
return (cmd, icon, name, category)
# Find the best icon with highest resolution for the launcher
def find_icon(icon):
foundiconfile=None
if icon == '':
return foundiconfile
if icon[0] == '/':
return icon
iconbase=('','Madbox','gnome','hicolor','locolor')
iconpath='/usr/share/icons'
sizelist =('', 'scalable', '256x256', '128x128', '64x64', '48x48', '32x32', '24x24')
categorylist=('actions', 'apps',"devices", 'categories','filesystems', 'places', 'status', '')
extensionlist = ('png', 'svg', 'xpm')
iconimagelist=[]
for extension in extensionlist:
if (icon.find('.'+extension) != -1):
icon = icon.replace('.'+extension,'')
for size in sizelist:
for extension in extensionlist:
for category in categorylist:
for iconbasecat in iconbase:
iconfile = iconpath+"/"+iconbasecat+'/'+size+'/'+category+'/'+icon+'.'+extension
iconimagelist.append(iconfile)
for extension in extensionlist:
iconfile = '/usr/share/pixmaps/'+icon+'.'+extension
iconimagelist.append(iconfile)
for extension in extensionlist:
iconfile = '/usr/share/app-install/icons/'+icon+'.'+extension
iconimagelist.append(iconfile)
# Seek if the files in pre-generated list exists.. first match is the best
# return it
for iconimage in iconimagelist:
if os.path.exists(iconimage):
return iconimage
return foundiconfile
def parse_desktop_dir(check_config=True):
config = {}
cfg_file = home = os.getenv('HOME') + '/.config/adesk-menu/config'
if os.access(cfg_file, os.F_OK|os.R_OK) and check_config:
print('found user config ..')
f = open(cfg_file,'r')
for line in f:
if line == '\n' or line.startswith('#'):
continue
elif line.startswith('@'):
cat = line[1:]
cat = cat.strip('\n')
cat = cat.strip(' ')
config[cat] = []
cat_index = cat
else:
line = line.strip('\n')
line = line.strip(' ')
config[cat_index].append(line.split('##'))
f.close()
else:
print('scan /usr/share/applications and parse .desktop file ..')
APP_PATH = '/usr/share/applications/'
listdir = os.listdir(APP_PATH)
for i in listdir:
if '.desktop' in i:
#~ print i
(cmd, icon, name, category) = info_desktop(APP_PATH + i)
#~ print cmd, icon, name, category
if category:
if not config.has_key(category):
#~ print "@ add :", category
config[category] = []
if icon:
icon_path = find_icon(icon)
config[category].append((cmd, icon_path, name))
else:
pass
#~ print "File =", i
#~ print '=> NO ICON ...'
else:
pass
#~ print "File =", i
#~ print '=> NO CATEGORY ...'
#~ print config
return config
def write_config():
home = os.environ['HOME']
cfg_file = "%s/.config/adesk-menu/config" % home
if not os.path.exists("%s/.config/adesk-menu" % home):
os.makedirs("%s/.config/adesk-menu" % home)
f = open(cfg_file,'w')
m = parse_desktop_dir(False)
m_tmp = []
for cat in m:
m_tmp.append(cat)
m_tmp.sort()
for cat in m_tmp:
f.write('@%s\n' % cat)
for item in m[cat]:
(cmd, icon, text) = item
f.write('%s##%s##%s\n' % item)
f.close()
print "menu saved to %s" % cfg_file
class Menu():
def __init__(self):
self.hide_me = False
self.focus_check = False
self.mode = None
self.create_window()
# Menu
self.popupMenu = gtk.Menu()
menuPopup = gtk.ImageMenuItem (gtk.STOCK_QUIT)
menuPopup.connect("activate", self.doquit)
self.popupMenu.add(menuPopup)
self.popupMenu.show_all()
def doquit(self, widget=None, data=None):
gtk.main_quit()
def run(self):
gtk.main()
def create_window(self):
self.window = gtk.Window() #gtk.WINDOW_POPUP) ## gtk.WINDOW_TOPLEVEL)
self.window.connect("destroy", self.doquit)
#~ self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
self.window.add_events(gtk.gdk.FOCUS_CHANGE_MASK)
self.window.connect("focus-out-event", self.lost_focus)
self.window.connect("key-press-event", self.onkeypress)
self.window.stick()
self.window.set_decorated(False)
self.window.set_keep_below(False)
self.window.set_keep_above(True)
self.window.set_resizable(True)
self.window.set_border_width(2)
self.window.set_app_paintable(True)
self.frame = gtk.Frame()
self.frame.show()
self.frame.set_size_request(MENU_WIDTH,-1)
self.nbook = gtk.Notebook()
self.nbook.show()
self.nbook.set_tab_pos(gtk.POS_LEFT)
self.nbook.set_border_width(2)
self.frame.add(self.nbook)
self.window.add(self.frame)
m = parse_desktop_dir()
m_tmp = []
for cat in m:
m_tmp.append(cat)
m_tmp.sort()
self.cat_but = []
self.cur_pos = 0
self.cat_visible = True
for cat in m_tmp:
bBox = gtk.VBox()
bBox.show()
bBox.set_spacing(4)
bBox.set_border_width(2)
scrolled = gtk.ScrolledWindow()
scrolled.show()
scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
scrolled.add_with_viewport(bBox)
label = gtk.Label(cat[0].upper() + cat[1:])
label.show()
label.set_alignment(0, 1)
self.nbook.append_page(scrolled, label)
for item in m[cat]:
(cmd, icon, text) = item
widget = gtk.Button() # item.label)
widget.set_relief(gtk.RELIEF_NONE)
widget.set_border_width(0)
widget.set_focus_on_click(False)
#~ widget.set_property('can-focus', False)
image = gtk.Image()
image.show()
image.set_size_request(24,24)
label = gtk.Label(text)
label.show()
label.set_alignment(0.2, 1)
align = gtk.Alignment(1, 0, 0.5, 0.5)
align.show()
align.add(label)
box = gtk.HBox(False, 4)
box.show()
box.pack_start(image, False, False)
box.pack_start(align, False, False)
widget.add(box)
try:
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon, 24, 24)
except:
pixbuf = gtk.gdk.pixbuf_new_from_file_at_size('/usr/share/pixmaps/debian-logo.png', 24, 24)
image.set_from_pixbuf(pixbuf)
image.show()
widget.connect("button-release-event", self.ExecuteAction, cmd)
bBox.pack_start(widget,False,False)
widget.show()
def ExecuteAction(self, widget, event, cmd):
self.focus_check = False
exec_cmd(cmd)
self.doquit()
def toggle_hide(self, widget=None, event=None):
if self.hide_me:
self.hide_me = False
self.show_menu(self.mode)
self.window.show()
self.focus_check = True
else:
self.hide_me = True
self.focus_check = False
self.window.hide()
def show_menu(self, mode=None):
screen_width, screen_height = gtk.gdk.screen_width(), gtk.gdk.screen_height()
rootwin = self.window.get_screen().get_root_window()
w_width , w_height = self.window.get_size()
x, y, mods = rootwin.get_pointer()
if x + MENU_WIDTH > screen_width:
x = screen_width - MENU_WIDTH
if y + w_height > screen_height:
y = screen_height - w_height
#to move the menu adjust the x= and y=
#this should not be commented out
self.window.move(x=10, y=40)
def lost_focus(self, widget, event):
if self.focus_check:
self.doquit()
def onkeypress(self, widget, event):
if event.keyval == gtk.keysyms.Escape:
self.doquit()
def status_icon_popup(self, widget, button, active_time):
self.popupMenu.popup(None, None, None, 1, 0)
def callback_signal(self):
#~ print "callback_signal .."
self.mode = "mouse"
self.toggle_hide()
self.mode = None
##--
global g_menu
def SigUSR1Handler(signum, frame):
""" grab signal to reload config """
# kill -10 <adesk-menu pid>
global g_menu
g_menu.callback_signal()
return
def save_pid():
home = os.environ['HOME']
pid_file = "%s/.config/adesk-menu/pid" % home
pid = os.getpid()
print "PID = %s" % pid
if not os.path.exists("%s/.config/adesk-menu" % home):
os.makedirs("%s/.config/adesk-menu" % home)
f = open(pid_file,'w')
f.write('%s' % pid)
f.close()
print "PID saved to %s" % pid_file
if __name__ == "__main__":
if len(sys.argv) == 2:
if sys.argv[1] == '-s':
write_config()
elif sys.argv[1] == '-t':
cmd = "kill -10 `cat ~/.config/adesk-menu/pid`"
os.system(cmd)
else:
print "Unknow option .."
print "Usage : adesk-menu [-s|-t]"
print " -s : save menu list ( ~/.config/adesk-menu/config )"
print " -t : show menu ( cursor position )"
else:
global g_menu
signal.signal(signal.SIGUSR1, SigUSR1Handler)
save_pid()
## need to change directory
SRC_PATH = os.path.dirname(os.path.realpath( __file__ ))
os.chdir(SRC_PATH)
g_menu = Menu()
# change directory to Home
os.chdir(os.getenv('HOME'))
g_menu.hide_me = False
g_menu.show_menu(g_menu.mode)
g_menu.window.show()
g_menu.focus_check = True
g_menu.run()and here is the .desktop file.
[Desktop Entry]
Type=Application
Name=menu
Exec=/usr/bin/adesk-menu2
Icon=/usr/share/pixmaps/debian-logo.pngMake the menu script executable and move it to /usr/bin "sudo chmod +x adesk-menu2 && sudo mv adesk-menu2 /usr/bin/"
After that put the .desktop file somewhere like ~/.scripts and point tint2 to it. After that you should be good to go. By default the menu opens in the top left corner of the screen. If you need to adjust where
the menu opens edit line 369 in the script.

