add copyrights information
[appstream:software-center.git] / appcenter / view / pendingview.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
21 import logging
22 import gtk
23 import gobject
24 import apt
25 import os
26 import xapian
27 import time
28
29 import aptdaemon.client
30 from aptdaemon.enums import *
31
32 class PendingStore(gtk.ListStore):
33
34     # column names
35     (COL_TID,
36      COL_ICON, 
37      COL_NAME, 
38      COL_STATUS, 
39      COL_PROGRESS,
40      COL_CANCEL) = range(6)
41
42     # icons
43     PENDING_STORE_ICON_CANCEL = gtk.STOCK_CANCEL
44     PENDING_STORE_ICON_NO_CANCEL = "" # gtk.STOCK_YES
45
46     def __init__(self, icons):
47         # icon, status, progress
48         gtk.ListStore.__init__(self, str, gtk.gdk.Pixbuf, str, str, float, str)
49         # the apt-daemon stuff
50         self.apt_client = aptdaemon.client.AptClient()
51         self.apt_daemon = aptdaemon.client.get_aptdaemon()
52         self.apt_daemon.connect_to_signal("ActiveTransactionsChanged",
53                                           self.on_transactions_changed)
54         # FIXME: reconnect if the daemon exists
55         self._signals = []
56         self.icons = icons
57
58     def clear(self):
59         super(PendingStore, self).clear()
60         for sig in self._signals:
61             gobject.source_remove(sig)
62             del sig
63         self._signals = []
64
65     def on_transactions_changed(self, current_tid, pending_tids):
66         #print "on_transaction_changed", current_tid, len(pending_tids)
67         self.clear()
68         for tid in [current_tid]+pending_tids:
69             if not tid:
70                 continue
71             trans = aptdaemon.client.get_transaction(tid)
72             self._signals.append(
73                 trans.connect("progress", self._on_progress_changed))
74             self._signals.append(
75                 trans.connect("status", self._on_status_changed))
76             self._signals.append(
77                 trans.connect("allow-cancel", self._on_allow_cancel_changed))
78             #FIXME: role is always "Applying changes"
79             #self._signals.append(
80             #    trans.connect("role", self._on_role_changed))
81             appname = trans.get_data("appname")
82             iconname = trans.get_data("iconname")
83             if iconname:
84                 icon = self.icons.load_icon(iconname, 24, 0)
85             else:
86                 icon = None
87             self.append([tid, icon, appname, "", 0.0, ""])
88             del trans
89
90     def _on_allow_cancel_changed(self, trans, allow_cancel):
91         #print "_on_allow_cancel: ", trans, allow_cancel
92         for row in self:
93             if row[self.COL_TID] == trans.tid:
94                 if allow_cancel:
95                     row[self.COL_CANCEL] = self.PENDING_STORE_ICON_CANCEL
96                 else:
97                     row[self.COL_CANCEL] = self.PENDING_STORE_ICON_NO_CANCEL
98
99     def _on_role_changed(self, trans, role):
100         #print "_on_progress_changed: ", trans, progress
101         for row in self:
102             if row[self.COL_TID] == trans.tid:
103                 row[self.COL_NAME] = get_role_localised_present_from_enum(role)
104
105     def _on_progress_changed(self, trans, progress):
106         #print "_on_progress_changed: ", trans, progress
107         for row in self:
108             if row[self.COL_TID] == trans.tid:
109                 row[self.COL_PROGRESS] = progress
110
111     def _on_status_changed(self, trans, status):
112         #print "_on_progress_changed: ", trans, status
113         for row in self:
114             if row[self.COL_TID] == trans.tid:
115                 row[self.COL_STATUS] = get_status_string_from_enum(status)
116
117
118 class PendingView(gtk.TreeView):
119     
120     CANCEL_XPAD = 4
121
122     def __init__(self, icons):
123         gtk.TreeView.__init__(self)
124         # customization
125         self.set_headers_visible(False)
126         self.connect("button-press-event", self._on_button_pressed)
127         # icon
128         self.icons = icons
129         tp = gtk.CellRendererPixbuf()
130         column = gtk.TreeViewColumn("Icon", tp, pixbuf=PendingStore.COL_ICON)
131         column.set_fixed_width(32)
132         column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
133         self.append_column(column)
134         # name
135         tr = gtk.CellRendererText()
136         column = gtk.TreeViewColumn("Name", tr, markup=PendingStore.COL_NAME)
137         self.append_column(column)
138         # progress
139         tp = gtk.CellRendererProgress()
140         column = gtk.TreeViewColumn("Progress", tp, 
141                                     value=PendingStore.COL_PROGRESS,
142                                     text=PendingStore.COL_STATUS)
143         column.set_fixed_width(200)
144         column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
145         self.append_column(column)
146         # cancel icon
147         tp = gtk.CellRendererPixbuf()
148         tp.set_property("xpad", self.CANCEL_XPAD)
149         column = gtk.TreeViewColumn("Cancel", tp, 
150                                     stock_id=PendingStore.COL_CANCEL)
151         self.append_column(column)
152         # fake columns that eats the extra space at the end
153         tt = gtk.CellRendererText()
154         column = gtk.TreeViewColumn("Cancel", tt)
155         self.append_column(column)
156         # add it
157         store = PendingStore(icons)
158         self.set_model(store)
159     def _on_button_pressed(self, widget, event):
160         """button press handler to capture clicks on the cancel button"""
161         #print "_on_clicked: ", event
162         if event == None or event.button != 1:
163             return
164         res = self.get_path_at_pos(event.x, event.y)
165         if not res:
166             return
167         (path, column, wx, wy) = res
168         # no path
169         if not path:
170             return
171         # wrong column
172         if column.get_title() != "Cancel":
173             return
174         # not cancelable (no icon)
175         model = self.get_model()
176         if model[path][PendingStore.COL_CANCEL] == "":
177             return 
178         # get tid
179         tid = model[path][PendingStore.COL_TID]
180         trans = aptdaemon.client.get_transaction(tid)
181         trans.cancel()
182
183 if __name__ == "__main__":
184     logging.basicConfig(level=logging.DEBUG)
185
186     icons = gtk.icon_theme_get_default()
187     view = PendingView(icons)
188
189     # gui
190     scroll = gtk.ScrolledWindow()
191     scroll.add(view)
192
193     win = gtk.Window()
194     win.add(scroll)
195     view.grab_focus()
196     win.set_size_request(500,200)
197     win.show_all()
198
199     gtk.main()