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

Source Code for Module screenlets.menu

  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 -def add_menuitem (menu, label, callback=None, cb_data=None):
28 """Convenience function to create a menuitem, connect 29 a callback, and add the menuitem to menu.""" 30 if label == "-": 31 item = gtk.SeparatorMenuItem() 32 else: 33 item = gtk.MenuItem(label) 34 return add_menuitem_with_item(menu, item, callback, cb_data)
35
36 -def add_image_menuitem (menu, stock, label=None, callback=None, cb_data=None):
37 """Convenience function to create an ImageMenuItem, connect 38 a callback, and add the menuitem to menu.""" 39 item = ImageMenuItem(stock, label) 40 return add_menuitem_with_item(menu, item, callback, cb_data)
41
42 -def add_menuitem_with_item (menu, item, callback=None, cb_data=None):
43 """Convenience function to add a menuitem to a menu 44 and connect a callback.""" 45 if callback: 46 if cb_data: 47 item.connect("activate", callback, cb_data) 48 else: 49 item.connect("activate", callback) 50 menu.append(item) 51 item.show() 52 return item
53
54 -def create_menu_from_file (filename, callback):
55 """Creates a menu from an XML-file and returns None if something went wrong""" 56 doc = None 57 try: 58 doc = xml.dom.minidom.parse(filename) 59 except Exception, e: 60 print _("XML-Error: %s") % str(e) 61 return None 62 return create_menu_from_xml(doc.firstChild, callback)
63
64 -def create_menu_from_xml (node, callback, icon_size=22):
65 """Create a gtk.Menu by an XML-Node""" 66 menu = gtk.Menu() 67 for node in node.childNodes: 68 #print node 69 type = node.nodeType 70 if type == Node.ELEMENT_NODE: 71 label = node.getAttribute("label") 72 id = node.getAttribute("id") 73 item = None 74 is_check = False 75 # <item> gtk.MenuItem 76 if node.nodeName == "item": 77 item = gtk.MenuItem(label) 78 # <checkitem> gtk.CheckMenuItem 79 elif node.nodeName == "checkitem": 80 item = gtk.CheckMenuItem(label) 81 is_check = True 82 if node.hasAttribute("checked"): 83 item.set_active(True) 84 # <imageitem> gtk.ImageMenuItem 85 elif node.nodeName == "imageitem": 86 icon = node.getAttribute("icon") 87 item = imageitem_from_name(icon, label, icon_size) 88 # <separator> gtk.SeparatorMenuItem 89 elif node.nodeName == "separator": 90 item = gtk.SeparatorMenuItem() 91 # <appdir> 92 elif node.nodeName == "appdir": 93 # create menu from dir with desktop-files 94 path = node.getAttribute("path") 95 appmenu = ApplicationMenu(path) 96 cats = node.getAttribute("cats").split(",") 97 for cat in cats: 98 item = gtk.MenuItem(cat) 99 #item = imageitem_from_name('games', cat) 100 submenu = appmenu.get_menu_for_category(cat, callback) 101 item.set_submenu(submenu) 102 item.show() 103 menu.append(item) 104 item = None # to overjump further append-item calls 105 # <scandir> create directory list 106 elif node.nodeName == "scandir": 107 # get dirname, prefix, suffix, replace-list, skip-list 108 dir = node.getAttribute("directory") 109 # replace $HOME with environment var 110 dir = dir.replace('$HOME', os.environ['HOME']) 111 #expr = node.getAttribute("expr") 112 idprfx = node.getAttribute("id_prefix") 113 idsufx = node.getAttribute("id_suffix") 114 srch = node.getAttribute("search").split(',') 115 repl = node.getAttribute("replace").split(',') 116 skp = node.getAttribute("skip").split(',') 117 # get filter attribute 118 flt = node.getAttribute("filter") 119 if flt=='': 120 flt='*' 121 # scan directory and append items to current menu 122 #fill_menu_from_directory(dir, menu, callback, regexp=expr, filter=flt) 123 fill_menu_from_directory(dir, menu, callback, filter=flt, 124 id_prefix=idprfx, id_suffix=idsufx, search=srch, 125 replace=repl, skip=skp) 126 # item created? 127 if item: 128 if node.hasChildNodes(): 129 # ... call function recursive and set returned menu as submenu 130 submenu = create_menu_from_xml(node, 131 callback, icon_size) 132 item.set_submenu(submenu) 133 item.show() 134 if id: 135 item.connect("activate", callback, id) 136 menu.append(item) 137 return menu
138
139 -def fill_menu_from_directory (dirname, menu, callback, filter='*', 140 id_prefix='', id_suffix='', search=[], replace=[], skip=[]):
141 """Create MenuItems from a directory. 142 TODO: use regular expressions""" 143 # create theme-list from theme-directory 144 lst = glob.glob(dirname + "/" + filter) 145 #print "Scanning: "+dirname + "/" + filter 146 lst.sort() 147 dlen = len(dirname) + 1 148 # check each entry in dir 149 for filename in lst: 150 #print "FILE: " + filename 151 fname = filename[dlen:] 152 # file allowed? 153 if skip.count(fname)<1: 154 #print "OK" 155 # create label (replace unwanted strings) 156 l = len(search) 157 if l>0 and l == len(replace): 158 for i in xrange(l): 159 fname = fname.replace(search[i], replace[i]) 160 # create label (add prefix/suffix/replace) 161 id = id_prefix + fname + id_suffix 162 #print "NAME: "+fname 163 # create menuitem 164 item = gtk.MenuItem(fname) 165 item.connect("activate", callback, id) 166 item.show() 167 menu.append(item)
168
169 -def imageitem_from_name (filename, label, icon_size=32):
170 """Creates a new gtk.ImageMenuItem from a given icon/filename. 171 If an absolute path is not given, the function checks for the name 172 of the icon within the current gtk-theme.""" 173 item = gtk.ImageMenuItem(label) 174 image = gtk.Image() 175 if filename and filename[0]=='/': 176 # load from file 177 try: 178 image.set_from_file(filename) 179 pb = image.get_pixbuf() 180 # rescale, if too big 181 if pb.get_width() > icon_size : 182 pb2 = pb.scale_simple( 183 icon_size, icon_size, 184 gtk.gdk.INTERP_HYPER) 185 image.set_from_pixbuf(pb2) 186 else: 187 image.set_from_pixbuf(pb) 188 except: 189 print _("Error while creating image from file: %s") % filename 190 return None 191 else: 192 image.set_from_icon_name(filename, 3) # TODO: use better size 193 if image: 194 item.set_image(image) 195 return item
196
197 -def read_desktop_file (filename):
198 """Read ".desktop"-file into a dict 199 NOTE: Should use utils.IniReader ...""" 200 list = {} 201 f=None 202 try: 203 f = open (filename, "r") 204 except: 205 print _("Error: file %s not found.") % filename 206 if f: 207 lines = f.readlines() 208 for line in lines: 209 if line[0] != "#" and line !="\n" and line[0] != "[": 210 ll = line.split('=', 1) 211 if len(ll) > 1: 212 list[ll[0]] = ll[1].replace("\n", "") 213 return list
214 215 #----------------------------------------------- 216 # Classes 217 #----------------------------------------------- 218
219 -class ApplicationMenu:
220 """A utility-class to simplify the creation of gtk.Menus from directories with 221 desktop-files. Reads all files in one or multiple directories into its internal list 222 and offers an easy way to create entire categories as complete gtk.Menu 223 with gtk.ImageMenuItems. """ 224 225 # the path to read files from 226 __path = "" 227 # list with apps (could be called "cache") 228 __applications = [] 229
230 - def __init__ (self, path):
231 """constructor""" 232 self.__path = path 233 self.__categories = {} 234 self.read_directory(path)
235
236 - def read_directory (self, path):
237 """read all desktop-files in a directory into the internal list 238 and sort them into the available categories""" 239 dirlst = glob.glob(path + '/*') 240 #print "Path: "+path 241 namelen = len(path) 242 for file in dirlst: 243 if file[-8:]=='.desktop': 244 fname = file[namelen:] 245 #print "file: "+fname 246 df = read_desktop_file(file) 247 name = "" 248 icon = "" 249 cmd = "" 250 try: 251 name = df['Name'] 252 icon = df['Icon'] 253 cmd = df['Exec'] 254 cats = df['Categories'].split(';') 255 #typ = df['Type'] 256 #if typ == "Application": 257 self.__applications.append(df) 258 except Exception, ex: 259 print _("Exception: %s") % str(ex) 260 print _("An error occured with desktop-file: %s") % file
261
262 - def get_menu_for_category (self, cat_name, callback):
263 """returns a gtk.Menu with all apps in the given category""" 264 # get apps in the category 265 applist = [] 266 for app in self.__applications: 267 try: 268 if (';'+app['Categories']).count(';'+cat_name+';') > 0: 269 applist.append(app) 270 except: 271 pass 272 273 # remove duplicates 274 for app in applist: 275 if applist.count(app) > 1: 276 applist.remove(app) 277 # sort list 278 applist.sort() 279 # create menu from list 280 menu = gtk.Menu() 281 for app in applist: 282 item = imageitem_from_name(app['Icon'], app['Name'], 24) 283 if item: 284 item.connect("activate", callback, "exec:" + app['Exec']) 285 item.show() 286 menu.append(item) 287 # return menu 288 return menu
289
290 -class DefaultMenuItem:
291 """A container with constants for the default menuitems""" 292 293 # default menuitem constants (is it right to increase like this?) 294 NONE = 0 295 DELETE = 1 296 THEMES = 2 297 INFO = 4 298 SIZE = 8 299 WINDOW_MENU = 16 300 PROPERTIES = 32 301 DELETE = 64 302 QUIT = 128 303 QUIT_ALL = 256 304 # EXPERIMENTAL!! If you use this, the file menu.xml in the 305 # Screenlet's data-dir is used for generating the menu ... 306 XML = 512 307 # the default items 308 STANDARD = 1|2|8|16|32|64|128|256
309 310
311 -class ImageMenuItem(gtk.ImageMenuItem):
312 """A menuitem with a custom image and label. 313 To set the image to a non-stock image, just 314 create the menuitem without an image and then 315 set the image with the appropriate method.""" 316
317 - def __init__ (self, stock=gtk.STOCK_MISSING_IMAGE, label=None):
318 """stock: a stock image or 'none'. 319 label: text to set as the label or None.""" 320 # call the superclass 321 super(ImageMenuItem, self).__init__(stock) 322 323 # get the label and image for later 324 children = self.get_children() 325 self.label = children[0] 326 self.image = children[1] 327 328 # set the label to custom text 329 if label is not None: 330 self.set_label(label)
331
332 - def set_image_from_file (self, filename):
333 """Set the image from file.""" 334 self.image.set_from_file(filename)
335
336 - def set_image_from_pixbuf (self, pixbuf):
337 """Set the image from a pixbuf.""" 338 self.image.set_from_pixbuf(pixbuf)
339
340 - def set_image_from_stock(self, name):
341 """Set the image from a stock image.""" 342 self.image.set_from_stock(name, gtk.ICON_SIZE_MENU)
343
344 - def set_label(self, text):
345 """Set the label's text.""" 346 self.label.set_text(text)
347
348 - def set_image_size (self, width, height):
349 """Resize the menuitem's image.""" 350 self.image.set_size_request(width, height)
351