Package screenlets :: Module XmlMenu
[hide private]
[frames] | no frames]

Source Code for Module screenlets.XmlMenu

  1  # This application is released under the GNU General Public License  
  2  # v3 (or, at your option, any later version). You can find the full  
  3  # text of the license under http://www.gnu.org/licenses/gpl.txt.  
  4  # By using, editing and/or distributing this software you agree to  
  5  # the terms and conditions of this license.  
  6  # Thank you for using free software! 
  7   
  8  # a very hackish, XML-based menu-system (c) RYX (Rico Pfaus) 2007 
  9  # 
 10  # NOTE: This thing is to be considered a quick hack and it lacks on all ends. 
 11  #       It should be either improved (and become a OOP-system ) or removed 
 12  #       once there is a suitable alternative ... 
 13  # 
 14   
 15  import glob, gtk 
 16  import xml.dom.minidom 
 17  from xml.dom.minidom import Node 
 18  import os 
 19  import gettext 
 20   
 21  gettext.textdomain('screenlets') 
 22  gettext.bindtextdomain('screenlets', '/usr/share/locale') 
 23   
24 -def _(s):
25 return gettext.gettext(s)
26 27 # creates a nw gtk.ImageMenuItem from a given icon-/filename. 28 # If no absolute path is given, the function checks for the name 29 # of the icon within the current gtk-theme.
30 -def imageitem_from_name (filename, label, icon_size=32):
31 """Creates a nw gtk.ImageMenuItem from a given icon-/filename.""" 32 item = gtk.ImageMenuItem(label) 33 image = gtk.Image() 34 if filename and filename[0]=='/': 35 # load from file 36 try: 37 image.set_from_file(filename) 38 pb = image.get_pixbuf() 39 # rescale, if too big 40 if pb.get_width() > icon_size : 41 pb2 = pb.scale_simple( 42 icon_size, icon_size, 43 gtk.gdk.INTERP_HYPER) 44 image.set_from_pixbuf(pb2) 45 else: 46 image.set_from_pixbuf(pb) 47 except: 48 print _("Error while creating image from file: %s") % filename 49 return None 50 else: 51 image.set_from_icon_name(filename, 3) # TODO: use better size 52 if image: 53 item.set_image(image) 54 return item
55
56 -def read_desktop_file (filename):
57 """Read ".desktop"-file into a dict 58 NOTE: Should use utils.IniReader ...""" 59 list = {} 60 f=None 61 try: 62 f = open (filename, "r") 63 except: 64 print _("Error: file %s not found.") % filename 65 if f: 66 lines = f.readlines() 67 for line in lines: 68 if line[0] != "#" and line !="\n" and line[0] != "[": 69 ll = line.split('=', 1) 70 if len(ll) > 1: 71 list[ll[0]] = ll[1].replace("\n", "") 72 return list
73
74 -def fill_menu_from_directory (dirname, menu, callback, filter='*', 75 id_prefix='', id_suffix='', search=[], replace=[], skip=[]):
76 """Create MenuItems from a directory. 77 TODO: use regular expressions""" 78 # create theme-list from theme-directory 79 lst = glob.glob(dirname + "/" + filter) 80 #print "Scanning: "+dirname + "/" + filter 81 lst.sort() 82 dlen = len(dirname) + 1 83 # check each entry in dir 84 for filename in lst: 85 #print "FILE: " + filename 86 fname = filename[dlen:] 87 # file allowed? 88 if skip.count(fname)<1: 89 #print "OK" 90 # create label (replace unwanted strings) 91 l = len(search) 92 if l>0 and l == len(replace): 93 for i in xrange(l): 94 fname = fname.replace(search[i], replace[i]) 95 # create label (add prefix/suffix/replace) 96 id = id_prefix + fname + id_suffix 97 #print "NAME: "+fname 98 # create menuitem 99 item = gtk.MenuItem(fname) 100 item.connect("activate", callback, id) 101 item.show() 102 menu.append(item)
103
104 -def create_menu_from_xml (node, callback, icon_size=22):
105 """Create a gtk.Menu by an XML-Node""" 106 menu = gtk.Menu() 107 for node in node.childNodes: 108 #print node 109 type = node.nodeType 110 if type == Node.ELEMENT_NODE: 111 label = node.getAttribute("label") 112 id = node.getAttribute("id") 113 item = None 114 is_check = False 115 # <item> gtk.MenuItem 116 if node.nodeName == "item": 117 item = gtk.MenuItem(label) 118 # <checkitem> gtk.CheckMenuItem 119 elif node.nodeName == "checkitem": 120 item = gtk.CheckMenuItem(label) 121 is_check = True 122 if node.hasAttribute("checked"): 123 item.set_active(True) 124 # <imageitem> gtk.ImageMenuItem 125 elif node.nodeName == "imageitem": 126 icon = node.getAttribute("icon") 127 item = imageitem_from_name(icon, label, icon_size) 128 # <separator> gtk.SeparatorMenuItem 129 elif node.nodeName == "separator": 130 item = gtk.SeparatorMenuItem() 131 # <appdir> 132 elif node.nodeName == "appdir": 133 # create menu from dir with desktop-files 134 path = node.getAttribute("path") 135 appmenu = ApplicationMenu(path) 136 cats = node.getAttribute("cats").split(",") 137 for cat in cats: 138 item = gtk.MenuItem(cat) 139 #item = imageitem_from_name('games', cat) 140 submenu = appmenu.get_menu_for_category(cat, callback) 141 item.set_submenu(submenu) 142 item.show() 143 menu.append(item) 144 item = None # to overjump further append-item calls 145 # <scandir> create directory list 146 elif node.nodeName == "scandir": 147 # get dirname, prefix, suffix, replace-list, skip-list 148 dir = node.getAttribute("directory") 149 # replace $HOME with environment var 150 dir = dir.replace('$HOME', os.environ['HOME']) 151 #expr = node.getAttribute("expr") 152 idprfx = node.getAttribute("id_prefix") 153 idsufx = node.getAttribute("id_suffix") 154 srch = node.getAttribute("search").split(',') 155 repl = node.getAttribute("replace").split(',') 156 skp = node.getAttribute("skip").split(',') 157 # get filter attribute 158 flt = node.getAttribute("filter") 159 if flt=='': 160 flt='*' 161 # scan directory and append items to current menu 162 #fill_menu_from_directory(dir, menu, callback, regexp=expr, filter=flt) 163 fill_menu_from_directory(dir, menu, callback, filter=flt, 164 id_prefix=idprfx, id_suffix=idsufx, search=srch, 165 replace=repl, skip=skp) 166 # item created? 167 if item: 168 if node.hasChildNodes(): 169 # ... call function recursive and set returned menu as submenu 170 submenu = create_menu_from_xml(node, 171 callback, icon_size) 172 item.set_submenu(submenu) 173 item.show() 174 if id: 175 item.connect("activate", callback, id) 176 menu.append(item) 177 return menu
178
179 -def create_menu_from_file (filename, callback):
180 """Creates a menu from an XML-file and returns None if something went wrong""" 181 doc = None 182 try: 183 doc = xml.dom.minidom.parse(filename) 184 except Exception, e: 185 print _("XML-Error: %s") % str(e) 186 return None 187 return create_menu_from_xml(doc.firstChild, callback)
188 189 190
191 -class ApplicationMenu:
192 """A utility-class to simplify the creation of gtk.Menus from directories with 193 desktop-files. Reads all files in one or multiple directories into its internal list 194 and offers an easy way to create entire categories as complete gtk.Menu 195 with gtk.ImageMenuItems. """ 196 197 # the path to read files from 198 __path = "" 199 # list with apps (could be called "cache") 200 __applications = [] 201 202 # constructor
203 - def __init__ (self, path):
204 self.__path = path 205 self.__categories = {} 206 self.read_directory(path)
207 208 # read all desktop-files in a directory into the internal list 209 # and sort them into the available categories
210 - def read_directory (self, path):
211 dirlst = glob.glob(path + '/*') 212 #print "Path: "+path 213 namelen = len(path) 214 for file in dirlst: 215 if file[-8:]=='.desktop': 216 fname = file[namelen:] 217 #print "file: "+fname 218 df = read_desktop_file(file) 219 name = "" 220 icon = "" 221 cmd = "" 222 try: 223 name = df['Name'] 224 icon = df['Icon'] 225 cmd = df['Exec'] 226 cats = df['Categories'].split(';') 227 #typ = df['Type'] 228 #if typ == "Application": 229 self.__applications.append(df) 230 except Exception, ex: 231 print _("Exception: %s") % str(ex) 232 print _("An error occured with desktop-file: %s") % file
233 234 # return a gtk.Menu with all app in the given category
235 - def get_menu_for_category (self, cat_name, callback):
236 # get apps in the given category 237 applist = [] 238 for app in self.__applications: 239 try: 240 if (';'+app['Categories']).count(';'+cat_name+';') > 0: 241 applist.append(app) 242 except: 243 pass 244 245 # remove duplicates 246 for app in applist: 247 if applist.count(app) > 1: 248 applist.remove(app) 249 # sort list 250 applist.sort() 251 # create menu from list 252 menu = gtk.Menu() 253 for app in applist: 254 item = imageitem_from_name(app['Icon'], app['Name'], 24) 255 if item: 256 item.connect("activate", callback, "exec:" + app['Exec']) 257 item.show() 258 menu.append(item) 259 # return menu 260 return menu
261 262 263 """ 264 # TEST: 265 266 # menu-callback 267 def menu_handler(item, id): 268 # now check id 269 if id[:5]=="exec:": 270 print "EXECUTE: " + id[5:] 271 272 def button_press(widget, event): 273 widget.menu.popup(None, None, None, event.button, 274 event.time) 275 return False 276 277 def destroy(widget, event): 278 gtk.main_quit() 279 280 281 282 # ApplicationMenu test 283 284 appmenu = ApplicationMenu('/usr/share/applications') 285 286 win = gtk.Window() 287 win.resize(200, 200) 288 win.connect("delete_event", destroy) 289 but = gtk.Button("Press!") 290 but.menu = gtk.Menu() 291 lst = ["Development", "Office", "Game", "Utility"] 292 for i in xrange(len(lst)): 293 item = gtk.MenuItem(lst[i]) 294 submenu = appmenu.get_menu_for_category(lst[i], menu_handler) 295 if submenu: 296 item.set_submenu(submenu) 297 item.show() 298 but.menu.append(item) 299 but.menu.show() 300 but.connect("button_press_event", button_press) 301 win.add(but) 302 but.show() 303 win.show() 304 gtk.main() 305 """ 306 307 # XML/Appmenu TEST 308 if __name__ == "__main__": 309 310 import screenlets.utils 311 312 # menu callback
313 - def xml_menu_handler (item, id):
314 print "ID: "+str(id) 315 # now check id 316 if id[:5]=="exec:": 317 print "EXECUTE: " + id[5:]
318
319 - def button_press (widget, event):
320 widget.menu.popup(None, None, None, event.button, 321 event.time) 322 return False
323
324 - def destroy (widget, event):
325 gtk.main_quit()
326 327 # create menu from XML-file 328 p = screenlets.utils.find_first_screenlet_path('Control') 329 print p 330 menu = create_menu_from_file(p + "/menu.xml", xml_menu_handler) 331 if menu: 332 win = gtk.Window() 333 win.resize(200, 200) 334 win.connect("delete_event", destroy) 335 but = gtk.Button("Press!") 336 but.menu = menu 337 but.connect("button_press_event", button_press) 338 win.add(but) 339 but.show() 340 win.show() 341 gtk.main() 342 else: 343 print _("Error while creating menu.") 344