~ubuntu-branches/ubuntu/hardy/screenlets/hardy-backports

« back to all changes in this revision

Viewing changes to src/lib/XmlMenu.py

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2007-10-19 16:52:50 UTC
  • Revision ID: james.westby@ubuntu.com-20071019165250-rzvyfmphfsbl6kip
Tags: upstream-0.0.10
ImportĀ upstreamĀ versionĀ 0.0.10

Show diffs side-by-side

added added

removed removed

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