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

2428.3.1 by Michael Terry
bring some of AptDialog from aptdaemon into our code to have more control over its presentation (i.e. stuff the widgets inside of our own main window; split UpdateManager into a central point of control and put old widgets into UpdatesAvailable class
1
# UpdatesAvailable.py
2511.1.12 by Michael Terry
last of the pep8 error fixes
2
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
2511.1.14 by Michael Terry
auto-pep8'd some warnings
3
#
2428.5.60 by Michael Terry
add myself to UpdateList authors and update copyright in UpdateList and UpdatesAvailable
4
#  Copyright (c) 2004-2013 Canonical
52 by Michael Vogt
* added a bunch of missing copyright notices in the files
5
#                2004 Michiel Sikkes
6
#                2005 Martin Willemoes Hansen
1875.1.2 by Mohamed Amine IL Idrissi
Finished implementing the feature.
7
#                2010 Mohamed Amine IL Idrissi
2511.1.14 by Michael Terry
auto-pep8'd some warnings
8
#
52 by Michael Vogt
* added a bunch of missing copyright notices in the files
9
#  Author: Michiel Sikkes <michiel@eyesopened.nl>
10
#          Michael Vogt <mvo@debian.org>
11
#          Martin Willemoes Hansen <mwh@sysrq.dk>
1875.1.2 by Mohamed Amine IL Idrissi
Finished implementing the feature.
12
#          Mohamed Amine IL Idrissi <ilidrissiamine@gmail.com>
1988.1.4 by Alex Launi
Update UpdateManager.py header to add Alex Launi to authors, and update copyright
13
#          Alex Launi <alex.launi@canonical.com>
2428.2.9 by Michael Terry
update author/copyright info
14
#          Michael Terry <michael.terry@canonical.com>
2428.5.57 by Dylan McCall
Adding myself to Authors list for UpdateList and UpdatesAvailable.
15
#          Dylan McCall <dylanmccall@ubuntu.com>
2511.1.14 by Michael Terry
auto-pep8'd some warnings
16
#
17
#  This program is free software; you can redistribute it and/or
18
#  modify it under the terms of the GNU General Public License as
52 by Michael Vogt
* added a bunch of missing copyright notices in the files
19
#  published by the Free Software Foundation; either version 2 of the
20
#  License, or (at your option) any later version.
2511.1.14 by Michael Terry
auto-pep8'd some warnings
21
#
52 by Michael Vogt
* added a bunch of missing copyright notices in the files
22
#  This program is distributed in the hope that it will be useful,
23
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
24
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
#  GNU General Public License for more details.
2511.1.14 by Michael Terry
auto-pep8'd some warnings
26
#
52 by Michael Vogt
* added a bunch of missing copyright notices in the files
27
#  You should have received a copy of the GNU General Public License
28
#  along with this program; if not, write to the Free Software
29
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30
#  USA
31
2409 by Colin Watson
Tell Python to use absolute imports by default, and annotate cases where
32
from __future__ import absolute_import, print_function
2394 by Colin Watson
Use Python 3-style print functions.
33
2741.1.1 by Jeremy Bicha
Add gi.require_version in a few more places to reduce the annoying warnings
34
import gi
35
gi.require_version("Gtk", "3.0")
2428.5.30 by Michael Terry
Set minimum and default heights for the expanded details window, based on spec recommendations of 3 rows and 6 rows respectively
36
from gi.repository import GLib
2146.2.12 by Michael Vogt
get rid of gconfclient references
37
from gi.repository import Gtk
38
from gi.repository import Gdk
2119.1.1 by Robert Roth
Porting to PyGI
39
from gi.repository import GObject
2146.2.12 by Michael Vogt
get rid of gconfclient references
40
from gi.repository import Gio
2119.1.2 by Robert Roth
Porting to PyGI
41
from gi.repository import Pango
1170 by Michael Vogt
* UpdateManager/Common/MyCache.py,
42
358.1.79 by Michael Vogt
* DistUpgrade/DistUpgradeCache.py:
43
import warnings
2511.1.12 by Michael Terry
last of the pep8 error fixes
44
warnings.filterwarnings("ignore", "Accessed deprecated property",
45
                        DeprecationWarning)
2164 by Michael Vogt
make UpdateManager/, tests/ pyflakes clean
46
10 by Michael Vogt
* code cleanup, make it all more structured
47
import apt_pkg
358.1.62 by Michael Vogt
* DistUpgrade/DistUpgradeCache.py, UpdateManager/UpdateManager.py:
48
10 by Michael Vogt
* code cleanup, make it all more structured
49
import os
50
import re
1571.1.13 by Michael Vogt
* UpdateManager/UpdateManager.py:
51
import logging
33 by Michael Vogt
* UpdateManager/UpdateManager.py: (re)added missing import time
52
import time
2407 by Colin Watson
Use the threading module instead of thread (renamed to _thread in Python
53
import threading
18 by Michael Vogt
* dist-upgade tool can fetch the update tarball now
54
10 by Michael Vogt
* code cleanup, make it all more structured
55
from gettext import gettext as _
1026 by Michael Vogt
* UpdateManager/UpdateManager.py:
56
from gettext import ngettext
10 by Michael Vogt
* code cleanup, make it all more structured
57
2596 by Colin Watson
Fix pyflakes failures.
58
from .Core.utils import humanize_size
2409 by Colin Watson
Tell Python to use absolute imports by default, and annotate cases where
59
from .Core.AlertWatcher import AlertWatcher
2428.5.28 by Michael Terry
always show 'Ubuntu base' even if it only has one update and some other small cleanups
60
from .Core.UpdateList import UpdateSystemGroup
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
61
from .Dialogs import InternalDialog
2167 by Michael Vogt
more pyflakes fixes, mostly good now
62
2164 by Michael Vogt
make UpdateManager/, tests/ pyflakes clean
63
from DistUpgrade.DistUpgradeCache import NotEnoughFreeSpaceError
2167 by Michael Vogt
more pyflakes fixes, mostly good now
64
2409 by Colin Watson
Tell Python to use absolute imports by default, and annotate cases where
65
from .ChangelogViewer import ChangelogViewer
66
from .UnitySupport import UnitySupport
14 by Michael Vogt
* move the MetaRelease code into it's own class/file
67
2093.1.4 by Bilal Akhtar
Move dbusmenu and Unity declaration to the top of script
68
337.4.5 by glatzor at ubuntu
* support unkown origins with the UpdateOrigin class
69
#import pdb
70
10 by Michael Vogt
* code cleanup, make it all more structured
71
# FIXME:
72
# - kill "all_changes" and move the changes into the "Update" class
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
73
# - screen reader does not read update toggle state
74
# - screen reader does not say "Downloaded" for downloaded updates
10 by Michael Vogt
* code cleanup, make it all more structured
75
76
# list constants
2953 by Steve Langasek
Fix autopkgtest regressions.
77
(LIST_NAME, LIST_UPDATE_DATA, LIST_SIZE, LIST_TOGGLE_ACTIVE,
78
 LIST_SENSITIVE) = range(5)
10 by Michael Vogt
* code cleanup, make it all more structured
79
1875.1.9 by Mohamed Amine IL Idrissi
Enums. I like enums.
80
# NetworkManager enums
2409 by Colin Watson
Tell Python to use absolute imports by default, and annotate cases where
81
from .Core.roam import NetworkManagerHelper
1875.1.9 by Mohamed Amine IL Idrissi
Enums. I like enums.
82
2511.1.12 by Michael Terry
last of the pep8 error fixes
83
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
84
class UpdateData():
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
85
    def __init__(self, groups, group, item):
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
86
        self.groups = groups if groups else []
87
        self.group = group
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
88
        self.item = item
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
89
90
2428.5.32 by Michael Terry
use custom Gtk.CellArea to layout package cells more like the spec has it
91
class CellAreaPackage(Gtk.CellAreaBox):
92
    """This CellArea lays our package cells side by side, without allocating
93
       width for a cell if it isn't present (like icons for header labels).
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
94
       We assume that the last cell should be expanded to fill remaining space,
95
       and other cells have a fixed width.
2428.5.32 by Michael Terry
use custom Gtk.CellArea to layout package cells more like the spec has it
96
    """
97
98
    def __init__(self, indent_toplevel=False):
99
        Gtk.CellAreaBox.__init__(self)
100
        self.indent_toplevel = indent_toplevel
2428.5.34 by Michael Terry
make the toggle icons reliably clickable
101
        self.column = None
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
102
        self.cached_cell_size = {}
2428.5.32 by Michael Terry
use custom Gtk.CellArea to layout package cells more like the spec has it
103
2428.5.34 by Michael Terry
make the toggle icons reliably clickable
104
    def do_foreach_alloc(self, context, widget, cell_area_in, bg_area_in,
105
                         callback):
2428.5.32 by Michael Terry
use custom Gtk.CellArea to layout package cells more like the spec has it
106
        cells = []
2595 by Colin Watson
Fix PEP-8 failures.
107
2428.5.32 by Michael Terry
use custom Gtk.CellArea to layout package cells more like the spec has it
108
        def gather(cell, data):
109
            cells.append(cell)
2595 by Colin Watson
Fix PEP-8 failures.
110
2428.5.32 by Michael Terry
use custom Gtk.CellArea to layout package cells more like the spec has it
111
        self.foreach(gather, None)
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
112
113
        cell_is_hidden = {}
114
115
        # Record the space required by each cell
116
        for cell_number, cell in enumerate(cells):
117
            # Detect if this cell should be allocated space
118
            if isinstance(cell, Gtk.CellRendererPixbuf):
119
                gicon = cell.get_property("gicon")
120
                hide_cell = gicon is None
121
            else:
122
                hide_cell = False
123
            cell_is_hidden[cell_number] = hide_cell
124
2587.1.1 by Barry Warsaw
Refactor CellAreaPackage class for ease of future changes.
125
            if not hide_cell and cell_number not in self.cached_cell_size:
2586.1.2 by Dylan McCall
Fixed spacing and misaligned security group headers in CellAreaPackage.
126
                min_size, natural_size = cell.get_preferred_width(widget)
127
                self.cached_cell_size[cell_number] = natural_size
128
2428.5.34 by Michael Terry
make the toggle icons reliably clickable
129
        cell_area = cell_area_in.copy()
130
        bg_area = bg_area_in.copy()
2428.5.32 by Michael Terry
use custom Gtk.CellArea to layout package cells more like the spec has it
131
        spacing = self.get_property("spacing")
2428.5.34 by Michael Terry
make the toggle icons reliably clickable
132
        cell_start = self.get_cell_start(widget)
2428.5.47 by Michael Terry
fix full width of column not being used for package labels
133
        orig_end = cell_area.width + cell_area.x
2428.5.34 by Michael Terry
make the toggle icons reliably clickable
134
        cur_path = self.get_current_path_string()
135
        depth = Gtk.TreePath.new_from_string(cur_path).get_depth()
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
136
137
        # And finally, start handling each cell
138
        extra_cell_width = 0
139
        cell_area.x = cell_start
140
        cell_area.width = 0
141
2587.1.1 by Barry Warsaw
Refactor CellAreaPackage class for ease of future changes.
142
        last_cell_number = len(cells) - 1
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
143
        for cell_number, cell in enumerate(cells):
2587.1.1 by Barry Warsaw
Refactor CellAreaPackage class for ease of future changes.
144
            is_last_cell = cell_number == last_cell_number
145
            cell_size = self.cached_cell_size.get(cell_number, 0)
2586.1.2 by Dylan McCall
Fixed spacing and misaligned security group headers in CellAreaPackage.
146
2587.1.1 by Barry Warsaw
Refactor CellAreaPackage class for ease of future changes.
147
            if cell_area.width > 0 and extra_cell_width == 0:
2586.1.2 by Dylan McCall
Fixed spacing and misaligned security group headers in CellAreaPackage.
148
                cell_area.x += cell_area.width + spacing
149
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
150
            if cell_number == 0:
151
                # The first cell is affected by its depth in the tree
2586.1.2 by Dylan McCall
Fixed spacing and misaligned security group headers in CellAreaPackage.
152
                if not cell_is_hidden[1] and self.indent_toplevel:
153
                    # if not a header, align with header rows
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
154
                    depth += 1
2586.1.2 by Dylan McCall
Fixed spacing and misaligned security group headers in CellAreaPackage.
155
                if depth > 1:
156
                    indent = max(0, depth - 1)
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
157
                    indent_size = cell_size * indent
2586.1.3 by Dylan McCall
In CellAreaPackage, fixed indent for cells at depth 3 to be consistent with current results.
158
                    if depth == 2:
159
                        indent_extra = spacing
160
                    elif depth == 3:
161
                        indent_extra = spacing + 1
162
                    else:
163
                        indent_extra = spacing * indent
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
164
                    cell_area.x += indent_size + indent_extra
165
166
            if is_last_cell:
167
                cell_size = max(cell_size, orig_end - cell_area.x)
2586.1.2 by Dylan McCall
Fixed spacing and misaligned security group headers in CellAreaPackage.
168
            if not cell_is_hidden[cell_number]:
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
169
                cell_area.width = cell_size + extra_cell_width
170
                extra_cell_width = 0
2586.1.2 by Dylan McCall
Fixed spacing and misaligned security group headers in CellAreaPackage.
171
            else:
172
                cell_area.width = 0
173
                extra_cell_width = cell_size + spacing
2586.1.1 by Dylan McCall
Refactoring CellAreaPackage to decouple it (mostly) from the specifics of UpdatesAvailable.
174
175
            if callback(cell, cell_area.copy(), bg_area.copy()):
176
                return
2428.5.34 by Michael Terry
make the toggle icons reliably clickable
177
178
    def do_event(self, context, widget, event, cell_area, flags):
179
        # This override is just to trick our parent implementation into
180
        # allowing clicks on toggle cells when they are where the expanders
181
        # usually are.  It doesn't expect that, so we expand the cell_area
182
        # here to be equivalent to bg_area.
183
        cell_start = self.get_cell_start(widget)
184
        cell_area.width = cell_area.width + cell_area.x - cell_start
185
        cell_area.x = cell_start
186
        return Gtk.CellAreaBox.do_event(self, context, widget, event,
187
                                        cell_area, flags)
188
189
    def get_cell_start(self, widget):
190
        if not self.column:
191
            return 0
192
        else:
193
            val = GObject.Value()
194
            val.init(int)
195
            widget.style_get_property("horizontal-separator", val)
196
            h_sep = val.get_int()
197
            widget.style_get_property("grid-line-width", val)
198
            line_width = val.get_int()
2428.5.37 by Michael Terry
whoops, if no security updates, we drew over the expanders
199
            cell_start = self.column.get_x_offset() - h_sep - line_width
200
            if not self.indent_toplevel:  # i.e. if no headers
201
                widget.style_get_property("expander-size", val)
202
                spacing = self.get_property("spacing")
203
                # Hardcode 4 because GTK+ hardcodes 4 internally
204
                cell_start = cell_start + val.get_int() + 4 + spacing
205
            return cell_start
2428.5.32 by Michael Terry
use custom Gtk.CellArea to layout package cells more like the spec has it
206
207
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
208
class UpdatesAvailable(InternalDialog):
2614.1.1 by Michael Terry
Look for application icons in app-install-data's icon directory
209
    APP_INSTALL_ICONS_PATH = "/usr/share/app-install/icons"
2428.3.1 by Michael Terry
bring some of AptDialog from aptdaemon into our code to have more control over its presentation (i.e. stuff the widgets inside of our own main window; split UpdateManager into a central point of control and put old widgets into UpdatesAvailable class
210
2664 by Marc Deslauriers
* Allow user to close the restart required dialog (LP: #1033226)
211
    def __init__(self, window_main, header=None, desc=None,
212
                 need_reboot=False):
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
213
        InternalDialog.__init__(self, window_main)
2622.1.2 by Dylan McCall
UpdatesAvailable is now a Dialog subclass.
214
215
        self.window_main = window_main
216
        self.datadir = window_main.datadir
217
        self.cache = window_main.cache
218
2622.1.6 by Dylan McCall
Fixed Updates Available dialog flickering when shown.
219
        self.custom_header = header
220
        self.custom_desc = desc
2664 by Marc Deslauriers
* Allow user to close the restart required dialog (LP: #1033226)
221
        self.need_reboot = need_reboot
2622.1.6 by Dylan McCall
Fixed Updates Available dialog flickering when shown.
222
2634 by Martin Pitt
Fix PEP-8 errors.
223
        content_ui_path = os.path.join(self.datadir,
224
                                       "gtkbuilder/UpdateManager.ui")
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
225
        self._load_ui(content_ui_path, "pane_updates_available")
2622.1.2 by Dylan McCall
UpdatesAvailable is now a Dialog subclass.
226
        self.set_content_widget(self.pane_updates_available)
2511.1.12 by Michael Terry
last of the pep8 error fixes
227
2622.1.6 by Dylan McCall
Fixed Updates Available dialog flickering when shown.
228
        self.dl_size = 0
229
        self.connected = True
230
2511.1.12 by Michael Terry
last of the pep8 error fixes
231
        # Used for inhibiting power management
232
        self.sleep_cookie = None
233
2662 by Martin Pitt
Stop using deprecated GObject constructors with positional arguments
234
        self.settings = Gio.Settings.new("com.ubuntu.update-manager")
2622.1.6 by Dylan McCall
Fixed Updates Available dialog flickering when shown.
235
236
        # Special icon theme for looking up app-install-data icons
2670 by Sebastien Bacher
* UpdateManager/UpdatesAvailable.py:
237
        self.app_icons = Gtk.IconTheme.get_default()
2622.1.6 by Dylan McCall
Fixed Updates Available dialog flickering when shown.
238
        self.app_icons.append_search_path(self.APP_INSTALL_ICONS_PATH)
239
240
        # Create Unity launcher quicklist
241
        # FIXME: instead of passing parent we really should just send signals
242
        self.unity = UnitySupport(parent=self)
2534.1.1 by Michael Terry
add you-stopped-update dialog
243
2622.1.2 by Dylan McCall
UpdatesAvailable is now a Dialog subclass.
244
        # setup the help viewer and disable the help button if there
245
        # is no viewer available
246
        #self.help_viewer = HelpViewer("update-manager")
247
        #if self.help_viewer.check() == False:
248
        #    self.button_help.set_sensitive(False)
249
250
        self.add_settings_button()
2634 by Martin Pitt
Fix PEP-8 errors.
251
        self.button_close = self.add_button(Gtk.STOCK_CANCEL,
252
                                            self.window_main.close)
2622.1.2 by Dylan McCall
UpdatesAvailable is now a Dialog subclass.
253
        self.button_install = self.add_button(_("Install Now"),
2634 by Martin Pitt
Fix PEP-8 errors.
254
                                              self.on_button_install_clicked)
2622.1.3 by Dylan McCall
Clean up header and description label for dialogs. Now reallocates height for width, fixing empty space in some dialogs.
255
        self.focus_button = self.button_install
2622.1.2 by Dylan McCall
UpdatesAvailable is now a Dialog subclass.
256
2511.1.12 by Michael Terry
last of the pep8 error fixes
257
        # create text view
258
        self.textview_changes = ChangelogViewer()
259
        self.textview_changes.show()
260
        self.scrolledwindow_changes.add(self.textview_changes)
261
        changes_buffer = self.textview_changes.get_buffer()
262
        changes_buffer.create_tag("versiontag", weight=Pango.Weight.BOLD)
263
264
        # the treeview (move into it's own code!)
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
265
        self.store = Gtk.TreeStore(str, GObject.TYPE_PYOBJECT, str, bool, bool)
2622.1.6 by Dylan McCall
Fixed Updates Available dialog flickering when shown.
266
        self.treeview_update.set_model(None)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
267
2616.2.1 by Michael Terry
Add a 'needs a restart' label if any selected package will require a restart
268
        self.image_restart.set_from_gicon(self.get_restart_icon(),
269
                                          Gtk.IconSize.BUTTON)
270
2428.5.10 by Dylan McCall
Packages are now only grouped by dependencies, not by source packages.
271
        restart_icon_renderer = Gtk.CellRendererPixbuf()
272
        restart_icon_renderer.set_property("xpad", 4)
273
        restart_icon_renderer.set_property("ypad", 2)
274
        restart_icon_renderer.set_property("stock-size", Gtk.IconSize.MENU)
2428.5.14 by Dylan McCall
Attempt to load a symbolic icon for 'restart required'.
275
        restart_icon_renderer.set_property("follow-state", True)
2428.5.10 by Dylan McCall
Packages are now only grouped by dependencies, not by source packages.
276
        restart_column = Gtk.TreeViewColumn(None, restart_icon_renderer)
2428.5.48 by Michael Terry
make restart column have a fixed size, so it shows the same regardless of whether we have packages that need a restart or not
277
        restart_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
278
        restart_column.set_fixed_width(20)
2428.5.10 by Dylan McCall
Packages are now only grouped by dependencies, not by source packages.
279
        self.treeview_update.append_column(restart_column)
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
280
        restart_column.set_cell_data_func(restart_icon_renderer,
281
                                          self.restart_icon_renderer_data_func)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
282
2622.1.1 by Dylan McCall
Defensive coding around package objects in UpdateList and UpdatesAvailable.
283
        self.pkg_cell_area = CellAreaPackage(False)
284
        pkg_column = Gtk.TreeViewColumn.new_with_area(self.pkg_cell_area)
285
        self.pkg_cell_area.column = pkg_column
2809.1.1 by Balint Reczey
Offer removal of unused autoremovable kernel packages
286
        pkg_column.set_title(_("Install or remove"))
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
287
        pkg_column.set_property("spacing", 4)
288
        pkg_column.set_expand(True)
289
        self.treeview_update.append_column(pkg_column)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
290
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
291
        pkg_toggle_renderer = Gtk.CellRendererToggle()
292
        pkg_toggle_renderer.set_property("ypad", 2)
293
        pkg_toggle_renderer.connect("toggled", self.on_update_toggled)
294
        pkg_column.pack_start(pkg_toggle_renderer, False)
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
295
        pkg_column.add_attribute(pkg_toggle_renderer,
296
                                 'active', LIST_TOGGLE_ACTIVE)
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
297
        pkg_column.add_attribute(pkg_toggle_renderer,
298
                                 'sensitive', LIST_SENSITIVE)
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
299
        pkg_column.set_cell_data_func(pkg_toggle_renderer,
300
                                      self.pkg_toggle_renderer_data_func)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
301
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
302
        pkg_icon_renderer = Gtk.CellRendererPixbuf()
303
        pkg_icon_renderer.set_property("ypad", 2)
304
        pkg_icon_renderer.set_property("stock-size", Gtk.IconSize.MENU)
305
        pkg_column.pack_start(pkg_icon_renderer, False)
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
306
        pkg_column.set_cell_data_func(pkg_icon_renderer,
307
                                      self.pkg_icon_renderer_data_func)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
308
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
309
        pkg_label_renderer = Gtk.CellRendererText()
310
        pkg_label_renderer.set_property("ypad", 2)
2586.2.1 by Dylan McCall
Properly ellipsize package column text (LP: #1105363)
311
        pkg_label_renderer.set_property("ellipsize", Pango.EllipsizeMode.END)
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
312
        pkg_column.pack_start(pkg_label_renderer, True)
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
313
        pkg_column.set_cell_data_func(pkg_label_renderer,
314
                                      self.pkg_label_renderer_data_func)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
315
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
316
        size_renderer = Gtk.CellRendererText()
317
        size_renderer.set_property("xpad", 6)
318
        size_renderer.set_property("ypad", 0)
319
        size_renderer.set_property("xalign", 1)
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
320
        # 1.0/1.2 == PANGO.Scale.SMALL. Constant is not (yet) introspected.
2428.5.23 by Michael Terry
make tests pass again
321
        size_renderer.set_property("scale", 1.0 / 1.2)
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
322
        size_column = Gtk.TreeViewColumn(_("Download"), size_renderer,
323
                                         text=LIST_SIZE)
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
324
        size_column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
325
        size_column.add_attribute(size_renderer,
326
                                  'sensitive', LIST_SENSITIVE)
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
327
        self.treeview_update.append_column(size_column)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
328
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
329
        self.treeview_update.set_headers_visible(True)
330
        self.treeview_update.set_headers_clickable(False)
2511.1.12 by Michael Terry
last of the pep8 error fixes
331
        self.treeview_update.set_direction(Gtk.TextDirection.LTR)
332
        self.treeview_update.set_fixed_height_mode(False)
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
333
        self.treeview_update.set_expander_column(pkg_column)
2511.1.12 by Michael Terry
last of the pep8 error fixes
334
        self.treeview_update.set_search_column(LIST_NAME)
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
335
        self.treeview_update.connect("button-press-event",
336
                                     self.on_treeview_button_press)
2428.5.32 by Michael Terry
use custom Gtk.CellArea to layout package cells more like the spec has it
337
2511.1.12 by Michael Terry
last of the pep8 error fixes
338
        # init show version
339
        self.show_versions = self.settings.get_boolean("show-versions")
340
        # init summary_before_name
341
        self.summary_before_name = self.settings.get_boolean(
342
            "summary-before-name")
343
2622.1.6 by Dylan McCall
Fixed Updates Available dialog flickering when shown.
344
        # expander
345
        self.expander_details.set_expanded(
346
            self.settings.get_boolean("show-details"))
347
        self.expander_details.connect("activate", self.pre_activate_details)
348
        self.expander_details.connect("notify::expanded",
2634 by Martin Pitt
Fix PEP-8 errors.
349
                                      self.activate_details)
2622.1.6 by Dylan McCall
Fixed Updates Available dialog flickering when shown.
350
        self.expander_desc.connect("notify::expanded", self.activate_desc)
351
352
        # If auto-updates are on, change cancel label
2662 by Martin Pitt
Stop using deprecated GObject constructors with positional arguments
353
        self.notifier_settings = Gio.Settings.new("com.ubuntu.update-notifier")
2622.1.6 by Dylan McCall
Fixed Updates Available dialog flickering when shown.
354
        self.notifier_settings.connect(
355
            "changed::auto-launch",
356
            lambda s, p: self.update_close_button())
357
        self.update_close_button()
2511.1.14 by Michael Terry
auto-pep8'd some warnings
358
2511.1.12 by Michael Terry
last of the pep8 error fixes
359
        # Alert watcher
360
        self.alert_watcher = AlertWatcher()
361
        self.alert_watcher.connect("network-alert", self._on_network_alert)
362
        self.alert_watcher.connect("battery-alert", self._on_battery_alert)
363
        self.alert_watcher.connect("network-3g-alert",
364
                                   self._on_network_3g_alert)
365
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
366
    def stop(self):
367
        InternalDialog.stop(self)
368
        self._save_state()
369
2622.1.10 by Dylan McCall
Removing ill-advised WrapLabel widget. Restored original behaviour for text wrapping in heading and description labels.
370
    def start(self):
371
        InternalDialog.start(self)
2622.1.11 by Dylan McCall
Removed unnecessary resizing of main window during _start_pane.
372
        self.set_update_list(self.window_main.update_list)
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
373
        self.alert_watcher.check_alert_state()
2622.1.11 by Dylan McCall
Removed unnecessary resizing of main window during _start_pane.
374
        self._restore_state()
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
375
2511.1.12 by Michael Terry
last of the pep8 error fixes
376
    def is_auto_update(self):
377
        update_days = apt_pkg.config.find_i(
378
            "APT::Periodic::Update-Package-Lists")
2624 by Brian Murray
Remove check for update-notifier auto-launch gsettings key
379
        return update_days >= 1
2511.1.12 by Michael Terry
last of the pep8 error fixes
380
381
    def update_close_button(self):
382
        if self.is_auto_update():
383
            self.button_close.set_label(_("_Remind Me Later"))
384
            self.button_close.set_use_stock(False)
385
            self.button_close.set_use_underline(True)
386
        else:
387
            self.button_close.set_label(Gtk.STOCK_CANCEL)
388
            self.button_close.set_use_stock(True)
389
            self.button_close.set_use_underline(False)
390
391
    def install_all_updates(self, menu, menuitem, data):
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
392
        self.select_all_upgrades(None)
2640 by Dmitrijs Ledkovs
Fix update-manager crashing when trying to raise
393
        self.on_button_install_clicked()
2511.1.12 by Michael Terry
last of the pep8 error fixes
394
2616.2.1 by Michael Terry
Add a 'needs a restart' label if any selected package will require a restart
395
    def pkg_requires_restart(self, pkg):
2616.2.2 by Michael Terry
Merge from trunk
396
        if pkg is None or pkg.candidate is None:
397
            return False
2660 by Michael Vogt
* lp:~mterry/update-manager/requires-restart:
398
        restart_condition = pkg.candidate.record.get('Restart-Required')
2616.2.1 by Michael Terry
Add a 'needs a restart' label if any selected package will require a restart
399
        return restart_condition == 'system'
400
401
    def get_restart_icon(self):
402
        # FIXME: Non-standard, incorrect icon name (from app category).
403
        # Theme support for what we want seems to be lacking.
404
        restart_icon_names = ['view-refresh-symbolic',
405
                              'system-restart',
406
                              'system-reboot']
407
        return Gio.ThemedIcon.new_from_names(restart_icon_names)
408
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
409
    def restart_icon_renderer_data_func(self, cell_layout, renderer, model,
2622.1.3 by Dylan McCall
Clean up header and description label for dialogs. Now reallocates height for width, fixing empty space in some dialogs.
410
                                        iter, user_data):
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
411
        data = model.get_value(iter, LIST_UPDATE_DATA)
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
412
        sensitive = model.get_value(iter, LIST_SENSITIVE)
2428.5.10 by Dylan McCall
Packages are now only grouped by dependencies, not by source packages.
413
        path = model.get_path(iter)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
414
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
415
        renderer.set_sensitive(sensitive)
416
2428.5.10 by Dylan McCall
Packages are now only grouped by dependencies, not by source packages.
417
        requires_restart = False
2622.1.3 by Dylan McCall
Clean up header and description label for dialogs. Now reallocates height for width, fixing empty space in some dialogs.
418
        if data.item and data.item.pkg:
2616.2.1 by Michael Terry
Add a 'needs a restart' label if any selected package will require a restart
419
            requires_restart = self.pkg_requires_restart(data.item.pkg)
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
420
        elif data.group:
2428.5.20 by Dylan McCall
Fixed style and commenting issues in UpdatesAvailable.py.
421
            if not self.treeview_update.row_expanded(path):
2428.5.10 by Dylan McCall
Packages are now only grouped by dependencies, not by source packages.
422
                # A package in the group requires restart
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
423
                for group_item in data.group.items:
2616.2.2 by Michael Terry
Merge from trunk
424
                    if group_item.pkg and self.pkg_requires_restart(
425
                            group_item.pkg):
2428.5.10 by Dylan McCall
Packages are now only grouped by dependencies, not by source packages.
426
                        requires_restart = True
427
                        break
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
428
2428.5.14 by Dylan McCall
Attempt to load a symbolic icon for 'restart required'.
429
        if requires_restart:
2616.2.1 by Michael Terry
Add a 'needs a restart' label if any selected package will require a restart
430
            gicon = self.get_restart_icon()
2428.5.14 by Dylan McCall
Attempt to load a symbolic icon for 'restart required'.
431
        else:
432
            gicon = None
2428.5.10 by Dylan McCall
Packages are now only grouped by dependencies, not by source packages.
433
        renderer.set_property("gicon", gicon)
434
2622.1.3 by Dylan McCall
Clean up header and description label for dialogs. Now reallocates height for width, fixing empty space in some dialogs.
435
    def pkg_toggle_renderer_data_func(self, cell_layout, renderer, model,
436
                                      iter, user_data):
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
437
        data = model.get_value(iter, LIST_UPDATE_DATA)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
438
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
439
        activatable = False
440
        inconsistent = False
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
441
        if data.item:
2584 by Michael Terry
Properly xml-escape application names too, not just package labels
442
            activatable = data.item.pkg.name not in self.list.held_back
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
443
            inconsistent = False
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
444
            renderer.set_sensitive(data.item.sensitive)
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
445
        elif data.group:
446
            activatable = True
447
            inconsistent = data.group.selection_is_inconsistent()
448
        elif data.groups:
449
            activatable = True
2428.5.33 by Michael Terry
fix rendering and toggle behavior of header toggles
450
            inconsistent = False
451
            saw_install = None
452
            for group in data.groups:
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
453
                for item in group.items:
2590.1.1 by Michael Terry
Fix toggling items after doing a deselect-all
454
                    this_install = item.is_selected()
2428.5.33 by Michael Terry
fix rendering and toggle behavior of header toggles
455
                    if saw_install is not None and saw_install != this_install:
456
                        inconsistent = True
457
                        break
458
                    saw_install = this_install
459
                if inconsistent:
460
                    break
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
461
2428.5.55 by Dylan McCall
Removed an item in the list being selected by default - this is not in the new specification.
462
        # The "active" attribute is already set via LIST_TOGGLE_ACTIVE in the
463
        # tree model, so we don't set it here.
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
464
        renderer.set_property("activatable", activatable)
465
        renderer.set_property("inconsistent", inconsistent)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
466
2622.1.3 by Dylan McCall
Clean up header and description label for dialogs. Now reallocates height for width, fixing empty space in some dialogs.
467
    def pkg_icon_renderer_data_func(self, cell_layout, renderer, model,
468
                                    iter, user_data):
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
469
        data = model.get_value(iter, LIST_UPDATE_DATA)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
470
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
471
        gicon = None
472
        if data.group:
2614.1.1 by Michael Terry
Look for application icons in app-install-data's icon directory
473
            gicon = self.get_app_install_icon(data.group.icon)
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
474
        elif data.item:
2614.1.1 by Michael Terry
Look for application icons in app-install-data's icon directory
475
            gicon = self.get_app_install_icon(data.item.icon)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
476
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
477
        renderer.set_property("gicon", gicon)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
478
2614.1.1 by Michael Terry
Look for application icons in app-install-data's icon directory
479
    def get_app_install_icon(self, icon):
480
        """Any application icon is coming from app-install-data's desktop
481
           files, which refer to icons from app-install-data's icon directory.
482
           So we look them up here."""
483
484
        if not isinstance(icon, Gio.ThemedIcon):
485
            return icon  # shouldn't happen
486
487
        info = self.app_icons.choose_icon(icon.get_names(), 16,
488
                                          Gtk.IconLookupFlags.FORCE_SIZE)
489
        if info is not None:
490
            return Gio.FileIcon.new(Gio.File.new_for_path(info.get_filename()))
491
        else:
492
            return icon  # Assume it's in one of the user's themes
493
2622.1.3 by Dylan McCall
Clean up header and description label for dialogs. Now reallocates height for width, fixing empty space in some dialogs.
494
    def pkg_label_renderer_data_func(self, cell_layout, renderer, model,
495
                                     iter, user_data):
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
496
        data = model.get_value(iter, LIST_UPDATE_DATA)
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
497
        sensitive = model.get_value(iter, LIST_SENSITIVE)
2584 by Michael Terry
Properly xml-escape application names too, not just package labels
498
        name = GLib.markup_escape_text(model.get_value(iter, LIST_NAME))
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
499
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
500
        renderer.set_sensitive(sensitive)
501
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
502
        if data.group:
503
            markup = name
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
504
        elif data.item:
2428.5.39 by Michael Terry
drop 'New install' paranthetical
505
            markup = name
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
506
        else:  # header
507
            markup = "<b>%s</b>" % name
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
508
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
509
        renderer.set_property("markup", markup)
2511.1.12 by Michael Terry
last of the pep8 error fixes
510
511
    def set_changes_buffer(self, changes_buffer, text, name, srcpkg):
512
        changes_buffer.set_text("")
513
        lines = text.split("\n")
514
        if len(lines) == 1:
515
            changes_buffer.set_text(text)
516
            return
2511.1.14 by Michael Terry
auto-pep8'd some warnings
517
2511.1.12 by Michael Terry
last of the pep8 error fixes
518
        for line in lines:
519
            end_iter = changes_buffer.get_end_iter()
520
            version_match = re.match(
521
                r'^%s \((.*)\)(.*)\;.*$' % re.escape(srcpkg), line)
522
            #bullet_match = re.match("^.*[\*-]", line)
523
            author_match = re.match("^.*--.*<.*@.*>.*$", line)
524
            if version_match:
525
                version = version_match.group(1)
526
                #upload_archive = version_match.group(2).strip()
527
                version_text = _("Version %s: \n") % version
528
                changes_buffer.insert_with_tags_by_name(end_iter, version_text,
529
                                                        "versiontag")
530
            elif (author_match):
531
                pass
532
            else:
533
                changes_buffer.insert(end_iter, line + "\n")
534
535
    def on_treeview_update_cursor_changed(self, widget):
536
        path = widget.get_cursor()[0]
537
        # check if we have a path at all
538
        if path is None:
539
            return
540
        model = widget.get_model()
541
        iter = model.get_iter(path)
542
543
        # set descr
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
544
        data = model.get_value(iter, LIST_UPDATE_DATA)
2428.5.46 by Michael Terry
if an app group has a core item, show its changelog when selected
545
        item = data.item
2841.1.1 by Balint Reczey
Ignore PEP 8 W503 instead of E502 and drop many added backslashes
546
        if (item is None and data.group is not None
2840.1.1 by Balint Reczey
Fix PEP 8 warnings
547
                and data.group.core_item is not None):
2428.5.46 by Michael Terry
if an app group has a core item, show its changelog when selected
548
            item = data.group.core_item
2841.1.1 by Balint Reczey
Ignore PEP 8 W503 instead of E502 and drop many added backslashes
549
        if (item is None or item.pkg is None
550
                or item.pkg.candidate is None
2840.1.1 by Balint Reczey
Fix PEP 8 warnings
551
                or item.pkg.candidate.description is None):
2511.1.12 by Michael Terry
last of the pep8 error fixes
552
            changes_buffer = self.textview_changes.get_buffer()
553
            changes_buffer.set_text("")
554
            desc_buffer = self.textview_descr.get_buffer()
555
            desc_buffer.set_text("")
556
            self.notebook_details.set_sensitive(False)
557
            return
2428.5.46 by Michael Terry
if an app group has a core item, show its changelog when selected
558
        long_desc = item.pkg.candidate.description
2511.1.12 by Michael Terry
last of the pep8 error fixes
559
        self.notebook_details.set_sensitive(True)
560
        # do some regular expression magic on the description
561
        # Add a newline before each bullet
562
        p = re.compile(r'^(\s|\t)*(\*|0|-)', re.MULTILINE)
563
        long_desc = p.sub('\n*', long_desc)
564
        # replace all newlines by spaces
565
        p = re.compile(r'\n', re.MULTILINE)
566
        long_desc = p.sub(" ", long_desc)
567
        # replace all multiple spaces by newlines
568
        p = re.compile(r'\s\s+', re.MULTILINE)
569
        long_desc = p.sub("\n", long_desc)
2894 by Brian Murray
UpdateManager/UpdatesAvailable.py: In the technical description portion of
570
        long_desc = ("Package: %s\n%s" %
571
                     (item.pkg.name, long_desc))
2511.1.12 by Michael Terry
last of the pep8 error fixes
572
573
        desc_buffer = self.textview_descr.get_buffer()
574
        desc_buffer.set_text(long_desc)
575
576
        # now do the changelog
2428.5.46 by Michael Terry
if an app group has a core item, show its changelog when selected
577
        name = item.pkg.name
2511.1.12 by Michael Terry
last of the pep8 error fixes
578
        if name is None:
579
            return
580
581
        changes_buffer = self.textview_changes.get_buffer()
2511.1.14 by Michael Terry
auto-pep8'd some warnings
582
583
        # check if we have the changes already and if so, display them
2511.1.12 by Michael Terry
last of the pep8 error fixes
584
        # (even if currently disconnected)
585
        if name in self.cache.all_changes:
586
            changes = self.cache.all_changes[name]
587
            srcpkg = self.cache[name].candidate.source_name
588
            self.set_changes_buffer(changes_buffer, changes, name, srcpkg)
589
        # if not connected, do not even attempt to get the changes
590
        elif not self.connected:
591
            changes_buffer.set_text(
592
                _("No network connection detected, you can not download "
593
                  "changelog information."))
594
        # else, get it from the entwork
595
        elif self.expander_details.get_expanded():
596
            lock = threading.Lock()
597
            lock.acquire()
598
            changelog_thread = threading.Thread(
599
                target=self.cache.get_news_and_changelog, args=(name, lock))
600
            changelog_thread.start()
601
            changes_buffer.set_text("%s\n" %
602
                                    _("Downloading list of changes..."))
603
            iter = changes_buffer.get_iter_at_line(1)
604
            anchor = changes_buffer.create_child_anchor(iter)
605
            button = Gtk.Button(stock="gtk-cancel")
606
            self.textview_changes.add_child_at_anchor(button, anchor)
607
            button.show()
608
            id = button.connect("clicked",
609
                                lambda w, lock: lock.release(), lock)
610
            # wait for the dl-thread
611
            while lock.locked():
612
                time.sleep(0.01)
613
                while Gtk.events_pending():
614
                    Gtk.main_iteration()
615
            # download finished (or canceld, or time-out)
616
            button.hide()
617
            if button.handler_is_connected(id):
618
                button.disconnect(id)
619
        # check if we still are in the right pkg (the download may have taken
620
        # some time and the user may have clicked on a new pkg)
2428.5.29 by Michael Terry
show changelog details for group of one app
621
        now_path = widget.get_cursor()[0]
622
        if now_path is None:
2511.1.12 by Michael Terry
last of the pep8 error fixes
623
            return
2428.5.29 by Michael Terry
show changelog details for group of one app
624
        if path != now_path:
2511.1.12 by Michael Terry
last of the pep8 error fixes
625
            return
626
        # display NEWS.Debian first, then the changelog
627
        changes = ""
628
        srcpkg = self.cache[name].candidate.source_name
629
        if name in self.cache.all_news:
630
            changes += self.cache.all_news[name]
631
        if name in self.cache.all_changes:
632
            changes += self.cache.all_changes[name]
633
        if changes:
634
            self.set_changes_buffer(changes_buffer, changes, name, srcpkg)
635
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
636
    def on_treeview_button_press(self, widget, event):
2511.1.12 by Michael Terry
last of the pep8 error fixes
637
        """
638
        Show a context menu if a right click was performed on an update entry
639
        """
640
        if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
641
            # need to keep a reference here of menu, otherwise it gets
642
            # deleted when it goes out of scope and no menu is visible
643
            # (bug #806949)
644
            self.menu = menu = Gtk.Menu()
645
            item_select_none = \
646
                Gtk.MenuItem.new_with_mnemonic(_("_Deselect All"))
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
647
            item_select_none.connect("activate", self.select_none_upgrades)
2511.1.12 by Michael Terry
last of the pep8 error fixes
648
            menu.append(item_select_none)
2837.1.1 by Balint Reczey
Allow unselecting updates and performing only removals
649
            num_updates = self.cache.install_count + self.cache.del_count
2511.1.12 by Michael Terry
last of the pep8 error fixes
650
            if num_updates == 0:
651
                item_select_none.set_property("sensitive", False)
652
            item_select_all = Gtk.MenuItem.new_with_mnemonic(_("Select _All"))
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
653
            item_select_all.connect("activate", self.select_all_upgrades)
2511.1.12 by Michael Terry
last of the pep8 error fixes
654
            menu.append(item_select_all)
655
            menu.show_all()
656
            menu.popup_for_device(
657
                None, None, None, None, None, event.button, event.time)
658
            menu.show()
659
            return True
660
661
    # we need this for select all/unselect all
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
662
    def _toggle_group_headers(self, new_selection_value):
663
        """ small helper that will set/unset the group headers
2511.1.12 by Michael Terry
last of the pep8 error fixes
664
        """
665
        model = self.treeview_update.get_model()
666
        for row in model:
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
667
            data = model.get_value(row.iter, LIST_UPDATE_DATA)
668
            if data.groups is not None or data.group is not None:
2428.5.23 by Michael Terry
make tests pass again
669
                model.set_value(row.iter, LIST_TOGGLE_ACTIVE,
670
                                new_selection_value)
2511.1.12 by Michael Terry
last of the pep8 error fixes
671
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
672
    def select_all_upgrades(self, widget):
2511.1.12 by Michael Terry
last of the pep8 error fixes
673
        """
674
        Select all updates
675
        """
676
        self.setBusy(True)
677
        self.cache.saveDistUpgrade()
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
678
        self._toggle_group_headers(True)
2511.1.12 by Michael Terry
last of the pep8 error fixes
679
        self.treeview_update.queue_draw()
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
680
        self.updates_changed()
2511.1.12 by Michael Terry
last of the pep8 error fixes
681
        self.setBusy(False)
682
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
683
    def select_none_upgrades(self, widget):
2511.1.12 by Michael Terry
last of the pep8 error fixes
684
        """
685
        Select none updates
686
        """
687
        self.setBusy(True)
688
        self.cache.clear()
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
689
        self._toggle_group_headers(False)
2511.1.12 by Michael Terry
last of the pep8 error fixes
690
        self.treeview_update.queue_draw()
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
691
        self.updates_changed()
2511.1.12 by Michael Terry
last of the pep8 error fixes
692
        self.setBusy(False)
693
694
    def setBusy(self, flag):
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 """
697
        if self.window_main.get_window() is None:
698
            return
699
        if flag:
700
            self.window_main.get_window().set_cursor(
701
                Gdk.Cursor.new(Gdk.CursorType.WATCH))
702
        else:
703
            self.window_main.get_window().set_cursor(None)
704
        while Gtk.events_pending():
2119.1.1 by Robert Roth
Porting to PyGI
705
            Gtk.main_iteration()
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
706
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
707
    def _mark_selected_updates(self):
708
        def foreach_cb(model, path, iter, data):
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
709
            data = model.get_value(iter, LIST_UPDATE_DATA)
710
            active = False
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
711
            if data.item:
2590.1.1 by Michael Terry
Fix toggling items after doing a deselect-all
712
                active = data.item.is_selected()
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
713
            elif data.group:
714
                active = data.group.packages_are_selected()
715
            elif data.groups:
716
                active = any([g.packages_are_selected() for g in data.groups])
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
717
            model.set_value(iter, LIST_TOGGLE_ACTIVE, active)
718
        self.store.foreach(foreach_cb, None)
2511.1.12 by Michael Terry
last of the pep8 error fixes
719
2616.2.1 by Michael Terry
Add a 'needs a restart' label if any selected package will require a restart
720
    def _check_for_required_restart(self):
721
        requires_restart = False
2616.2.2 by Michael Terry
Merge from trunk
722
2616.2.1 by Michael Terry
Add a 'needs a restart' label if any selected package will require a restart
723
        def foreach_cb(model, path, iter, data):
724
            data = model.get_value(iter, LIST_UPDATE_DATA)
725
            active = model.get_value(iter, LIST_TOGGLE_ACTIVE)
726
            if not active:
727
                return
728
            pkg = None
729
            if data.item:
730
                pkg = data.item.pkg
731
            elif data.group and data.group.core_item:
732
                pkg = data.group.core_item.pkg
733
            if pkg and self.pkg_requires_restart(pkg):
734
                nonlocal requires_restart
735
                requires_restart = True
2616.2.2 by Michael Terry
Merge from trunk
736
2616.2.1 by Michael Terry
Add a 'needs a restart' label if any selected package will require a restart
737
        self.store.foreach(foreach_cb, None)
738
        self.hbox_restart.set_visible(requires_restart)
739
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
740
    def _refresh_updates_count(self):
2841.1.1 by Balint Reczey
Ignore PEP 8 W503 instead of E502 and drop many added backslashes
741
        self.button_install.set_sensitive(self.cache.install_count
2840.1.1 by Balint Reczey
Fix PEP 8 warnings
742
                                          + self.cache.del_count)
2511.1.12 by Michael Terry
last of the pep8 error fixes
743
        try:
2837.1.1 by Balint Reczey
Allow unselecting updates and performing only removals
744
            inst_count = self.cache.install_count + self.cache.del_count
2511.1.12 by Michael Terry
last of the pep8 error fixes
745
            self.dl_size = self.cache.required_download
746
            download_str = ""
747
            if self.dl_size != 0:
748
                download_str = _("%s will be downloaded.") % (
749
                    humanize_size(self.dl_size))
750
                self.image_downsize.set_sensitive(True)
751
                # do not set the buttons to sensitive/insensitive until NM
752
                # can deal with dialup connections properly
753
                #if self.alert_watcher.network_state != NM_STATE_CONNECTED:
754
                #    self.button_install.set_sensitive(False)
755
                #else:
756
                #    self.button_install.set_sensitive(True)
757
                self.button_install.set_sensitive(True)
758
                self.unity.set_install_menuitem_visible(True)
759
            else:
760
                if inst_count > 0:
761
                    download_str = ngettext(
762
                        "The update has already been downloaded.",
763
                        "The updates have already been downloaded.",
764
                        inst_count)
765
                    self.button_install.set_sensitive(True)
766
                    self.unity.set_install_menuitem_visible(True)
767
                else:
768
                    download_str = _("There are no updates to install.")
769
                    self.button_install.set_sensitive(False)
770
                    self.unity.set_install_menuitem_visible(False)
771
                self.image_downsize.set_sensitive(False)
772
            self.label_downsize.set_text(download_str)
773
            self.hbox_downsize.show()
774
            self.vbox_alerts.show()
775
        except SystemError as e:
776
            print("required_download could not be calculated: %s" % e)
777
            self.label_downsize.set_markup(_("Unknown download size."))
778
            self.image_downsize.set_sensitive(False)
779
            self.hbox_downsize.show()
780
            self.vbox_alerts.show()
781
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
782
    def updates_changed(self):
783
        self._mark_selected_updates()
2616.2.1 by Michael Terry
Add a 'needs a restart' label if any selected package will require a restart
784
        self._check_for_required_restart()
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
785
        self._refresh_updates_count()
786
2511.1.12 by Michael Terry
last of the pep8 error fixes
787
    def update_count(self):
2643 by Brian Murray
Core/UpdateList.py: if a package is an ignored phased update mark it for
788
        """activate or disable widgets and show dialog texts corresponding to
2511.1.12 by Michael Terry
last of the pep8 error fixes
789
           the number of available updates"""
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
790
        self.updates_changed()
2534.1.1 by Michael Terry
add you-stopped-update dialog
791
792
        text_header = None
793
        text_desc = None
794
795
        if self.custom_header is not None:
796
            text_header = self.custom_header
2622.1.2 by Dylan McCall
UpdatesAvailable is now a Dialog subclass.
797
        if self.custom_desc is not None:
798
            text_desc = self.custom_desc
799
2534.1.1 by Michael Terry
add you-stopped-update dialog
800
        # show different text on first run (UX team suggestion)
801
        elif self.settings.get_boolean("first-run"):
802
            flavor = self.window_main.meta_release.flavor_name
803
            version = self.window_main.meta_release.current_dist_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)
807
            self.settings.set_boolean("first-run", False)
808
        else:
809
            text_header = _("Updated software is available for this "
810
                            "computer. Do you want to install it now?")
2665 by Martin Pitt
Fix PEP-8 style error to fix tests.
811
            if not self.hbox_restart.get_visible() and self.need_reboot:
2664 by Marc Deslauriers
* Allow user to close the restart required dialog (LP: #1033226)
812
                text_desc = _("The computer also needs to restart "
813
                              "to finish installing previous updates.")
2534.1.1 by Michael Terry
add you-stopped-update dialog
814
815
        self.notebook_details.set_sensitive(True)
816
        self.treeview_update.set_sensitive(True)
2622.1.2 by Dylan McCall
UpdatesAvailable is now a Dialog subclass.
817
        self.set_header(text_header)
818
        self.set_desc(text_desc)
2534.1.1 by Michael Terry
add you-stopped-update dialog
819
337.4.11 by glatzor at ubuntu
* Allow to select all or none update - fixes #42296
820
        return True
821
2511.1.12 by Michael Terry
last of the pep8 error fixes
822
    # Before we shrink the window, capture the size
823
    def pre_activate_details(self, expander):
824
        expanded = self.expander_details.get_expanded()
825
        if expanded:
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
826
            self._save_state()
2511.1.12 by Michael Terry
last of the pep8 error fixes
827
828
    def activate_details(self, expander, data):
829
        expanded = self.expander_details.get_expanded()
830
        self.settings.set_boolean("show-details", expanded)
831
        if expanded:
832
            self.on_treeview_update_cursor_changed(self.treeview_update)
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
833
        self._restore_state()
2511.1.12 by Michael Terry
last of the pep8 error fixes
834
835
    def activate_desc(self, expander, data):
836
        expanded = self.expander_desc.get_expanded()
837
        self.expander_desc.set_vexpand(expanded)
838
2622.1.2 by Dylan McCall
UpdatesAvailable is now a Dialog subclass.
839
    def on_button_install_clicked(self):
2549.1.1 by Michael Terry
stop showing Install All Available Updates quicklist item when it isn't appropriate
840
        self.unity.set_install_menuitem_visible(False)
2794 by Brian Murray
UpdateManager/UpdatesAvailable.py: Provide instructions when mount points
841
        # print("on_button_install_clicked")
2511.1.12 by Michael Terry
last of the pep8 error fixes
842
        err_sum = _("Not enough free disk space")
2794 by Brian Murray
UpdateManager/UpdatesAvailable.py: Provide instructions when mount points
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")
847
        # specific ways to resolve lack of free space
848
        remedy_archivedir = _("Remove temporary packages of former "
849
                              "installations using 'sudo apt clean'.")
2796 by Brian Murray
UpdateManager/UpdatesAvailable.py: Also mention using COMPRESS=xz in
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.")
2794 by Brian Murray
UpdateManager/UpdatesAvailable.py: Provide instructions when mount points
855
        remedy_root = _("Empty your trash and remove temporary "
856
                        "packages of former installations using "
857
                        "'sudo apt clean'.")
858
        remedy_tmp = _("Reboot to clean up files in /tmp.")
859
        remedy_usr = _("")
2511.1.12 by Michael Terry
last of the pep8 error fixes
860
        # check free space and error if its not enough
861
        try:
862
            self.cache.checkFreeSpace()
863
        except NotEnoughFreeSpaceError as e:
2794 by Brian Murray
UpdateManager/UpdatesAvailable.py: Provide instructions when mount points
864
            # CheckFreeSpace examines where packages are cached
865
            archivedir = apt_pkg.config.find_dir("Dir::Cache::archives")
866
            err_long = ""
2511.1.12 by Michael Terry
last of the pep8 error fixes
867
            for req in e.free_space_required_list:
2794 by Brian Murray
UpdateManager/UpdatesAvailable.py: Provide instructions when mount points
868
                if err_long != "":
869
                    err_long += " "
870
                if req.dir == archivedir:
871
                    err_long += err_msg % (req.size_total, req.dir,
872
                                           req.size_needed, req.dir,
873
                                           remedy_archivedir)
874
                elif req.dir == "/boot":
875
                    err_long += err_msg % (req.size_total, req.dir,
876
                                           req.size_needed, req.dir,
877
                                           remedy_boot)
878
                elif req.dir == "/":
879
                    err_long += err_msg % (req.size_total, req.dir,
880
                                           req.size_needed, req.dir,
881
                                           remedy_root)
882
                elif req.dir == "/tmp":
883
                    err_long += err_msg % (req.size_total, req.dir,
884
                                           req.size_needed, req.dir,
885
                                           remedy_tmp)
886
                elif req.dir == "/usr":
887
                    err_long += err_msg % (req.size_total, req.dir,
888
                                           req.size_needed, req.dir,
889
                                           remedy_usr)
890
            self.window_main.start_error(False, err_sum, err_long)
2511.1.12 by Michael Terry
last of the pep8 error fixes
891
            return
2853 by Brian Murray
debian/tests/control: really use pyflakes3 instead of pyflakes,
892
        except SystemError:
2511.1.12 by Michael Terry
last of the pep8 error fixes
893
            logging.exception("free space check failed")
894
        self.window_main.start_install()
895
896
    def _on_network_alert(self, watcher, state):
897
        # do not set the buttons to sensitive/insensitive until NM
898
        # can deal with dialup connections properly
899
        if state in NetworkManagerHelper.NM_STATE_CONNECTING_LIST:
900
            self.label_offline.set_text(_("Connecting..."))
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
901
            self.updates_changed()
2511.1.12 by Michael Terry
last of the pep8 error fixes
902
            self.hbox_offline.show()
903
            self.vbox_alerts.show()
904
            self.connected = False
905
        # in doubt (STATE_UNKNOWN), assume connected
2841.1.1 by Balint Reczey
Ignore PEP 8 W503 instead of E502 and drop many added backslashes
906
        elif (state in NetworkManagerHelper.NM_STATE_CONNECTED_LIST
2840.1.1 by Balint Reczey
Fix PEP 8 warnings
907
              or state == NetworkManagerHelper.NM_STATE_UNKNOWN):
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
908
            self.updates_changed()
2511.1.12 by Michael Terry
last of the pep8 error fixes
909
            self.hbox_offline.hide()
910
            self.connected = True
911
            # trigger re-showing the current app to get changelog info (if
912
            # needed)
913
            self.on_treeview_update_cursor_changed(self.treeview_update)
914
        else:
915
            self.connected = False
916
            self.label_offline.set_text(_("You may not be able to check for "
917
                                          "updates or download new updates."))
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
918
            self.updates_changed()
2511.1.12 by Michael Terry
last of the pep8 error fixes
919
            self.hbox_offline.show()
920
            self.vbox_alerts.show()
2511.1.14 by Michael Terry
auto-pep8'd some warnings
921
2511.1.12 by Michael Terry
last of the pep8 error fixes
922
    def _on_battery_alert(self, watcher, on_battery):
923
        if on_battery:
924
            self.hbox_battery.show()
925
            self.vbox_alerts.show()
926
        else:
2511.1.14 by Michael Terry
auto-pep8'd some warnings
927
            self.hbox_battery.hide()
2511.1.12 by Michael Terry
last of the pep8 error fixes
928
929
    def _on_network_3g_alert(self, watcher, on_3g, is_roaming):
930
        #print("on 3g: %s; roaming: %s" % (on_3g, is_roaming))
931
        if is_roaming:
932
            self.hbox_roaming.show()
933
            self.hbox_on_3g.hide()
934
        elif on_3g:
935
            self.hbox_on_3g.show()
936
            self.hbox_roaming.hide()
937
        else:
938
            self.hbox_on_3g.hide()
939
            self.hbox_roaming.hide()
940
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
941
    def on_update_toggled(self, renderer, path):
2511.1.12 by Michael Terry
last of the pep8 error fixes
942
        """ a toggle button in the listview was toggled """
943
        iter = self.store.get_iter(path)
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
944
        data = self.store.get_value(iter, LIST_UPDATE_DATA)
2511.1.12 by Michael Terry
last of the pep8 error fixes
945
        # make sure that we don't allow to toggle deactivated updates
946
        # this is needed for the call by the row activation callback
2590.1.1 by Michael Terry
Fix toggling items after doing a deselect-all
947
        if data.groups:
948
            self.toggle_from_items([item for group in data.groups
949
                                    for item in group.items])
950
        elif data.group:
951
            self.toggle_from_items(data.group.items)
2511.1.12 by Michael Terry
last of the pep8 error fixes
952
        else:
2590.1.1 by Michael Terry
Fix toggling items after doing a deselect-all
953
            self.toggle_from_items([data.item])
2511.1.12 by Michael Terry
last of the pep8 error fixes
954
955
    def on_treeview_update_row_activated(self, treeview, path, column, *args):
956
        """
2511.1.14 by Michael Terry
auto-pep8'd some warnings
957
        If an update row was activated (by pressing space), toggle the
2511.1.12 by Michael Terry
last of the pep8 error fixes
958
        install check box
959
        """
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
960
        self.on_update_toggled(None, path)
2511.1.12 by Michael Terry
last of the pep8 error fixes
961
2590.1.1 by Michael Terry
Fix toggling items after doing a deselect-all
962
    def toggle_from_items(self, items):
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
963
        self.setBusy(True)
964
        actiongroup = apt_pkg.ActionGroup(self.cache._depcache)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
965
2590.1.1 by Michael Terry
Fix toggling items after doing a deselect-all
966
        # Deselect all updates if any are selected
967
        keep_packages = any([item.is_selected() for item in items])
968
        for item in items:
969
            try:
2428.5.33 by Michael Terry
fix rendering and toggle behavior of header toggles
970
                if keep_packages:
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
971
                    item.pkg.mark_keep()
2685 by Michael Vogt
pep8 fixes to fix autopkgtest failure with the latest pep8
972
                elif item.pkg.name not in self.list.held_back:
2809.1.1 by Balint Reczey
Offer removal of unused autoremovable kernel packages
973
                    if not item.to_remove:
974
                        item.pkg.mark_install()
975
                    else:
976
                        item.pkg.mark_delete()
2590.1.1 by Michael Terry
Fix toggling items after doing a deselect-all
977
            except SystemError:
978
                pass
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
979
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
980
        # check if we left breakage
1849 by Michael Vogt
UpdateManager/Core/utils.py,
981
        if self.cache._depcache.broken_count:
1851 by Michael Vogt
more python0.8 API transition
982
            Fix = apt_pkg.ProblemResolver(self.cache._depcache)
983
            Fix.resolve_by_keep()
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
984
        self.updates_changed()
985
        self.treeview_update.queue_draw()
986
        del actiongroup
987
        self.setBusy(False)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
988
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
989
    def _save_state(self):
2511.1.12 by Michael Terry
last of the pep8 error fixes
990
        """ save the state  (window-size for now) """
991
        if self.expander_details.get_expanded():
992
            (w, h) = self.window_main.get_size()
993
            self.settings.set_int("window-width", w)
994
            self.settings.set_int("window-height", h)
995
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
996
    def _restore_state(self):
2511.1.12 by Michael Terry
last of the pep8 error fixes
997
        """ restore the state (window-size for now) """
998
        w = self.settings.get_int("window-width")
999
        h = self.settings.get_int("window-height")
1000
        expanded = self.expander_details.get_expanded()
2622.1.7 by Dylan McCall
Every GUI component is now a Dialog subclass, inserted as a widget into the main window, by the main window.
1001
        if expanded:
1002
            self.window_main.begin_user_resizable(w, h)
1003
        else:
1004
            self.window_main.end_user_resizable()
2511.1.12 by Michael Terry
last of the pep8 error fixes
1005
        return False
1006
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
1007
    def _add_header(self, name, groups):
1008
        total_size = 0
1009
        for group in groups:
1010
            total_size = total_size + group.get_total_size()
1011
        header_row = [
1012
            name,
1013
            UpdateData(groups, None, None),
1014
            humanize_size(total_size),
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
1015
            True,
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
1016
            True
1017
        ]
1018
        return self.store.append(None, header_row)
1019
1020
    def _add_groups(self, groups):
1021
        # Each row contains:
1022
        #  row label (for screen reader),
1023
        #  update data tuple (is_toplevel, group object, package object),
1024
        #  update size,
1025
        #  update selection state
1026
        for group in groups:
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
1027
            if not group.items:
2428.5.25 by Michael Terry
don't show empty groups (stops 'Ubuntu base' from always appearing)
1028
                continue
1029
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
1030
            group_is_item = None
2428.5.29 by Michael Terry
show changelog details for group of one app
1031
            if not isinstance(group, UpdateSystemGroup) and \
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
1032
                    len(group.items) == 1:
1033
                group_is_item = group.items[0]
2428.5.29 by Michael Terry
show changelog details for group of one app
1034
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
1035
            group_row = [
1036
                group.name,
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
1037
                UpdateData(None, group, group_is_item),
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
1038
                humanize_size(group.get_total_size()),
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
1039
                True,
1040
                group.sensitive
2428.5.23 by Michael Terry
make tests pass again
1041
            ]
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
1042
            group_iter = self.store.append(None, group_row)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
1043
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
1044
            if group_is_item:
2428.5.26 by Michael Terry
don't show children for groups of size 1
1045
                continue
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
1046
            for item in group.items:
1047
                item_row = [
1048
                    item.name,
1049
                    UpdateData(None, None, item),
1050
                    humanize_size(getattr(item.pkg.candidate, "size", 0)),
2947.1.3 by Robert Ancell
Make Ubuntu pro updates insensitive
1051
                    True,
1052
                    group.sensitive
2428.5.23 by Michael Terry
make tests pass again
1053
                ]
2428.5.45 by Michael Terry
use a thin wrapper object around pkgs in groups to make it easier for UpdateList to sort and provide labels for sub packages
1054
                self.store.append(group_iter, item_row)
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
1055
2622.1.1 by Dylan McCall
Defensive coding around package objects in UpdateList and UpdatesAvailable.
1056
    def set_update_list(self, update_list):
1057
        self.list = update_list
1058
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
1059
        # use the watch cursor
1060
        self.setBusy(True)
1061
        # disconnect the view first
1062
        self.treeview_update.set_model(None)
1063
        self.store.clear()
1064
        # clean most objects
1065
        self.dl_size = 0
1066
1067
        self.scrolledwindow_update.show()
1068
1069
        # add security and update groups to self.store
2893.2.1 by Iain Lane
List OEM metapackages
1070
        if self.list.oem_groups:
2897 by Julian Andres Klode
Make pycodestyle happy
1071
            self._add_header(_("Improved hardware support"),
1072
                             self.list.oem_groups)
2893.2.1 by Iain Lane
List OEM metapackages
1073
            self._add_groups(self.list.oem_groups)
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
1074
        if self.list.security_groups:
1075
            self._add_header(_("Security updates"), self.list.security_groups)
1076
            self._add_groups(self.list.security_groups)
2897 by Julian Andres Klode
Make pycodestyle happy
1077
        if ((self.list.security_groups or self.list.oem_groups)
1078
                and self.list.update_groups):
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
1079
            self._add_header(_("Other updates"), self.list.update_groups)
2865 by Marcus Tomlinson
UpdateManager/backend/__init__.py: once apt upgrade completes, use the deb2snap.json file from ubuntu-release-upgrader to perform snap updates.
1080
        elif self.list.update_groups and (self.list.kernel_autoremove_groups
1081
                                          or self.list.duplicate_groups):
2809.1.1 by Balint Reczey
Offer removal of unused autoremovable kernel packages
1082
            self._add_header(_("Updates"), self.list.update_groups)
2428.5.31 by Michael Terry
Add security groupings and header (doesn't look like spec yet, but behavior is there)
1083
        if self.list.update_groups:
1084
            self._add_groups(self.list.update_groups)
2809.1.1 by Balint Reczey
Offer removal of unused autoremovable kernel packages
1085
        if self.list.kernel_autoremove_groups:
1086
            self._add_header(
1087
                _("Unused kernel updates to be removed"),
1088
                self.list.kernel_autoremove_groups)
1089
            self._add_groups(self.list.kernel_autoremove_groups)
2865 by Marcus Tomlinson
UpdateManager/backend/__init__.py: once apt upgrade completes, use the deb2snap.json file from ubuntu-release-upgrader to perform snap updates.
1090
        if self.list.duplicate_groups:
1091
            self._add_header(
1092
                _("Duplicate packages to be removed"),
1093
                self.list.duplicate_groups)
1094
            self._add_groups(self.list.duplicate_groups)
2947.1.1 by Robert Ancell
Show Ubuntu Pro updates that would be allowed
1095
        if self.list.ubuntu_pro_groups:
1096
            self._add_groups(self.list.ubuntu_pro_groups)
2428.5.16 by Dylan McCall
Fixed unintended formatting changes.
1097
2511.1.12 by Michael Terry
last of the pep8 error fixes
1098
        self.treeview_update.set_model(self.store)
2809.1.1 by Balint Reczey
Offer removal of unused autoremovable kernel packages
1099
        self.pkg_cell_area.indent_toplevel = (
2841.1.1 by Balint Reczey
Ignore PEP 8 W503 instead of E502 and drop many added backslashes
1100
            bool(self.list.security_groups)
2865 by Marcus Tomlinson
UpdateManager/backend/__init__.py: once apt upgrade completes, use the deb2snap.json file from ubuntu-release-upgrader to perform snap updates.
1101
            or bool(self.list.kernel_autoremove_groups)
1102
            or bool(self.list.duplicate_groups))
2622.1.1 by Dylan McCall
Defensive coding around package objects in UpdateList and UpdatesAvailable.
1103
        self.update_close_button()
2511.1.12 by Michael Terry
last of the pep8 error fixes
1104
        self.update_count()
1105
        self.setBusy(False)
1106
        while Gtk.events_pending():
1107
            Gtk.main_iteration()
2428.5.9 by Dylan McCall
Merged changes from trunk. Manually merged UpdateManager/UpdatesAvailable.py and UpdateManager/Core/UpdateList.py.
1108
        self.updates_changed()
2511.1.12 by Michael Terry
last of the pep8 error fixes
1109
        return False