add copyrights information
[appstream:software-center.git] / appcenter / view / catview.py
1 # Copyright (C) 2009 Canonical
2 #
3 # Authors:
4 #  Michael Vogt
5 #
6 # This program is free software; you can redistribute it and/or modify it under
7 # the terms of the GNU General Public License as published by the Free Software
8 # Foundation; either version 2 of the License, or (at your option) any later
9 # version.
10 #
11 # This program is distributed in the hope that it will be useful, but WITHOUT
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14 # details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # this program; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 import gobject
21 import gtk
22 import logging
23 import os
24 import xapian
25
26
27 from gettext import gettext as _
28 from xml.etree import ElementTree as ET
29 from ConfigParser import ConfigParser
30
31 (COL_CAT_NAME,
32  COL_CAT_PIXBUF,
33  COL_CAT_QUERY) = range(3)
34
35 class CategoriesModel(gtk.ListStore):
36     def __init__(self, desktopdir, xapiandb, icons):
37         gtk.ListStore.__init__(self, str, gtk.gdk.Pixbuf, object)
38         cat = self.parse_applications_menu(desktopdir)
39         for key in sorted(cat.keys()):
40             (iconname, query) = cat[key]
41             icon = icons.load_icon(iconname, 24, 0)
42             self.append([gobject.markup_escape_text(key), icon, query])
43
44     def parse_applications_menu(self, datadir):
45         " parse a application menu and build xapian querries from it "
46         tree = ET.parse(datadir+"/desktop/applications.menu")
47         categories = {}
48         only_unallocated = set()
49         root = tree.getroot()
50         for child in root.getchildren():
51             if child.tag == "Menu":
52                 name = None
53                 query = None
54                 icon = None
55                 for element in child.getchildren():
56                     if element.tag == "Name":
57                         name = element.text
58                     elif element.tag == "Directory":
59                         cp = ConfigParser()
60                         cp.read("/usr/share/desktop-directories/%s" % element.text)
61                         icon = cp.get("Desktop Entry","Icon")
62                         name = cp.get("Desktop Entry","Name")
63                     elif element.tag == "Include":
64                         query = xapian.Query("")
65                         for include in element.getchildren():
66                             if include.tag == "And":
67                                 for and_elem in include.getchildren():
68                                     if and_elem.tag == "Not":
69                                         for not_elem in and_elem.getchildren():
70                                             if not_elem.tag == "Category":
71                                                 q = xapian.Query("AC"+not_elem.text.lower())
72                                                 query = xapian.Query(xapian.Query.OP_AND_NOT, query, q)
73                                                 
74                                     elif and_elem.tag == "Category":
75                                         print "adding: ", and_elem.text
76                                         q = xapian.Query("AC"+and_elem.text.lower())
77                                         query = xapian.Query(xapian.Query.OP_AND, query, q)
78                                     else: 
79                                         print "UNHANDLED: ", and_elem.tag, and_elem.text
80                     elif element.tag == "OnlyUnallocated":
81                         only_unallocated.add(name)
82                     if name and query:
83                         categories[name] = (icon, query)
84         # post processing for <OnlyUnallocated>
85         for unalloc in only_unallocated:
86             (icon, query) = categories[unalloc]
87             for key in categories:
88                 if key != unalloc:
89                     (ic, q) = categories[key]
90                     query = xapian.Query(xapian.Query.OP_AND_NOT, query, q)
91             categories[unalloc] = (icon, query)
92         # debug print
93         for cat in categories:
94             (icon, query) = categories[cat]
95             print cat, query.get_description()
96         return categories
97
98 class LabeledCategoriesView(gtk.Viewport):
99     def __init__(self, datadir, xapiandb, icons):
100         gtk.Viewport.__init__(self)
101         # a vbox in the outside and a hbox in the inside
102         vbox = gtk.VBox()
103         align = gtk.Alignment()
104         top = bottom = 2
105         left = right = 8
106         align.set_padding(top, bottom, left, right)
107         # we need a eventbox around the label to set the background
108         label = gtk.Label("")
109         label.set_markup("<b>%s</b>" % _("Categories"))
110         label.set_alignment(0.0, 0.5)
111         align.add(label)
112         align.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("yellow"))
113         eb = gtk.EventBox()
114         eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("yellow"))
115         eb.add(align)
116         # needed to make the background not spawn all over
117         hbox_inside = gtk.HBox()
118         hbox_inside.pack_start(eb, expand=False, fill=False)
119         # FIXME: how to make sure the background color is right
120         #hbox_inside.pack_start(gtk.IconView())
121         vbox.pack_start(hbox_inside, expand=False, fill=False)
122         # and now the categoryies
123         self.catview = CategoriesView(datadir, xapiandb, icons)
124         vbox.pack_start(self.catview)
125         self.add(vbox)
126         # FIXME: add the row-activated signal
127
128 class CategoriesView(gtk.IconView):
129     def __init__(self, datadir, xapiandb, icons):
130         # model
131         model = CategoriesModel(datadir, xapiandb, icons)
132         gtk.IconView.__init__(self, model)
133         # data
134         self.xapiandb = xapiandb
135         self.icons = icons
136         self.cursor_hand = gtk.gdk.Cursor(gtk.gdk.HAND2)
137         # customization
138         self.set_markup_column(COL_CAT_NAME)
139         self.set_pixbuf_column(COL_CAT_PIXBUF)
140         # signals
141         self.connect("motion-notify-event", self.on_motion_notify_event)
142         self.connect("button-press-event", self.on_button_press_event)
143     def on_motion_notify_event(self, widget, event):
144         #print "on_motion_notify_event: ", event
145         path = self.get_path_at_pos(event.x, event.y)
146         if path is None:
147             self.window.set_cursor(None)
148         else:
149             self.window.set_cursor(self.cursor_hand)
150     def on_button_press_event(self, widget, event):
151         #print "on_button_press_event: ", event
152         path = self.get_path_at_pos(event.x, event.y)
153         if event.button != 1 or path is None:
154             return
155         self.emit("item-activated", path)
156
157 # test code
158 def category_activated(iconview, path, xapiandb):
159     (name, pixbuf, query) = iconview.get_model()[path]
160     enquire = xapian.Enquire(xapiandb)
161     enquire.set_query(query)
162     matches = enquire.get_mset(0, 2000)
163     for m in matches:
164         doc = m[xapian.MSET_DOCUMENT]
165         appname = doc.get_data()
166         print "appname: ", appname,
167             #for t in doc.termlist():
168             #    print "'%s': %s (%s); " % (t.term, t.wdf, t.termfreq),
169             #print "\n"
170     print len(matches)
171
172 if __name__ == "__main__":
173
174     logging.basicConfig(level=logging.DEBUG)
175
176     datadir = "/usr/share/app-install"
177
178     xapian_base_path = "/var/cache/app-install"
179     pathname = os.path.join(xapian_base_path, "xapian")
180     db = xapian.Database(pathname)
181
182     # additional icons come from app-install-data
183     icons = gtk.icon_theme_get_default()
184     icons.append_search_path("/usr/share/app-install/icons/")
185
186     # now the store
187     #view = LabeledCategoriesView(datadir, db, icons)
188     view = CategoriesView(datadir, db, icons)
189     view.connect("item-activated", category_activated, db)
190
191     # gui
192     scroll = gtk.ScrolledWindow()
193     scroll.add(view)
194
195     win = gtk.Window()
196     win.add(scroll)
197     view.grab_focus()
198     win.set_size_request(500,200)
199     win.show_all()
200
201     gtk.main()