~ubuntu-core-dev/update-manager/main

« back to all changes in this revision

Viewing changes to UpdateManager/UpdatesAvailable.py

Use uaclient library instead of calling ua tool directly

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30
30
#  USA
31
31
 
 
32
from __future__ import absolute_import, print_function
 
33
 
32
34
import gi
33
 
 
34
35
gi.require_version("Gtk", "3.0")
35
36
from gi.repository import GLib
36
37
from gi.repository import Gtk
40
41
from gi.repository import Pango
41
42
 
42
43
import warnings
43
 
 
44
 
warnings.filterwarnings(
45
 
    "ignore", "Accessed deprecated property", DeprecationWarning
46
 
)
 
44
warnings.filterwarnings("ignore", "Accessed deprecated property",
 
45
                        DeprecationWarning)
47
46
 
48
47
import apt_pkg
49
48
 
67
66
from .UnitySupport import UnitySupport
68
67
 
69
68
 
70
 
# import pdb
 
69
#import pdb
71
70
 
72
71
# FIXME:
73
72
# - kill "all_changes" and move the changes into the "Update" class
75
74
# - screen reader does not say "Downloaded" for downloaded updates
76
75
 
77
76
# list constants
78
 
(
79
 
    LIST_NAME,
80
 
    LIST_UPDATE_DATA,
81
 
    LIST_SIZE,
82
 
    LIST_TOGGLE_ACTIVE,
83
 
    LIST_SENSITIVE,
84
 
) = range(5)
 
77
(LIST_NAME, LIST_UPDATE_DATA, LIST_SIZE, LIST_TOGGLE_ACTIVE,
 
78
 LIST_SENSITIVE) = range(5)
85
79
 
86
80
# NetworkManager enums
87
81
from .Core.roam import NetworkManagerHelper
88
82
 
89
83
 
90
 
class UpdateData:
 
84
class UpdateData():
91
85
    def __init__(self, groups, group, item):
92
86
        self.groups = groups if groups else []
93
87
        self.group = group
96
90
 
97
91
class CellAreaPackage(Gtk.CellAreaBox):
98
92
    """This CellArea lays our package cells side by side, without allocating
99
 
    width for a cell if it isn't present (like icons for header labels).
100
 
    We assume that the last cell should be expanded to fill remaining space,
101
 
    and other cells have a fixed width.
 
93
       width for a cell if it isn't present (like icons for header labels).
 
94
       We assume that the last cell should be expanded to fill remaining space,
 
95
       and other cells have a fixed width.
102
96
    """
103
97
 
104
98
    def __init__(self, indent_toplevel=False):
107
101
        self.column = None
108
102
        self.cached_cell_size = {}
109
103
 
110
 
    def do_foreach_alloc(
111
 
        self, context, widget, cell_area_in, bg_area_in, callback
112
 
    ):
 
104
    def do_foreach_alloc(self, context, widget, cell_area_in, bg_area_in,
 
105
                         callback):
113
106
        cells = []
114
107
 
115
108
        def gather(cell, data):
190
183
        cell_start = self.get_cell_start(widget)
191
184
        cell_area.width = cell_area.width + cell_area.x - cell_start
192
185
        cell_area.x = cell_start
193
 
        return Gtk.CellAreaBox.do_event(
194
 
            self, context, widget, event, cell_area, flags
195
 
        )
 
186
        return Gtk.CellAreaBox.do_event(self, context, widget, event,
 
187
                                        cell_area, flags)
196
188
 
197
189
    def get_cell_start(self, widget):
198
190
        if not self.column:
216
208
class UpdatesAvailable(InternalDialog):
217
209
    APP_INSTALL_ICONS_PATH = "/usr/share/app-install/icons"
218
210
 
219
 
    def __init__(self, window_main, header=None, desc=None, need_reboot=False):
 
211
    def __init__(self, window_main, header=None, desc=None,
 
212
                 need_reboot=False):
220
213
        InternalDialog.__init__(self, window_main)
221
214
 
222
215
        self.window_main = window_main
227
220
        self.custom_desc = desc
228
221
        self.need_reboot = need_reboot
229
222
 
230
 
        content_ui_path = os.path.join(
231
 
            self.datadir, "gtkbuilder/UpdateManager.ui"
232
 
        )
 
223
        content_ui_path = os.path.join(self.datadir,
 
224
                                       "gtkbuilder/UpdateManager.ui")
233
225
        self._load_ui(content_ui_path, "pane_updates_available")
234
226
        self.set_content_widget(self.pane_updates_available)
235
227
 
251
243
 
252
244
        # setup the help viewer and disable the help button if there
253
245
        # is no viewer available
254
 
        # self.help_viewer = HelpViewer("update-manager")
255
 
        # if self.help_viewer.check() == False:
 
246
        #self.help_viewer = HelpViewer("update-manager")
 
247
        #if self.help_viewer.check() == False:
256
248
        #    self.button_help.set_sensitive(False)
257
249
 
258
250
        self.add_settings_button()
259
 
        self.button_close = self.add_button(
260
 
            Gtk.STOCK_CANCEL, self.window_main.close
261
 
        )
262
 
        self.button_install = self.add_button(
263
 
            _("Install Now"), self.on_button_install_clicked
264
 
        )
 
251
        self.button_close = self.add_button(Gtk.STOCK_CANCEL,
 
252
                                            self.window_main.close)
 
253
        self.button_install = self.add_button(_("Install Now"),
 
254
                                              self.on_button_install_clicked)
265
255
        self.focus_button = self.button_install
266
256
 
267
257
        # create text view
275
265
        self.store = Gtk.TreeStore(str, GObject.TYPE_PYOBJECT, str, bool, bool)
276
266
        self.treeview_update.set_model(None)
277
267
 
278
 
        self.image_restart.set_from_gicon(
279
 
            self.get_restart_icon(), Gtk.IconSize.BUTTON
280
 
        )
 
268
        self.image_restart.set_from_gicon(self.get_restart_icon(),
 
269
                                          Gtk.IconSize.BUTTON)
281
270
 
282
271
        restart_icon_renderer = Gtk.CellRendererPixbuf()
283
272
        restart_icon_renderer.set_property("xpad", 4)
288
277
        restart_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
289
278
        restart_column.set_fixed_width(20)
290
279
        self.treeview_update.append_column(restart_column)
291
 
        restart_column.set_cell_data_func(
292
 
            restart_icon_renderer, self.restart_icon_renderer_data_func
293
 
        )
 
280
        restart_column.set_cell_data_func(restart_icon_renderer,
 
281
                                          self.restart_icon_renderer_data_func)
294
282
 
295
283
        self.pkg_cell_area = CellAreaPackage(False)
296
284
        pkg_column = Gtk.TreeViewColumn.new_with_area(self.pkg_cell_area)
304
292
        pkg_toggle_renderer.set_property("ypad", 2)
305
293
        pkg_toggle_renderer.connect("toggled", self.on_update_toggled)
306
294
        pkg_column.pack_start(pkg_toggle_renderer, False)
307
 
        pkg_column.add_attribute(
308
 
            pkg_toggle_renderer, "active", LIST_TOGGLE_ACTIVE
309
 
        )
310
 
        pkg_column.add_attribute(
311
 
            pkg_toggle_renderer, "sensitive", LIST_SENSITIVE
312
 
        )
313
 
        pkg_column.set_cell_data_func(
314
 
            pkg_toggle_renderer, self.pkg_toggle_renderer_data_func
315
 
        )
 
295
        pkg_column.add_attribute(pkg_toggle_renderer,
 
296
                                 'active', LIST_TOGGLE_ACTIVE)
 
297
        pkg_column.add_attribute(pkg_toggle_renderer,
 
298
                                 'sensitive', LIST_SENSITIVE)
 
299
        pkg_column.set_cell_data_func(pkg_toggle_renderer,
 
300
                                      self.pkg_toggle_renderer_data_func)
316
301
 
317
302
        pkg_icon_renderer = Gtk.CellRendererPixbuf()
318
303
        pkg_icon_renderer.set_property("ypad", 2)
319
304
        pkg_icon_renderer.set_property("stock-size", Gtk.IconSize.MENU)
320
305
        pkg_column.pack_start(pkg_icon_renderer, False)
321
 
        pkg_column.set_cell_data_func(
322
 
            pkg_icon_renderer, self.pkg_icon_renderer_data_func
323
 
        )
 
306
        pkg_column.set_cell_data_func(pkg_icon_renderer,
 
307
                                      self.pkg_icon_renderer_data_func)
324
308
 
325
309
        pkg_label_renderer = Gtk.CellRendererText()
326
310
        pkg_label_renderer.set_property("ypad", 2)
327
311
        pkg_label_renderer.set_property("ellipsize", Pango.EllipsizeMode.END)
328
312
        pkg_column.pack_start(pkg_label_renderer, True)
329
 
        pkg_column.set_cell_data_func(
330
 
            pkg_label_renderer, self.pkg_label_renderer_data_func
331
 
        )
 
313
        pkg_column.set_cell_data_func(pkg_label_renderer,
 
314
                                      self.pkg_label_renderer_data_func)
332
315
 
333
316
        size_renderer = Gtk.CellRendererText()
334
317
        size_renderer.set_property("xpad", 6)
336
319
        size_renderer.set_property("xalign", 1)
337
320
        # 1.0/1.2 == PANGO.Scale.SMALL. Constant is not (yet) introspected.
338
321
        size_renderer.set_property("scale", 1.0 / 1.2)
339
 
        size_column = Gtk.TreeViewColumn(
340
 
            _("Download"), size_renderer, text=LIST_SIZE
341
 
        )
 
322
        size_column = Gtk.TreeViewColumn(_("Download"), size_renderer,
 
323
                                         text=LIST_SIZE)
342
324
        size_column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
343
 
        size_column.add_attribute(size_renderer, "sensitive", LIST_SENSITIVE)
 
325
        size_column.add_attribute(size_renderer,
 
326
                                  'sensitive', LIST_SENSITIVE)
344
327
        self.treeview_update.append_column(size_column)
345
328
 
346
329
        self.treeview_update.set_headers_visible(True)
349
332
        self.treeview_update.set_fixed_height_mode(False)
350
333
        self.treeview_update.set_expander_column(pkg_column)
351
334
        self.treeview_update.set_search_column(LIST_NAME)
352
 
        self.treeview_update.connect(
353
 
            "button-press-event", self.on_treeview_button_press
354
 
        )
 
335
        self.treeview_update.connect("button-press-event",
 
336
                                     self.on_treeview_button_press)
355
337
 
356
338
        # init show version
357
339
        self.show_versions = self.settings.get_boolean("show-versions")
358
340
        # init summary_before_name
359
341
        self.summary_before_name = self.settings.get_boolean(
360
 
            "summary-before-name"
361
 
        )
 
342
            "summary-before-name")
362
343
 
363
344
        # expander
364
345
        self.expander_details.set_expanded(
365
 
            self.settings.get_boolean("show-details")
366
 
        )
 
346
            self.settings.get_boolean("show-details"))
367
347
        self.expander_details.connect("activate", self.pre_activate_details)
368
 
        self.expander_details.connect(
369
 
            "notify::expanded", self.activate_details
370
 
        )
 
348
        self.expander_details.connect("notify::expanded",
 
349
                                      self.activate_details)
371
350
        self.expander_desc.connect("notify::expanded", self.activate_desc)
372
351
 
373
352
        # If auto-updates are on, change cancel label
374
353
        self.notifier_settings = Gio.Settings.new("com.ubuntu.update-notifier")
375
354
        self.notifier_settings.connect(
376
 
            "changed::auto-launch", lambda s, p: self.update_close_button()
377
 
        )
 
355
            "changed::auto-launch",
 
356
            lambda s, p: self.update_close_button())
378
357
        self.update_close_button()
379
358
 
380
359
        # Alert watcher
381
360
        self.alert_watcher = AlertWatcher()
382
361
        self.alert_watcher.connect("network-alert", self._on_network_alert)
383
362
        self.alert_watcher.connect("battery-alert", self._on_battery_alert)
384
 
        self.alert_watcher.connect(
385
 
            "network-3g-alert", self._on_network_3g_alert
386
 
        )
 
363
        self.alert_watcher.connect("network-3g-alert",
 
364
                                   self._on_network_3g_alert)
387
365
 
388
366
    def stop(self):
389
367
        InternalDialog.stop(self)
397
375
 
398
376
    def is_auto_update(self):
399
377
        update_days = apt_pkg.config.find_i(
400
 
            "APT::Periodic::Update-Package-Lists"
401
 
        )
 
378
            "APT::Periodic::Update-Package-Lists")
402
379
        return update_days >= 1
403
380
 
404
381
    def update_close_button(self):
418
395
    def pkg_requires_restart(self, pkg):
419
396
        if pkg is None or pkg.candidate is None:
420
397
            return False
421
 
        restart_condition = pkg.candidate.record.get("Restart-Required")
422
 
        return restart_condition == "system"
 
398
        restart_condition = pkg.candidate.record.get('Restart-Required')
 
399
        return restart_condition == 'system'
423
400
 
424
401
    def get_restart_icon(self):
425
402
        # FIXME: Non-standard, incorrect icon name (from app category).
426
403
        # Theme support for what we want seems to be lacking.
427
 
        restart_icon_names = [
428
 
            "view-refresh-symbolic",
429
 
            "system-restart",
430
 
            "system-reboot",
431
 
        ]
 
404
        restart_icon_names = ['view-refresh-symbolic',
 
405
                              'system-restart',
 
406
                              'system-reboot']
432
407
        return Gio.ThemedIcon.new_from_names(restart_icon_names)
433
408
 
434
 
    def restart_icon_renderer_data_func(
435
 
        self, cell_layout, renderer, model, iter, user_data
436
 
    ):
 
409
    def restart_icon_renderer_data_func(self, cell_layout, renderer, model,
 
410
                                        iter, user_data):
437
411
        data = model.get_value(iter, LIST_UPDATE_DATA)
438
412
        sensitive = model.get_value(iter, LIST_SENSITIVE)
439
413
        path = model.get_path(iter)
448
422
                # A package in the group requires restart
449
423
                for group_item in data.group.items:
450
424
                    if group_item.pkg and self.pkg_requires_restart(
451
 
                        group_item.pkg
452
 
                    ):
 
425
                            group_item.pkg):
453
426
                        requires_restart = True
454
427
                        break
455
428
 
459
432
            gicon = None
460
433
        renderer.set_property("gicon", gicon)
461
434
 
462
 
    def pkg_toggle_renderer_data_func(
463
 
        self, cell_layout, renderer, model, iter, user_data
464
 
    ):
 
435
    def pkg_toggle_renderer_data_func(self, cell_layout, renderer, model,
 
436
                                      iter, user_data):
465
437
        data = model.get_value(iter, LIST_UPDATE_DATA)
466
438
 
467
439
        activatable = False
492
464
        renderer.set_property("activatable", activatable)
493
465
        renderer.set_property("inconsistent", inconsistent)
494
466
 
495
 
    def pkg_icon_renderer_data_func(
496
 
        self, cell_layout, renderer, model, iter, user_data
497
 
    ):
 
467
    def pkg_icon_renderer_data_func(self, cell_layout, renderer, model,
 
468
                                    iter, user_data):
498
469
        data = model.get_value(iter, LIST_UPDATE_DATA)
499
470
 
500
471
        gicon = None
507
478
 
508
479
    def get_app_install_icon(self, icon):
509
480
        """Any application icon is coming from app-install-data's desktop
510
 
        files, which refer to icons from app-install-data's icon directory.
511
 
        So we look them up here."""
 
481
           files, which refer to icons from app-install-data's icon directory.
 
482
           So we look them up here."""
512
483
 
513
484
        if not isinstance(icon, Gio.ThemedIcon):
514
485
            return icon  # shouldn't happen
515
486
 
516
 
        info = self.app_icons.choose_icon(
517
 
            icon.get_names(), 16, Gtk.IconLookupFlags.FORCE_SIZE
518
 
        )
 
487
        info = self.app_icons.choose_icon(icon.get_names(), 16,
 
488
                                          Gtk.IconLookupFlags.FORCE_SIZE)
519
489
        if info is not None:
520
490
            return Gio.FileIcon.new(Gio.File.new_for_path(info.get_filename()))
521
491
        else:
522
492
            return icon  # Assume it's in one of the user's themes
523
493
 
524
 
    def pkg_label_renderer_data_func(
525
 
        self, cell_layout, renderer, model, iter, user_data
526
 
    ):
 
494
    def pkg_label_renderer_data_func(self, cell_layout, renderer, model,
 
495
                                     iter, user_data):
527
496
        data = model.get_value(iter, LIST_UPDATE_DATA)
528
497
        sensitive = model.get_value(iter, LIST_SENSITIVE)
529
498
        name = GLib.markup_escape_text(model.get_value(iter, LIST_NAME))
549
518
        for line in lines:
550
519
            end_iter = changes_buffer.get_end_iter()
551
520
            version_match = re.match(
552
 
                r"^%s \((.*)\)(.*)\;.*$" % re.escape(srcpkg), line
553
 
            )
554
 
            # bullet_match = re.match("^.*[\*-]", line)
 
521
                r'^%s \((.*)\)(.*)\;.*$' % re.escape(srcpkg), line)
 
522
            #bullet_match = re.match("^.*[\*-]", line)
555
523
            author_match = re.match("^.*--.*<.*@.*>.*$", line)
556
524
            if version_match:
557
525
                version = version_match.group(1)
558
 
                # upload_archive = version_match.group(2).strip()
 
526
                #upload_archive = version_match.group(2).strip()
559
527
                version_text = _("Version %s: \n") % version
560
 
                changes_buffer.insert_with_tags_by_name(
561
 
                    end_iter, version_text, "versiontag"
562
 
                )
563
 
            elif author_match:
 
528
                changes_buffer.insert_with_tags_by_name(end_iter, version_text,
 
529
                                                        "versiontag")
 
530
            elif (author_match):
564
531
                pass
565
532
            else:
566
533
                changes_buffer.insert(end_iter, line + "\n")
576
543
        # set descr
577
544
        data = model.get_value(iter, LIST_UPDATE_DATA)
578
545
        item = data.item
579
 
        if (
580
 
            item is None
581
 
            and data.group is not None
582
 
            and data.group.core_item is not None
583
 
        ):
 
546
        if (item is None and data.group is not None
 
547
                and data.group.core_item is not None):
584
548
            item = data.group.core_item
585
 
        if (
586
 
            item is None
587
 
            or item.pkg is None
588
 
            or item.pkg.candidate is None
589
 
            or item.pkg.candidate.description is None
590
 
        ):
 
549
        if (item is None or item.pkg is None
 
550
                or item.pkg.candidate is None
 
551
                or item.pkg.candidate.description is None):
591
552
            changes_buffer = self.textview_changes.get_buffer()
592
553
            changes_buffer.set_text("")
593
554
            desc_buffer = self.textview_descr.get_buffer()
598
559
        self.notebook_details.set_sensitive(True)
599
560
        # do some regular expression magic on the description
600
561
        # Add a newline before each bullet
601
 
        p = re.compile(r"^(\s|\t)*(\*|0|-)", re.MULTILINE)
602
 
        long_desc = p.sub("\n*", long_desc)
 
562
        p = re.compile(r'^(\s|\t)*(\*|0|-)', re.MULTILINE)
 
563
        long_desc = p.sub('\n*', long_desc)
603
564
        # replace all newlines by spaces
604
 
        p = re.compile(r"\n", re.MULTILINE)
 
565
        p = re.compile(r'\n', re.MULTILINE)
605
566
        long_desc = p.sub(" ", long_desc)
606
567
        # replace all multiple spaces by newlines
607
 
        p = re.compile(r"\s\s+", re.MULTILINE)
 
568
        p = re.compile(r'\s\s+', re.MULTILINE)
608
569
        long_desc = p.sub("\n", long_desc)
609
 
        long_desc = "Package: %s\n%s" % (item.pkg.name, long_desc)
 
570
        long_desc = ("Package: %s\n%s" %
 
571
                     (item.pkg.name, long_desc))
610
572
 
611
573
        desc_buffer = self.textview_descr.get_buffer()
612
574
        desc_buffer.set_text(long_desc)
627
589
        # if not connected, do not even attempt to get the changes
628
590
        elif not self.connected:
629
591
            changes_buffer.set_text(
630
 
                _(
631
 
                    "No network connection detected, you can not download "
632
 
                    "changelog information."
633
 
                )
634
 
            )
 
592
                _("No network connection detected, you can not download "
 
593
                  "changelog information."))
635
594
        # else, get it from the entwork
636
595
        elif self.expander_details.get_expanded():
637
596
            lock = threading.Lock()
638
597
            lock.acquire()
639
598
            changelog_thread = threading.Thread(
640
 
                target=self.cache.get_news_and_changelog, args=(name, lock)
641
 
            )
 
599
                target=self.cache.get_news_and_changelog, args=(name, lock))
642
600
            changelog_thread.start()
643
 
            changes_buffer.set_text(
644
 
                "%s\n" % _("Downloading list of changes...")
645
 
            )
 
601
            changes_buffer.set_text("%s\n" %
 
602
                                    _("Downloading list of changes..."))
646
603
            iter = changes_buffer.get_iter_at_line(1)
647
604
            anchor = changes_buffer.create_child_anchor(iter)
648
605
            button = Gtk.Button(stock="gtk-cancel")
649
606
            self.textview_changes.add_child_at_anchor(button, anchor)
650
607
            button.show()
651
 
            id = button.connect(
652
 
                "clicked", lambda w, lock: lock.release(), lock
653
 
            )
 
608
            id = button.connect("clicked",
 
609
                                lambda w, lock: lock.release(), lock)
654
610
            # wait for the dl-thread
655
611
            while lock.locked():
656
612
                time.sleep(0.01)
686
642
            # deleted when it goes out of scope and no menu is visible
687
643
            # (bug #806949)
688
644
            self.menu = menu = Gtk.Menu()
689
 
            item_select_none = Gtk.MenuItem.new_with_mnemonic(
690
 
                _("_Deselect All")
691
 
            )
 
645
            item_select_none = \
 
646
                Gtk.MenuItem.new_with_mnemonic(_("_Deselect All"))
692
647
            item_select_none.connect("activate", self.select_none_upgrades)
693
648
            menu.append(item_select_none)
694
649
            num_updates = self.cache.install_count + self.cache.del_count
699
654
            menu.append(item_select_all)
700
655
            menu.show_all()
701
656
            menu.popup_for_device(
702
 
                None, None, None, None, None, event.button, event.time
703
 
            )
 
657
                None, None, None, None, None, event.button, event.time)
704
658
            menu.show()
705
659
            return True
706
660
 
707
661
    # we need this for select all/unselect all
708
662
    def _toggle_group_headers(self, new_selection_value):
709
 
        """small helper that will set/unset the group headers"""
 
663
        """ small helper that will set/unset the group headers
 
664
        """
710
665
        model = self.treeview_update.get_model()
711
666
        for row in model:
712
667
            data = model.get_value(row.iter, LIST_UPDATE_DATA)
713
668
            if data.groups is not None or data.group is not None:
714
 
                model.set_value(
715
 
                    row.iter, LIST_TOGGLE_ACTIVE, new_selection_value
716
 
                )
 
669
                model.set_value(row.iter, LIST_TOGGLE_ACTIVE,
 
670
                                new_selection_value)
717
671
 
718
672
    def select_all_upgrades(self, widget):
719
673
        """
738
692
        self.setBusy(False)
739
693
 
740
694
    def setBusy(self, flag):
741
 
        """Show a watch cursor if the app is busy for more than 0.3 sec.
742
 
        Furthermore provide a loop to handle user interface events"""
 
695
        """ Show a watch cursor if the app is busy for more than 0.3 sec.
 
696
        Furthermore provide a loop to handle user interface events """
743
697
        if self.window_main.get_window() is None:
744
698
            return
745
699
        if flag:
746
700
            self.window_main.get_window().set_cursor(
747
 
                Gdk.Cursor.new(Gdk.CursorType.WATCH)
748
 
            )
 
701
                Gdk.Cursor.new(Gdk.CursorType.WATCH))
749
702
        else:
750
703
            self.window_main.get_window().set_cursor(None)
751
704
        while Gtk.events_pending():
762
715
            elif data.groups:
763
716
                active = any([g.packages_are_selected() for g in data.groups])
764
717
            model.set_value(iter, LIST_TOGGLE_ACTIVE, active)
765
 
 
766
718
        self.store.foreach(foreach_cb, None)
767
719
 
768
720
    def _check_for_required_restart(self):
786
738
        self.hbox_restart.set_visible(requires_restart)
787
739
 
788
740
    def _refresh_updates_count(self):
789
 
        self.button_install.set_sensitive(
790
 
            self.cache.install_count + self.cache.del_count
791
 
        )
 
741
        self.button_install.set_sensitive(self.cache.install_count
 
742
                                          + self.cache.del_count)
792
743
        try:
793
744
            inst_count = self.cache.install_count + self.cache.del_count
794
745
            self.dl_size = self.cache.required_download
795
746
            download_str = ""
796
747
            if self.dl_size != 0:
797
748
                download_str = _("%s will be downloaded.") % (
798
 
                    humanize_size(self.dl_size)
799
 
                )
 
749
                    humanize_size(self.dl_size))
800
750
                self.image_downsize.set_sensitive(True)
801
751
                # do not set the buttons to sensitive/insensitive until NM
802
752
                # can deal with dialup connections properly
803
 
                # if self.alert_watcher.network_state != NM_STATE_CONNECTED:
 
753
                #if self.alert_watcher.network_state != NM_STATE_CONNECTED:
804
754
                #    self.button_install.set_sensitive(False)
805
 
                # else:
 
755
                #else:
806
756
                #    self.button_install.set_sensitive(True)
807
757
                self.button_install.set_sensitive(True)
808
758
                self.unity.set_install_menuitem_visible(True)
811
761
                    download_str = ngettext(
812
762
                        "The update has already been downloaded.",
813
763
                        "The updates have already been downloaded.",
814
 
                        inst_count,
815
 
                    )
 
764
                        inst_count)
816
765
                    self.button_install.set_sensitive(True)
817
766
                    self.unity.set_install_menuitem_visible(True)
818
767
                else:
837
786
 
838
787
    def update_count(self):
839
788
        """activate or disable widgets and show dialog texts corresponding to
840
 
        the number of available updates"""
 
789
           the number of available updates"""
841
790
        self.updates_changed()
842
791
 
843
792
        text_header = None
852
801
        elif self.settings.get_boolean("first-run"):
853
802
            flavor = self.window_main.meta_release.flavor_name
854
803
            version = self.window_main.meta_release.current_dist_version
855
 
            text_header = _(
856
 
                "Updated software has been issued since %s %s "
857
 
                "was released. Do you want to install it now?"
858
 
            ) % (flavor, version)
 
804
            text_header = _("Updated software has been issued since %s %s "
 
805
                            "was released. Do you want to install "
 
806
                            "it now?") % (flavor, version)
859
807
            self.settings.set_boolean("first-run", False)
860
808
        else:
861
 
            text_header = _(
862
 
                "Updated software is available for this "
863
 
                "computer. Do you want to install it now?"
864
 
            )
 
809
            text_header = _("Updated software is available for this "
 
810
                            "computer. Do you want to install it now?")
865
811
            if not self.hbox_restart.get_visible() and self.need_reboot:
866
 
                text_desc = _(
867
 
                    "The computer also needs to restart "
868
 
                    "to finish installing previous updates."
869
 
                )
 
812
                text_desc = _("The computer also needs to restart "
 
813
                              "to finish installing previous updates.")
870
814
 
871
815
        self.notebook_details.set_sensitive(True)
872
816
        self.treeview_update.set_sensitive(True)
896
840
        self.unity.set_install_menuitem_visible(False)
897
841
        # print("on_button_install_clicked")
898
842
        err_sum = _("Not enough free disk space")
899
 
        err_msg = _(
900
 
            "The upgrade needs a total of %s free space on disk '%s'. "
901
 
            "Please free at least an additional %s of disk space on '%s'. %s"
902
 
        )
 
843
        err_msg = _("The upgrade needs a total of %s free space on "
 
844
                    "disk '%s'. "
 
845
                    "Please free at least an additional %s of disk "
 
846
                    "space on '%s'. %s")
903
847
        # specific ways to resolve lack of free space
904
 
        remedy_archivedir = _(
905
 
            "Remove temporary packages of former "
906
 
            "installations using 'sudo apt clean'."
907
 
        )
908
 
        remedy_boot = _(
909
 
            "You can remove old kernels using 'sudo apt autoremove', and you "
910
 
            "could also set COMPRESS=xz in /etc/initramfs-tools/initramfs.conf"
911
 
            " to reduce the size of your initramfs."
912
 
        )
913
 
        remedy_root = _(
914
 
            "Empty your trash and remove temporary packages "
915
 
            "of former installations using 'sudo apt clean'."
916
 
        )
 
848
        remedy_archivedir = _("Remove temporary packages of former "
 
849
                              "installations using 'sudo apt clean'.")
 
850
        remedy_boot = _("You can remove old kernels using "
 
851
                        "'sudo apt autoremove', and you could also "
 
852
                        "set COMPRESS=xz in "
 
853
                        "/etc/initramfs-tools/initramfs.conf to "
 
854
                        "reduce the size of your initramfs.")
 
855
        remedy_root = _("Empty your trash and remove temporary "
 
856
                        "packages of former installations using "
 
857
                        "'sudo apt clean'.")
917
858
        remedy_tmp = _("Reboot to clean up files in /tmp.")
918
859
        remedy_usr = _("")
919
860
        # check free space and error if its not enough
927
868
                if err_long != "":
928
869
                    err_long += " "
929
870
                if req.dir == archivedir:
930
 
                    err_long += err_msg % (
931
 
                        req.size_total,
932
 
                        req.dir,
933
 
                        req.size_needed,
934
 
                        req.dir,
935
 
                        remedy_archivedir,
936
 
                    )
 
871
                    err_long += err_msg % (req.size_total, req.dir,
 
872
                                           req.size_needed, req.dir,
 
873
                                           remedy_archivedir)
937
874
                elif req.dir == "/boot":
938
 
                    err_long += err_msg % (
939
 
                        req.size_total,
940
 
                        req.dir,
941
 
                        req.size_needed,
942
 
                        req.dir,
943
 
                        remedy_boot,
944
 
                    )
 
875
                    err_long += err_msg % (req.size_total, req.dir,
 
876
                                           req.size_needed, req.dir,
 
877
                                           remedy_boot)
945
878
                elif req.dir == "/":
946
 
                    err_long += err_msg % (
947
 
                        req.size_total,
948
 
                        req.dir,
949
 
                        req.size_needed,
950
 
                        req.dir,
951
 
                        remedy_root,
952
 
                    )
 
879
                    err_long += err_msg % (req.size_total, req.dir,
 
880
                                           req.size_needed, req.dir,
 
881
                                           remedy_root)
953
882
                elif req.dir == "/tmp":
954
 
                    err_long += err_msg % (
955
 
                        req.size_total,
956
 
                        req.dir,
957
 
                        req.size_needed,
958
 
                        req.dir,
959
 
                        remedy_tmp,
960
 
                    )
 
883
                    err_long += err_msg % (req.size_total, req.dir,
 
884
                                           req.size_needed, req.dir,
 
885
                                           remedy_tmp)
961
886
                elif req.dir == "/usr":
962
 
                    err_long += err_msg % (
963
 
                        req.size_total,
964
 
                        req.dir,
965
 
                        req.size_needed,
966
 
                        req.dir,
967
 
                        remedy_usr,
968
 
                    )
 
887
                    err_long += err_msg % (req.size_total, req.dir,
 
888
                                           req.size_needed, req.dir,
 
889
                                           remedy_usr)
969
890
            self.window_main.start_error(False, err_sum, err_long)
970
891
            return
971
892
        except SystemError:
982
903
            self.vbox_alerts.show()
983
904
            self.connected = False
984
905
        # in doubt (STATE_UNKNOWN), assume connected
985
 
        elif (
986
 
            state in NetworkManagerHelper.NM_STATE_CONNECTED_LIST
987
 
            or state == NetworkManagerHelper.NM_STATE_UNKNOWN
988
 
        ):
 
906
        elif (state in NetworkManagerHelper.NM_STATE_CONNECTED_LIST
 
907
              or state == NetworkManagerHelper.NM_STATE_UNKNOWN):
989
908
            self.updates_changed()
990
909
            self.hbox_offline.hide()
991
910
            self.connected = True
994
913
            self.on_treeview_update_cursor_changed(self.treeview_update)
995
914
        else:
996
915
            self.connected = False
997
 
            self.label_offline.set_text(
998
 
                _(
999
 
                    "You may not be able to check for "
1000
 
                    "updates or download new updates."
1001
 
                )
1002
 
            )
 
916
            self.label_offline.set_text(_("You may not be able to check for "
 
917
                                          "updates or download new updates."))
1003
918
            self.updates_changed()
1004
919
            self.hbox_offline.show()
1005
920
            self.vbox_alerts.show()
1012
927
            self.hbox_battery.hide()
1013
928
 
1014
929
    def _on_network_3g_alert(self, watcher, on_3g, is_roaming):
1015
 
        # print("on 3g: %s; roaming: %s" % (on_3g, is_roaming))
 
930
        #print("on 3g: %s; roaming: %s" % (on_3g, is_roaming))
1016
931
        if is_roaming:
1017
932
            self.hbox_roaming.show()
1018
933
            self.hbox_on_3g.hide()
1024
939
            self.hbox_roaming.hide()
1025
940
 
1026
941
    def on_update_toggled(self, renderer, path):
1027
 
        """a toggle button in the listview was toggled"""
 
942
        """ a toggle button in the listview was toggled """
1028
943
        iter = self.store.get_iter(path)
1029
944
        data = self.store.get_value(iter, LIST_UPDATE_DATA)
1030
945
        # make sure that we don't allow to toggle deactivated updates
1031
946
        # this is needed for the call by the row activation callback
1032
947
        if data.groups:
1033
 
            self.toggle_from_items(
1034
 
                [item for group in data.groups for item in group.items]
1035
 
            )
 
948
            self.toggle_from_items([item for group in data.groups
 
949
                                    for item in group.items])
1036
950
        elif data.group:
1037
951
            self.toggle_from_items(data.group.items)
1038
952
        else:
1073
987
        self.setBusy(False)
1074
988
 
1075
989
    def _save_state(self):
1076
 
        """save the state  (window-size for now)"""
 
990
        """ save the state  (window-size for now) """
1077
991
        if self.expander_details.get_expanded():
1078
992
            (w, h) = self.window_main.get_size()
1079
993
            self.settings.set_int("window-width", w)
1080
994
            self.settings.set_int("window-height", h)
1081
995
 
1082
996
    def _restore_state(self):
1083
 
        """restore the state (window-size for now)"""
 
997
        """ restore the state (window-size for now) """
1084
998
        w = self.settings.get_int("window-width")
1085
999
        h = self.settings.get_int("window-height")
1086
1000
        expanded = self.expander_details.get_expanded()
1090
1004
            self.window_main.end_user_resizable()
1091
1005
        return False
1092
1006
 
1093
 
    def _add_header(self, name, groups, sensitive=True):
 
1007
    def _add_header(self, name, groups):
1094
1008
        total_size = 0
1095
1009
        for group in groups:
1096
1010
            total_size = total_size + group.get_total_size()
1099
1013
            UpdateData(groups, None, None),
1100
1014
            humanize_size(total_size),
1101
1015
            True,
1102
 
            sensitive,
 
1016
            True
1103
1017
        ]
1104
1018
        return self.store.append(None, header_row)
1105
1019
 
1114
1028
                continue
1115
1029
 
1116
1030
            group_is_item = None
1117
 
            if (
1118
 
                not isinstance(group, UpdateSystemGroup)
1119
 
                and len(group.items) == 1
1120
 
            ):
 
1031
            if not isinstance(group, UpdateSystemGroup) and \
 
1032
                    len(group.items) == 1:
1121
1033
                group_is_item = group.items[0]
1122
1034
 
1123
1035
            group_row = [
1125
1037
                UpdateData(None, group, group_is_item),
1126
1038
                humanize_size(group.get_total_size()),
1127
1039
                True,
1128
 
                group.sensitive,
 
1040
                group.sensitive
1129
1041
            ]
1130
1042
            group_iter = self.store.append(None, group_row)
1131
1043
 
1137
1049
                    UpdateData(None, None, item),
1138
1050
                    humanize_size(getattr(item.pkg.candidate, "size", 0)),
1139
1051
                    True,
1140
 
                    group.sensitive,
 
1052
                    group.sensitive
1141
1053
                ]
1142
1054
                self.store.append(group_iter, item_row)
1143
1055
 
1156
1068
 
1157
1069
        # add security and update groups to self.store
1158
1070
        if self.list.oem_groups:
1159
 
            self._add_header(
1160
 
                _("Improved hardware support"), self.list.oem_groups
1161
 
            )
 
1071
            self._add_header(_("Improved hardware support"),
 
1072
                             self.list.oem_groups)
1162
1073
            self._add_groups(self.list.oem_groups)
1163
1074
        if self.list.security_groups:
1164
1075
            self._add_header(_("Security updates"), self.list.security_groups)
1165
1076
            self._add_groups(self.list.security_groups)
1166
 
        if (
1167
 
            self.list.security_groups or self.list.oem_groups
1168
 
        ) and self.list.update_groups:
 
1077
        if ((self.list.security_groups or self.list.oem_groups)
 
1078
                and self.list.update_groups):
1169
1079
            self._add_header(_("Other updates"), self.list.update_groups)
1170
 
        elif self.list.update_groups and (
1171
 
            self.list.kernel_autoremove_groups or self.list.duplicate_groups
1172
 
        ):
 
1080
        elif self.list.update_groups and (self.list.kernel_autoremove_groups
 
1081
                                          or self.list.duplicate_groups):
1173
1082
            self._add_header(_("Updates"), self.list.update_groups)
1174
1083
        if self.list.update_groups:
1175
1084
            self._add_groups(self.list.update_groups)
1176
1085
        if self.list.kernel_autoremove_groups:
1177
1086
            self._add_header(
1178
1087
                _("Unused kernel updates to be removed"),
1179
 
                self.list.kernel_autoremove_groups,
1180
 
            )
 
1088
                self.list.kernel_autoremove_groups)
1181
1089
            self._add_groups(self.list.kernel_autoremove_groups)
1182
1090
        if self.list.duplicate_groups:
1183
1091
            self._add_header(
1184
1092
                _("Duplicate packages to be removed"),
1185
 
                self.list.duplicate_groups,
1186
 
            )
 
1093
                self.list.duplicate_groups)
1187
1094
            self._add_groups(self.list.duplicate_groups)
1188
1095
        if self.list.ubuntu_pro_groups:
1189
 
            self._add_header(
1190
 
                _("Ubuntu Pro (enable in Settings…)"),
1191
 
                self.list.ubuntu_pro_groups,
1192
 
                sensitive=False,
1193
 
            )
1194
1096
            self._add_groups(self.list.ubuntu_pro_groups)
1195
1097
 
1196
1098
        self.treeview_update.set_model(self.store)
1197
1099
        self.pkg_cell_area.indent_toplevel = (
1198
1100
            bool(self.list.security_groups)
1199
1101
            or bool(self.list.kernel_autoremove_groups)
1200
 
            or bool(self.list.duplicate_groups)
1201
 
        )
 
1102
            or bool(self.list.duplicate_groups))
1202
1103
        self.update_close_button()
1203
1104
        self.update_count()
1204
1105
        self.setBusy(False)