~tualatrix/ubuntu-tweak/arb-packaging

« back to all changes in this revision

Viewing changes to ubuntutweak/modules/cleaner.py

  • Committer: TualatriX
  • Date: 2009-10-22 14:14:56 UTC
  • Revision ID: git-v1:455f01496d7149fb9832dabdf1bf0eef506a0101
WIP, make most things works

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
# Ubuntu Tweak - PyGTK based desktop configure tool
 
4
#
 
5
# Copyright (C) 2007-2008 TualatriX <tualatrix@gmail.com>
 
6
#
 
7
# Ubuntu Tweak is free software; you can redistribute it and/or modify
 
8
# it under the terms of the GNU General Public License as published by
 
9
# the Free Software Foundation; either version 2 of the License, or
 
10
# (at your option) any later version.
 
11
#
 
12
# Ubuntu Tweak is distributed in the hope that it will be useful,
 
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
15
# GNU General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU General Public License
 
18
# along with Ubuntu Tweak; if not, write to the Free Software Foundation, Inc.,
 
19
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 
20
 
 
21
import os
 
22
import gtk
 
23
import thread
 
24
import gobject
 
25
import gettext
 
26
 
 
27
from tweak import TweakModule
 
28
from common.utils import *
 
29
from common.misc import filesizeformat
 
30
from common.policykit import PolkitButton, DbusProxy
 
31
from common.package import package_worker
 
32
from common.widgets.dialogs import *
 
33
from common.widgets.utils import ProcessDialog
 
34
from backends.packageconfig import PATH
 
35
 
 
36
(
 
37
    COLUMN_CHECK,
 
38
    COLUMN_ICON,
 
39
    COLUMN_NAME,
 
40
    COLUMN_DESC,
 
41
    COLUMN_DISPLAY,
 
42
) = range(5)
 
43
 
 
44
class AbsPkg:
 
45
    def __init__(self, pkg, des):
 
46
        self.name = pkg
 
47
        self.des = des
 
48
 
 
49
class CleanConfigDialog(ProcessDialog):
 
50
    def __init__(self, parent, pkgs):
 
51
        self.pkgs = pkgs
 
52
        self.done = False
 
53
        self.user_action = False
 
54
 
 
55
        super(CleanConfigDialog, self).__init__(parent = parent)
 
56
        self.set_dialog_lable(_('Cleaning Package Config'))
 
57
        self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)
 
58
 
 
59
    def process_data(self):
 
60
        for pkg in self.pkgs:
 
61
            if self.user_action == True:
 
62
                break
 
63
            self.set_progress_text(_('Cleaning...%s') % pkg)
 
64
            proxy.clean_config(pkg)
 
65
        self.done = True
 
66
        
 
67
    def on_timeout(self):
 
68
        self.pulse()
 
69
 
 
70
        if not self.done:
 
71
            return True
 
72
        else:
 
73
            self.destroy()
 
74
 
 
75
class CleanCacheDailog(ProcessDialog):
 
76
    def __init__(self, parent, files):
 
77
        self.files = files
 
78
        self.done = False
 
79
        self.error = False
 
80
        self.user_action = False
 
81
 
 
82
        super(CleanCacheDailog, self).__init__(parent=parent)
 
83
        self.set_dialog_lable(_('Cleaning Package Cache'))
 
84
        self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)
 
85
 
 
86
    def process_data(self):
 
87
        for file in self.files:
 
88
            if self.user_action == True:
 
89
                break
 
90
            self.set_progress_text(_('Cleaning...%s') % os.path.basename(file))
 
91
            result = proxy.delete_file(file)
 
92
            if result == 'error':
 
93
                self.error = True
 
94
                break
 
95
        self.done = True
 
96
 
 
97
    def on_timeout(self):
 
98
        self.pulse()
 
99
 
 
100
        if not self.done:
 
101
            return True
 
102
        else:
 
103
            self.destroy()
 
104
 
 
105
class PackageView(gtk.TreeView):
 
106
    __gsignals__ = {
 
107
        'checked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, 
 
108
                    (gobject.TYPE_BOOLEAN,)),
 
109
        'cleaned': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ())
 
110
    }
 
111
 
 
112
    def __init__(self):
 
113
        super(PackageView, self).__init__()
 
114
 
 
115
        model = self.__create_model()
 
116
        self.set_model(model)
 
117
        model.set_sort_column_id(COLUMN_NAME, gtk.SORT_ASCENDING)
 
118
 
 
119
        self.__check_list = []
 
120
        self.package_worker = package_worker
 
121
 
 
122
        self.__add_column()
 
123
 
 
124
        self.mode = 'package'
 
125
        self.update_package_model()
 
126
        self.selection = self.get_selection()
 
127
        self.set_sensitive(False)
 
128
 
 
129
    def __create_model(self):
 
130
        model = gtk.ListStore(
 
131
                gobject.TYPE_BOOLEAN,
 
132
                gtk.gdk.Pixbuf,
 
133
                gobject.TYPE_STRING,
 
134
                gobject.TYPE_STRING,
 
135
                gobject.TYPE_STRING)
 
136
 
 
137
        return model
 
138
 
 
139
    def __add_column(self):
 
140
        renderer = gtk.CellRendererToggle()
 
141
        renderer.connect('toggled', self.on_package_toggled)
 
142
        column = gtk.TreeViewColumn(' ', renderer, active = COLUMN_CHECK)
 
143
        column.set_sort_column_id(COLUMN_CHECK)
 
144
        self.append_column(column)
 
145
 
 
146
        self.__column = gtk.TreeViewColumn(_('Package'))
 
147
        self.__column.set_sort_column_id(COLUMN_NAME)
 
148
        self.__column.set_spacing(5)
 
149
        renderer = gtk.CellRendererPixbuf()
 
150
        self.__column.pack_start(renderer, False)
 
151
        self.__column.set_attributes(renderer, pixbuf = COLUMN_ICON)
 
152
 
 
153
        renderer = gtk.CellRendererText()
 
154
        self.__column.pack_start(renderer, True)
 
155
        self.__column.set_attributes(renderer, markup = COLUMN_DISPLAY)
 
156
 
 
157
        self.append_column(self.__column)
 
158
 
 
159
    def get_list(self):
 
160
        return self.__check_list
 
161
 
 
162
    def update_package_model(self):
 
163
        self.set_busy()
 
164
        model = self.get_model()
 
165
        model.clear()
 
166
        self.mode = 'package'
 
167
 
 
168
        icon = get_icon_with_name('deb', 24)
 
169
        list = self.package_worker.list_autoremovable()
 
170
        self.total_num = len(list)
 
171
        self.__column.set_title(_('Packages'))
 
172
 
 
173
        while gtk.events_pending():
 
174
            gtk.main_iteration()
 
175
 
 
176
        for pkg in list:
 
177
            desc = self.package_worker.get_pkgsummary(pkg)
 
178
 
 
179
            iter = model.append()
 
180
            model.set(iter,
 
181
                   COLUMN_CHECK, False,
 
182
                   COLUMN_ICON, icon,
 
183
                   COLUMN_NAME, pkg,
 
184
                   COLUMN_DESC, desc,
 
185
                   COLUMN_DISPLAY, '<b>%s</b>\n%s' % (pkg, desc)
 
186
                )
 
187
 
 
188
        self.unset_busy()
 
189
 
 
190
    def update_kernel_model(self):
 
191
        self.set_busy()
 
192
        model = self.get_model()
 
193
        model.clear()
 
194
        self.mode = 'kernel'
 
195
 
 
196
        icon = get_icon_with_name('deb', 24)
 
197
        list = self.package_worker.list_unneeded_kerenl()
 
198
        self.total_num = len(list)
 
199
        self.__column.set_title(_('Packages'))
 
200
 
 
201
        while gtk.events_pending():
 
202
            gtk.main_iteration()
 
203
 
 
204
        for pkg in list:
 
205
            desc = self.package_worker.get_pkgsummary(pkg)
 
206
 
 
207
            iter = model.append()
 
208
            model.set(iter,
 
209
                   COLUMN_CHECK, False,
 
210
                   COLUMN_ICON, icon,
 
211
                   COLUMN_NAME, pkg,
 
212
                   COLUMN_DESC, desc,
 
213
                   COLUMN_DISPLAY, '<b>%s</b>\n%s' % (pkg, desc)
 
214
                )
 
215
        self.unset_busy()
 
216
 
 
217
    def update_cache_model(self):
 
218
        self.set_busy()
 
219
        model = self.get_model()
 
220
        model.clear()
 
221
        self.mode = 'cache'
 
222
 
 
223
        cache_dir = '/var/cache/apt/archives' 
 
224
        icon = get_icon_with_name('deb', 24)
 
225
        list = map(lambda file: '%s/%s' % (cache_dir, file),
 
226
                    filter(lambda x:x.endswith('deb'), os.listdir(cache_dir))) 
 
227
        self.total_num = len(list)
 
228
        self.__column.set_title(_('Package Cache'))
 
229
 
 
230
        while gtk.events_pending():
 
231
            gtk.main_iteration()
 
232
 
 
233
        for pkg in list:
 
234
            size = str(os.path.getsize(pkg))
 
235
 
 
236
            iter = model.append()
 
237
            model.set(iter,
 
238
                   COLUMN_ICON, icon,
 
239
                   COLUMN_CHECK, False,
 
240
                   COLUMN_NAME, pkg,
 
241
                   COLUMN_DESC, size,
 
242
                   COLUMN_DISPLAY, _('<b>%s</b>\nTake %s of disk space') % (os.path.basename(pkg), filesizeformat(size)) 
 
243
 
 
244
                )
 
245
        self.unset_busy()
 
246
 
 
247
    def update_config_model(self):
 
248
        self.set_busy()
 
249
        model = self.get_model()
 
250
        model.clear()
 
251
        self.mode = 'config'
 
252
#        command = "dpkg -l |awk '/^rc/ {print $2}'"
 
253
        icon = get_icon_with_name('text', 24)
 
254
 
 
255
        list = []
 
256
        for line in os.popen('dpkg -l'):
 
257
            try:
 
258
                temp_list = line.split()
 
259
                status, pkg = temp_list[0], temp_list[1]
 
260
                if status == 'rc':
 
261
                    des = temp_list[3:]
 
262
                    pkg = AbsPkg(pkg, ' '.join(temp_list[3:]))
 
263
                    list.append(pkg) 
 
264
            except:
 
265
                pass
 
266
 
 
267
        self.total_num = len(list)
 
268
        self.__column.set_title(_('Package Config'))
 
269
 
 
270
        while gtk.events_pending():
 
271
            gtk.main_iteration()
 
272
 
 
273
        for pkg in list:
 
274
            iter = model.append()
 
275
            model.set(iter,
 
276
                   COLUMN_CHECK, False,
 
277
                   COLUMN_ICON, icon,
 
278
                   COLUMN_NAME, pkg.name,
 
279
                   COLUMN_DESC, 0,
 
280
                   COLUMN_DISPLAY, '<b>%s</b>\n%s' % (pkg.name, pkg.des),
 
281
                )
 
282
        self.unset_busy()
 
283
 
 
284
    def on_package_toggled(self, cell, path):
 
285
        model = self.get_model()
 
286
        iter = model.get_iter(path)
 
287
 
 
288
        check = model.get_value(iter, COLUMN_CHECK)
 
289
        model.set(iter, COLUMN_CHECK, not check)
 
290
        if not check:
 
291
            self.__check_list.append(model.get_value(iter, COLUMN_NAME))
 
292
        else:
 
293
            self.__check_list.remove(model.get_value(iter, COLUMN_NAME))
 
294
 
 
295
        if len(self.__check_list) == self.total_num:
 
296
            self.emit('checked', True)
 
297
        else:
 
298
            self.emit('checked', False)
 
299
 
 
300
        self.set_column_title()
 
301
 
 
302
    def set_column_title(self):
 
303
        if self.mode == 'package' or self.mode == 'kernel':
 
304
            n = len(self.__check_list)
 
305
            self.__column.set_title(
 
306
                    gettext.ngettext('%d package selected to remove' % n, 
 
307
                                    '%d packages selected to remove' % n, n))
 
308
        elif self.mode == 'cache':
 
309
            self.compute_cache_size()
 
310
            self.__column.set_title(_('%s of space will be freed') % filesizeformat(self.size))
 
311
 
 
312
    def compute_cache_size(self):
 
313
        self.size = 0
 
314
 
 
315
        model = self.get_model()
 
316
        model.foreach(self.__cache_foreach)
 
317
 
 
318
    def __cache_foreach(self, model, path, iter):
 
319
        cache = model.get_value(iter, COLUMN_NAME)
 
320
 
 
321
        if cache in self.__check_list:
 
322
            size = model.get_value(iter, COLUMN_DESC)
 
323
            self.size = self.size + int(size)
 
324
 
 
325
    def select_all(self, check = True):
 
326
        self.__check_list = []
 
327
 
 
328
        model = self.get_model()
 
329
        model.foreach(self.__select_foreach, check)
 
330
        self.emit('checked', check)
 
331
 
 
332
        self.set_column_title()
 
333
 
 
334
    def __select_foreach(self, model, path, iter, check):
 
335
        model.set(iter, COLUMN_CHECK, check)
 
336
        if check:
 
337
            self.__check_list.append(model.get_value(iter, COLUMN_NAME))
 
338
 
 
339
    def clean_selected_package(self):
 
340
        self.set_busy()
 
341
        state = self.package_worker.perform_action(self.get_toplevel(), [],self.__check_list)
 
342
 
 
343
        if state == 0:
 
344
            self.show_success_dialog()
 
345
        else:
 
346
            self.show_failed_dialog()
 
347
 
 
348
        package_worker.update_apt_cache(True)
 
349
        if self.mode == 'package':
 
350
            self.update_package_model()
 
351
        else:
 
352
            self.update_kernel_model()
 
353
        self.__check_list = []
 
354
        self.emit('cleaned')
 
355
        self.unset_busy()
 
356
 
 
357
    def clean_selected_cache(self):
 
358
        self.set_busy()
 
359
        model = self.get_model()
 
360
 
 
361
        dialog = CleanCacheDailog(self.get_toplevel(), self.get_list())
 
362
        if dialog.run() == gtk.RESPONSE_REJECT:
 
363
            dialog.destroy()
 
364
            dialog.user_action = True
 
365
            self.show_usercancel_dialog()
 
366
 
 
367
        if not dialog.user_action:
 
368
            if dialog.error == False:
 
369
                self.show_success_dialog()
 
370
            else:
 
371
                self.show_failed_dialog()
 
372
 
 
373
        self.update_cache_model()
 
374
        self.emit('cleaned')
 
375
        self.unset_busy()
 
376
 
 
377
    def clean_selected_config(self):
 
378
        self.set_busy()
 
379
        model = self.get_model()
 
380
 
 
381
        dialog = CleanConfigDialog(self.get_toplevel(), self.get_list())
 
382
        if dialog.run() == gtk.RESPONSE_REJECT:
 
383
            dialog.destroy()
 
384
            dialog.user_action = True
 
385
            self.show_usercancel_dialog()
 
386
        else:
 
387
            self.show_success_dialog()
 
388
 
 
389
        package_worker.update_apt_cache(True)
 
390
        self.update_config_model()
 
391
        self.emit('cleaned')
 
392
        self.unset_busy()
 
393
 
 
394
    def show_usercancel_dialog(self):
 
395
        InfoDialog(_('Canceled by user!')).launch()
 
396
 
 
397
    def show_success_dialog(self):
 
398
        InfoDialog(_('Clean up Successful!')).launch()
 
399
 
 
400
    def show_failed_dialog(self):
 
401
        ErrorDialog(_('Clean up Failed!')).launch()
 
402
 
 
403
    def set_busy(self):
 
404
        window = self.get_toplevel().window
 
405
        if window:
 
406
            window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
 
407
 
 
408
    def unset_busy(self):
 
409
        window = self.get_toplevel().window
 
410
        if window:
 
411
            window.set_cursor(None)
 
412
 
 
413
class PackageCleaner(TweakModule):
 
414
    __title__ = _('Package Cleaner')
 
415
    __desc__ = _('Free up disk space by removing unneeded packages and cleaning the package download cache.')
 
416
    __icon__ = 'edit-clear'
 
417
    __category__ = 'application'
 
418
 
 
419
    def __init__(self):
 
420
        TweakModule.__init__(self)
 
421
 
 
422
        self.to_add = []
 
423
        self.to_rm = []
 
424
        self.button_list = []
 
425
        self.current_button = 0
 
426
 
 
427
        hbox = gtk.HBox(False, 0)
 
428
        self.add_start(hbox, True, True, 0)
 
429
 
 
430
        sw = gtk.ScrolledWindow()
 
431
        sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
 
432
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
433
        hbox.pack_start(sw)
 
434
 
 
435
        vbox = gtk.VBox(False, 8)
 
436
        hbox.pack_start(vbox, False, False, 5)
 
437
 
 
438
        # create tree view
 
439
        self.treeview = PackageView()
 
440
        self.treeview.set_rules_hint(True)
 
441
        self.treeview.connect('checked', self.on_item_checked)
 
442
        self.treeview.connect('cleaned', self.on_item_cleaned)
 
443
        sw.add(self.treeview)
 
444
 
 
445
        # create the button
 
446
        self.pkg_button = self.create_button(_('Clean Package'), 
 
447
                gtk.image_new_from_pixbuf(get_icon_with_name('deb', 24)),
 
448
                self.treeview.update_package_model)
 
449
        vbox.pack_start(self.pkg_button, False, False, 0)
 
450
 
 
451
        self.cache_button = self.create_button(_('Clean Cache'), 
 
452
                gtk.image_new_from_stock(gtk.STOCK_CLEAR, gtk.ICON_SIZE_BUTTON),
 
453
                self.treeview.update_cache_model)
 
454
        vbox.pack_start(self.cache_button, False, False, 0)
 
455
 
 
456
        self.config_button = self.create_button(_('Clean Config'), 
 
457
                gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_BUTTON),
 
458
                self.treeview.update_config_model)
 
459
        vbox.pack_start(self.config_button, False, False, 0)
 
460
 
 
461
        self.kernel_button = self.create_button(_('Clean Kernel'), 
 
462
                gtk.image_new_from_pixbuf(get_icon_with_name('start-here', 24)),
 
463
                self.treeview.update_kernel_model)
 
464
        vbox.pack_start(self.kernel_button, False, False, 0)
 
465
 
 
466
        # checkbutton
 
467
        self.select_button = gtk.CheckButton(_('Select All'))
 
468
        self.select_button.set_sensitive(False)
 
469
        self.__handler_id = self.select_button.connect('toggled', self.on_select_all)
 
470
        self.add_start(self.select_button, False, False, 0)
 
471
 
 
472
        # button
 
473
        hbox = gtk.HBox(False, 0)
 
474
        self.pack_end(hbox, False ,False, 5)
 
475
 
 
476
        un_lock = PolkitButton()
 
477
        un_lock.connect('changed', self.on_polkit_action)
 
478
        hbox.pack_end(un_lock, False, False, 5)
 
479
 
 
480
        self.clean_button = gtk.Button(stock=gtk.STOCK_CLEAR)
 
481
        set_label_for_stock_button(self.clean_button, _('_Cleanup'))
 
482
        self.clean_button.connect('clicked', self.on_clean_button_clicked)
 
483
        self.clean_button.set_sensitive(False)
 
484
        hbox.pack_end(self.clean_button, False, False, 5)
 
485
 
 
486
        self.show_all()
 
487
 
 
488
    def create_button(self, text, image, function):
 
489
        button = gtk.ToggleButton(text)
 
490
        if len(self.button_list) == 0:
 
491
            button.set_active(True)
 
492
        self.button_list.append(button)
 
493
        button.set_image(image)
 
494
        button.set_data('handler', button.connect('toggled', self.on_button_toggled))
 
495
        button.set_data('function', function)
 
496
 
 
497
        return button
 
498
 
 
499
    def on_select_all(self, widget):
 
500
        check = widget.get_active()
 
501
 
 
502
        self.treeview.select_all(check)
 
503
 
 
504
    def on_item_checked(self, widget, all):
 
505
        list = widget.get_list()
 
506
        if list:
 
507
            self.clean_button.set_sensitive(True)
 
508
        else:
 
509
            self.clean_button.set_sensitive(False)
 
510
 
 
511
        self.select_button.handler_block(self.__handler_id)
 
512
        if all:
 
513
            self.select_button.set_active(True)
 
514
        else:
 
515
            self.select_button.set_active(False)
 
516
        self.select_button.handler_unblock(self.__handler_id)
 
517
 
 
518
    def on_item_cleaned(self, widget):
 
519
        self.select_button.set_active(False)
 
520
        self.clean_button.set_sensitive(False)
 
521
 
 
522
    def on_button_toggled(self, widget):
 
523
        self.select_button.set_active(False)
 
524
 
 
525
        if self.current_button != self.button_list.index(widget):
 
526
            button = self.button_list[self.current_button]
 
527
 
 
528
            handler = button.get_data('handler')
 
529
            button.handler_block(handler)
 
530
            button.set_active(False)
 
531
            button.handler_unblock(handler)
 
532
 
 
533
            widget.set_active(True)
 
534
            self.current_button = self.button_list.index(widget)
 
535
 
 
536
            function = widget.get_data('function')
 
537
            function()
 
538
        elif self.current_button == self.button_list.index(widget):
 
539
            widget.set_active(True)
 
540
 
 
541
    def on_clean_button_clicked(self, widget):
 
542
        mode = self.treeview.mode
 
543
        if mode == 'package' or mode == 'kernel':
 
544
            self.treeview.clean_selected_package()
 
545
        elif mode == 'cache':
 
546
            self.treeview.clean_selected_cache()
 
547
        elif mode == 'config':
 
548
            self.treeview.clean_selected_config()
 
549
 
 
550
    def on_polkit_action(self, widget, action):
 
551
        global proxy
 
552
        if action:
 
553
            self.treeview.set_sensitive(True)
 
554
            self.select_button.set_sensitive(True)
 
555
            proxy = DbusProxy(PATH)
 
556
        else:
 
557
            AuthenticateFailDialog().launch()