~ubuntu-branches/ubuntu/lucid/software-center/lucid

« back to all changes in this revision

Viewing changes to softwarecenter/view/viewswitcher.py

  • Committer: Bazaar Package Importer
  • Author(s): Michael Vogt, Michael Vogt, Gary Lasker
  • Date: 2010-02-17 10:13:41 UTC
  • Revision ID: james.westby@ubuntu.com-20100217101341-x134t7lm0bss3jxn
Tags: 1.1.12
[ Michael Vogt ]
* merged lp:~mmcg069/software-center/spinner  (many thanks)
* merged lp:~mmcg069/software-center/fixes-and-tweaks  (many thanks)

[ Gary Lasker ]
* implement standalone presentation of individual software
  repositories

[ Michael Vogt ]
* README:
  - document menu.d/ directory
* softwarecenter/view/catview.py:
  - add SCPkgname xml filter
* apt-xapian-index-plugin/software-center.py:
  - add custom apt-xapian-index plugins for indexing 
    "XB-Softwarecenter-Appname" in debian/control
* apt-xapian-index-plugin/origin.py:
  - add custom apt-xapian-index plugins for indexing the origins
* softwarecenter/distro/Ubuntu.py:
  - add abstraction for distro_channel_{name,description}
* softwarecenter/distro/__init__.py:
  - make get_distro() return a singleton
* softwarecenter/view/channelpane.py:
  - for the main distro channel, show only apps, not all packages
* data/featured.menu.in:
  - add a "Featured" category

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
 
32
32
from gettext import gettext as _
33
33
 
 
34
 
34
35
from softwarecenter.backend import get_install_backend
 
36
from softwarecenter.distro import get_distro
 
37
from softwarecenter.db.database import StoreDatabase
 
38
from softwarecenter.enums import *
 
39
 
35
40
from widgets.animatedimage import CellRendererAnimatedImage, AnimatedImage
36
41
 
37
42
class ViewSwitcher(gtk.TreeView):
39
44
    __gsignals__ = {
40
45
        "view-changed" : (gobject.SIGNAL_RUN_LAST,
41
46
                          gobject.TYPE_NONE, 
42
 
                          (int, ),
 
47
                          (int, str),
43
48
                         ),
44
49
    }
45
50
 
46
51
 
47
 
    def __init__(self, datadir, icons, store=None):
 
52
    def __init__(self, datadir, db, icons, store=None):
48
53
        super(ViewSwitcher, self).__init__()
49
54
        self.datadir = datadir
50
55
        self.icons = icons
51
56
        if not store:
52
 
            store = ViewSwitcherList(datadir, icons)
 
57
            store = ViewSwitcherList(datadir, db, icons)
53
58
            # FIXME: this is just set here for app.py, make the
54
59
            #        transactions-changed signal part of the view api
55
60
            #        instead of the model
70
75
        self.set_model(store)
71
76
        self.set_headers_visible(False)
72
77
        self.get_selection().set_select_function(self.on_treeview_selected)
73
 
        # expand the first entry (get software)
74
 
        self.expand_to_path((0,))
75
78
        self.set_level_indentation(4)
76
79
        self.set_enable_search(False)
77
80
        
96
99
        (path, column) = self.get_cursor()
97
100
        model = self.get_model()
98
101
        action = model[path][ViewSwitcherList.COL_ACTION]
99
 
        self.emit("view-changed", action) 
 
102
        channel_name = model[path][ViewSwitcherList.COL_CHANNEL_NAME]
 
103
        self.emit("view-changed", action, channel_name)
100
104
        
101
105
    def get_view(self):
102
106
        """return the current activated view number or None if no
104
108
           disappeared). Views are:
105
109
           
106
110
           ViewSwitcherList.ACTION_ITEM_AVAILABLE
 
111
           ViewSwitcherList.ACTION_ITEM_CHANNEL
107
112
           ViewSwitcherList.ACTION_ITEM_INSTALLED
108
113
           ViewSwitcherList.ACTION_ITEM_PENDING
109
114
        """
113
118
        return path[0]
114
119
    def set_view(self, action):
115
120
        self.set_cursor((action,))
116
 
        self.emit("view-changed", action)
 
121
        self.emit("view-changed", action, None)
117
122
    def on_motion_notify_event(self, widget, event):
118
123
        #print "on_motion_notify_event: ", event
119
124
        path = self.get_path_at_pos(int(event.x), int(event.y))
127
132
    # columns
128
133
    (COL_ICON,
129
134
     COL_NAME,
130
 
     COL_ACTION) = range(3)
 
135
     COL_ACTION,
 
136
     COL_CHANNEL_NAME) = range(4)
131
137
 
132
138
    # items in the treeview
133
139
    (ACTION_ITEM_AVAILABLE,
134
140
     ACTION_ITEM_INSTALLED,
135
141
     ACTION_ITEM_SEPARATOR_1,
136
 
     ACTION_ITEM_PENDING) = range(4)
 
142
     ACTION_ITEM_PENDING,
 
143
     ACTION_ITEM_CHANNEL) = range(5)
137
144
 
138
145
    ICON_SIZE = 24
139
146
 
140
147
    ANIMATION_PATH = "/usr/share/icons/hicolor/24x24/status/softwarecenter-progress.png"
141
148
 
142
 
    def __init__(self, datadir, icons):
143
 
        gtk.TreeStore.__init__(self, AnimatedImage, str, int)
 
149
    def __init__(self, datadir, db, icons):
 
150
        gtk.TreeStore.__init__(self, AnimatedImage, str, int, str)
144
151
        self.icons = icons
145
152
        self.datadir = datadir
146
153
        self.backend = get_install_backend()
147
154
        self.backend.connect("transactions-changed", self.on_transactions_changed)
 
155
        self.db = db
 
156
        self.distro = get_distro()
 
157
        # pending transactions
 
158
        self._pending = 0
148
159
        # setup the normal stuff
149
 
        if self.icons.lookup_icon("softwarecenter", self.ICON_SIZE, 0):
150
 
            icon = AnimatedImage(self.icons.load_icon("softwarecenter", self.ICON_SIZE, 0))
151
 
        else:
152
 
            icon = AnimatedImage(self.icons.load_icon("gtk-missing-image", 
153
 
                                                      self.ICON_SIZE, 0))
154
 
        piter = self.append(None, [icon, _("Get Software"), self.ACTION_ITEM_AVAILABLE])
155
 
        
156
 
        if self.icons.lookup_icon("distributor-logo", self.ICON_SIZE, 0):
157
 
            icon = AnimatedImage(self.icons.load_icon("distributor-logo", self.ICON_SIZE, 0))
158
 
        else:
159
 
            # icon not present in theme, probably because running uninstalled
160
 
            icon = AnimatedImage(self.icons.load_icon("gtk-missing-image", 
161
 
                                                      self.ICON_SIZE, 0))
162
 
        self.append(piter, [icon, _("Free Software"), self.ACTION_ITEM_AVAILABLE])
 
160
        available_icon = self._get_icon("softwarecenter")
 
161
        available_iter = self.append(None, [available_icon, _("Get Software"), self.ACTION_ITEM_AVAILABLE, None])
 
162
        
 
163
        # gather icons for use with channel sources
 
164
        self.dist_icon = self._get_icon("distributor-logo")
 
165
        self.partner_icon = self._get_icon("partner")
 
166
        self.ppa_icon = self._get_icon("ppa")
 
167
        self.generic_repo_icon = self._get_icon("generic-repository")
 
168
        self.unknown_channel_icon = self._get_icon("unknown-channel")
 
169
        
 
170
        # get list of channel sources of form:
 
171
        #     [icon, label, action, channel_name]
 
172
        channel_sources = self._get_channel_sources()
 
173
        
 
174
        # iterate the channel sources list and add as subnodes of the available node
 
175
        for channel in channel_sources:
 
176
            self.append(available_iter, channel)
163
177
        
164
178
        icon = AnimatedImage(self.icons.load_icon("computer", self.ICON_SIZE, 0))
165
 
        self.append(None, [icon, _("Installed Software"), self.ACTION_ITEM_INSTALLED])
 
179
        installed_iter = self.append(None, [icon, _("Installed Software"), self.ACTION_ITEM_INSTALLED, ""])
166
180
        icon = AnimatedImage(None)
167
 
        self.append(None, [icon, "<span size='1'> </span>", self.ACTION_ITEM_SEPARATOR_1])
 
181
        self.append(None, [icon, "<span size='1'> </span>", self.ACTION_ITEM_SEPARATOR_1, ""])
168
182
 
169
183
    def on_transactions_changed(self, backend, total_transactions):
170
184
        logging.debug("on_transactions_changed '%s'" % total_transactions)
184
198
                if row[self.COL_ACTION] == self.ACTION_ITEM_PENDING:
185
199
                    del self[(i,)]
186
200
 
 
201
    def _get_icon(self, icon_name):
 
202
        if self.icons.lookup_icon(icon_name, self.ICON_SIZE, 0):
 
203
            icon = AnimatedImage(self.icons.load_icon(icon_name, self.ICON_SIZE, 0))
 
204
        else:
 
205
            # icon not present in theme, probably because running uninstalled
 
206
            icon = AnimatedImage(self.icons.load_icon("gtk-missing-image", 
 
207
                                                      self.ICON_SIZE, 0))
 
208
        return icon
 
209
        
 
210
    def _get_channel_sources(self):
 
211
        """
 
212
        return a list of channel sources, with each entry in the list
 
213
        in the form:
 
214
               [icon, label, action, channel_name]
 
215
        """        
 
216
        channels = []
 
217
        for channel_iter in self.db.xapiandb.allterms("XOL"):
 
218
            if len(channel_iter.term) == 3:
 
219
                continue
 
220
            channel_name = channel_iter.term[3:]
 
221
            
 
222
            # get origin information for this channel
 
223
            m = self.db.xapiandb.postlist_begin(channel_iter.term)
 
224
            doc = self.db.xapiandb.get_document(m.get_docid())
 
225
            for term_iter in doc.termlist():
 
226
                if term_iter.term.startswith("XOO") and len(term_iter.term) > 3: 
 
227
                    channel_origin = term_iter.term[3:]
 
228
                    break
 
229
            logging.debug("channel_name: %s" % channel_name)
 
230
            logging.debug("channel_origin: %s" % channel_origin)
 
231
            channels.append((channel_name, channel_origin))
 
232
            
 
233
        channel_sources = []
 
234
        for (channel_name, channel_origin) in self._order_channels(channels):
 
235
            channel_sources.append(
 
236
                [self._get_icon_for_channel(channel_name, channel_origin), 
 
237
                 self._get_display_name_for_channel(channel_name),
 
238
                 self.ACTION_ITEM_CHANNEL,
 
239
                 channel_name])     
 
240
                
 
241
        return channel_sources
 
242
        
 
243
    def _order_channels(self, channels):
 
244
        """
 
245
        given a list of channels, order them according to:
 
246
            Distribution, Partners, PPAs alphabetically, Other channels alphabetically,
 
247
            Unknown channel last
 
248
        """
 
249
        dist_channel = []
 
250
        partner_channel = []
 
251
        ppa_channels = []
 
252
        other_channels = []
 
253
        unknown_channel = []
 
254
        ordered_channels = []
 
255
        
 
256
        for (channel_name, channel_origin) in channels:
 
257
            if not channel_name:
 
258
                unknown_channel.append((channel_name, channel_origin))
 
259
            elif channel_name == self.distro.get_distro_channel_name():
 
260
                dist_channel.append((channel_name, channel_origin))
 
261
            elif channel_origin and channel_origin.startswith("LP-PPA"):
 
262
                ppa_channels.append((channel_name, channel_origin))
 
263
            # TODO: detect generic repository source (e.g., Google, Inc.)
 
264
            # TODO: detect partner channel
 
265
            else:
 
266
                other_channels.append((channel_name, channel_origin))
 
267
        
 
268
        # set them in order
 
269
        ordered_channels.extend(dist_channel)
 
270
        ordered_channels.extend(partner_channel)
 
271
        ordered_channels.extend(ppa_channels)
 
272
        ordered_channels.extend(other_channels)
 
273
        ordered_channels.extend(unknown_channel)
 
274
        
 
275
        return ordered_channels
 
276
        
 
277
    def _get_icon_for_channel(self, channel_name, channel_origin):
 
278
        """
 
279
        return the icon that corresponds to each channel node based
 
280
        on the channel name and its origin string
 
281
        """
 
282
        if not channel_name:
 
283
            channel_icon = self.unknown_channel_icon
 
284
        elif channel_name == self.distro.get_distro_channel_name():
 
285
            channel_icon = self.dist_icon
 
286
        elif channel_origin and channel_origin.startswith("LP-PPA"):
 
287
            channel_icon = self.ppa_icon
 
288
        # TODO: add check for generic repository source (e.g., Google, Inc.)
 
289
        # TODO: add check for partner_icon
 
290
        else:
 
291
            channel_icon = self.unknown_channel_icon
 
292
        return channel_icon
 
293
        
 
294
    def _get_display_name_for_channel(self, channel_name):
 
295
        """
 
296
        return the display name for the corresponding channel node
 
297
        """
 
298
        if not channel_name:
 
299
            channel_display_name = _("Other")
 
300
        elif channel_name == self.distro.get_distro_channel_name():
 
301
            channel_display_name = self.distro.get_distro_channel_description()
 
302
        else:
 
303
            channel_display_name = channel_name
 
304
        return channel_display_name
 
305
 
187
306
if __name__ == "__main__":
188
307
    logging.basicConfig(level=logging.DEBUG)
189
308
    import sys
197
316
 
198
317
    scroll = gtk.ScrolledWindow()
199
318
    icons = gtk.icon_theme_get_default()
200
 
    view = ViewSwitcher(datadir, icons)
 
319
 
 
320
    xapian_base_path = XAPIAN_BASE_PATH
 
321
    pathname = os.path.join(xapian_base_path, "xapian")
 
322
    cache = apt.Cache(apt.progress.OpTextProgress())
 
323
    db = StoreDatabase(pathname, cache)
 
324
    db.open()
 
325
 
 
326
    view = ViewSwitcher(datadir, db, icons)
201
327
 
202
328
    box = gtk.VBox()
203
329
    box.pack_start(scroll)