From 2ddd3b4d0f480f45cd9d100e9e80902b9783c6b8 Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Tue, 31 Jul 2012 19:35:35 +0200 Subject: [PATCH] [Rigo] implement a Bottom NotificationBox "Activity" button This Button allows users to see the current Application management queue by leveraging the new RigoDaemon API. --- rigo/rigo/models/application.py | 32 +++++++++-- rigo/rigo/ui/gtk3/controllers/applications.py | 53 ++++++++++++++++++- .../rigo/ui/gtk3/controllers/notifications.py | 12 +++++ rigo/rigo/ui/gtk3/models/appliststore.py | 44 ++++++++++++++- rigo/rigo_app.py | 17 +++--- 5 files changed, 143 insertions(+), 15 deletions(-) diff --git a/rigo/rigo/models/application.py b/rigo/rigo/models/application.py index d8da143ac..78de9c50b 100644 --- a/rigo/rigo/models/application.py +++ b/rigo/rigo/models/application.py @@ -856,7 +856,8 @@ class Application(object): return self._licenses def __init__(self, entropy_client, entropy_ws, rigo_service, - package_match, redraw_callback=None, package_path=None): + package_match, redraw_callback=None, package_path=None, + children=None, vanished_callback=None): self._entropy = entropy_client self._entropy_ws = entropy_ws self._service = rigo_service @@ -864,6 +865,8 @@ class Application(object): self._pkg_id, self._repo_id = package_match self._path = package_path self._redraw_callback = redraw_callback + self._children = children + self._vanished_callback = vanished_callback @property def name(self): @@ -873,6 +876,8 @@ class Application(object): repo = self._entropy.open_repository(self._repo_id) name = repo.retrieveName(self._pkg_id) if name is None: + if self._vanished_callback is not None: + self._vanished_callback(self) return _("N/A") name = " ".join([x.capitalize() for x in \ name.replace("-"," ").split()]) @@ -888,6 +893,15 @@ class Application(object): """ return self._path + @property + def children(self): + """ + If the Application is the parent of other Applications, + this object shall contain a list of children Application + objects. Otherwise, it returns None. + """ + return self._children + def is_installed(self): """ Return if Application is currently installed. @@ -931,9 +945,7 @@ class Application(object): # in the installed packages repository, matches # must be of length < 2. if len(matches) > 1: - raise AttributeError( - "searchKeySlot for %s returned: %s" % ( - (key, slot), matches,)) + return None if not matches: # not installed return None @@ -1232,6 +1244,10 @@ class Application(object): self._entropy.rwsem().reader_acquire() try: repo = self._entropy.open_repository(self._repo_id) + if self._vanished_callback is not None: + if not repo.isPackageIdAvailable(self._pkg_id): + self._vanished_callback(self) + version = repo.retrieveVersion(self._pkg_id) if version is None: version = _("N/A") @@ -1271,6 +1287,10 @@ class Application(object): self._entropy.rwsem().reader_acquire() try: repo = self._entropy.open_repository(self._repo_id) + if self._vanished_callback is not None: + if not repo.isPackageIdAvailable(self._pkg_id): + self._vanished_callback(self) + inst_repo = self._entropy.installed_repository() strict = repo.getStrictData(self._pkg_id) if strict is None: @@ -1347,6 +1367,10 @@ class Application(object): lic_url = "%s/license/" % (etpConst['packages_website_url'],) repo = self._entropy.open_repository(self._repo_id) + if self._vanished_callback is not None: + if not repo.isPackageIdAvailable(self._pkg_id): + self._vanished_callback(self) + licenses = repo.retrieveLicense(self._pkg_id) if licenses: licenses_txt = "%s: " % (escape_markup(_("License")),) diff --git a/rigo/rigo/ui/gtk3/controllers/applications.py b/rigo/rigo/ui/gtk3/controllers/applications.py index fe126c6f5..c033124fd 100644 --- a/rigo/rigo/ui/gtk3/controllers/applications.py +++ b/rigo/rigo/ui/gtk3/controllers/applications.py @@ -78,9 +78,10 @@ class ApplicationsViewController(GObject.Object): MIN_RECENT_SEARCH_KEY_LEN = 2 SHOW_INSTALLED_KEY = "in:installed" + SHOW_QUEUE_KEY = "in:queue" def __init__(self, activity_rwsem, entropy_client, entropy_ws, - nc, rigo_service, prefc, icons, nf_box, + nc, bottom_nc, rigo_service, prefc, icons, nf_box, search_entry, search_entry_completion, search_entry_store, store, view): GObject.Object.__init__(self) @@ -97,6 +98,7 @@ class ApplicationsViewController(GObject.Object): self._not_found_search_box = None self._not_found_label = None self._nc = nc + self._bottom_nc = bottom_nc self._prefc = prefc self._cacher = EntropyCacher() @@ -333,6 +335,39 @@ class ApplicationsViewController(GObject.Object): __name__, "upgrade:" " upgrade_system() sent") + def _show_action_queue_items(self, _invalid_matches=False): + """ + Request the UI to show the current Action Queue, if any. + """ + const_debug_write( + __name__, "_show_action_queue_items called") + apps = self._service.action_queue_items() + const_debug_write( + __name__, "_show_action_queue_items, items: %d" % (len(apps),)) + + matches = [] + if not _invalid_matches: + for app in apps: + const_debug_write( + __name__, "_show_action_queue_items:" + " %s" % (app,)) + matches.append(app.get_details().pkg) + else: + self._entropy.rwsem().reader_acquire() + try: + inst_repo = self._entropy.installed_repository() + repo_name = inst_repo.repository_id() + matches.extend( + [(-2, repo_name), + (-5, repo_name), + (-10, repo_name)]) + finally: + self._entropy.rwsem().reader_release() + + if matches: + self.set_many_safe(matches, + _from_search=ApplicationsViewController.SHOW_QUEUE_KEY) + def __simulate_orphaned_apps(self, text): const_debug_write( @@ -368,6 +403,9 @@ class ApplicationsViewController(GObject.Object): elif text == "in:confupdate": self._service.configuration_updates() return + elif text == ApplicationsViewController.SHOW_QUEUE_KEY: + self._show_action_queue_items() + return elif text == "in:config": GLib.idle_add(self.emit, "view-want-change", RigoViewStates.PREFERENCES_VIEW_STATE, @@ -403,9 +441,12 @@ class ApplicationsViewController(GObject.Object): if sim_str: self.__simulate_orphaned_apps(sim_str) return - if text == "in:simulate:u": + elif text == "in:simulate:u": self.upgrade(simulate=True) return + elif text == "in:simulate:v": + self._show_action_queue_items(_invalid_matches=True) + return return self.__search_thread_body(text) @@ -655,6 +696,14 @@ class ApplicationsViewController(GObject.Object): "drive-harddisk", _show_installed) self._prefc.append(pref) + def _show_queue_view(widget): + self._search(ApplicationsViewController.SHOW_QUEUE_KEY, + _force=True) + self._bottom_nc.connect("show-queue-view", _show_queue_view) + def _clear(widget): + self.clear() + self._store.connect("all-vanished", _clear) + self._view.set_model(self._store) self._search_entry.connect( "changed", self._search_changed) diff --git a/rigo/rigo/ui/gtk3/controllers/notifications.py b/rigo/rigo/ui/gtk3/controllers/notifications.py index bf08bba09..4c2e4132b 100644 --- a/rigo/rigo/ui/gtk3/controllers/notifications.py +++ b/rigo/rigo/ui/gtk3/controllers/notifications.py @@ -181,6 +181,10 @@ class BottomNotificationViewController(NotificationViewController): None, tuple(), ), + "show-queue-view" : (GObject.SignalFlags.RUN_LAST, + None, + tuple(), + ), "work-interrupt" : (GObject.SignalFlags.RUN_LAST, None, tuple(), @@ -206,6 +210,12 @@ class BottomNotificationViewController(NotificationViewController): """ self.emit("work-interrupt") + def _on_show_activity(self, widget): + """ + User is asking to show the Application Queue. + """ + self.emit("show-queue-view") + def _append_repositories_update(self): """ Add a NotificationBox related to Repositories Update @@ -232,6 +242,7 @@ class BottomNotificationViewController(NotificationViewController): w, h = self._window.get_size() self._window.resize(w, 1) box.add_button(_("Show me"), _show_me) + box.add_button(_("Activity"), self._on_show_activity) box.add_button(_("Interrupt"), self._on_work_interrupt) self.append(box) @@ -249,6 +260,7 @@ class BottomNotificationViewController(NotificationViewController): w, h = self._window.get_size() self._window.resize(w, 1) box.add_button(_("Show me"), _show_me) + box.add_button(_("Activity"), self._on_show_activity) box.add_button(_("Interrupt"), self._on_work_interrupt) self.append(box) diff --git a/rigo/rigo/ui/gtk3/models/appliststore.py b/rigo/rigo/ui/gtk3/models/appliststore.py index ed2021cd9..aecfbe996 100644 --- a/rigo/rigo/ui/gtk3/models/appliststore.py +++ b/rigo/rigo/ui/gtk3/models/appliststore.py @@ -50,6 +50,12 @@ class AppListStore(Gtk.ListStore): None, (GObject.TYPE_PYOBJECT,), ), + # signal that all the elements in the List + # have vanished. + "all-vanished" : (GObject.SignalFlags.RUN_LAST, + None, + tuple(), + ), } def __init__(self, entropy_client, entropy_ws, rigo_service, @@ -194,6 +200,41 @@ class AppListStore(Gtk.ListStore): AppListStore._ICON_CACHE[pkg_match] = pixbuf return pixbuf + def _vanished_callback(self, app): + """ + Remove elements that are marked as "vanished" due + to unavailable metadata. + """ + def _remove(_app): + pkg_match = _app.get_details().pkg + + vis_data = self._view.get_visible_range() + if vis_data is None: + return + if len(vis_data) == 2: + # Gtk 3.4 + valid_paths = True + start_path, end_path = vis_data + else: + # Gtk <3.2 + valid_paths, start_path, end_path = vis_data + + if not valid_paths: + return + + path = start_path + while path <= end_path: + path_iter = self.get_iter(path) + if self.iter_is_valid(path_iter): + visible_pkg_match = self.get_value(path_iter, 0) + if visible_pkg_match == pkg_match: + self.remove(path_iter) + if len(self) == 0: + self.emit("all-vanished") + return + + GLib.idle_add(_remove, app) + def get_application(self, pkg_match): def _ui_redraw_callback(*args): if const_debug_enabled(): @@ -203,5 +244,6 @@ class AppListStore(Gtk.ListStore): app = Application(self._entropy, self._entropy_ws, self._service, pkg_match, - redraw_callback=_ui_redraw_callback) + redraw_callback=_ui_redraw_callback, + vanished_callback=self._vanished_callback) return app diff --git a/rigo/rigo_app.py b/rigo/rigo_app.py index fe790922b..74eba9ab1 100644 --- a/rigo/rigo_app.py +++ b/rigo/rigo_app.py @@ -291,10 +291,18 @@ class Rigo(Gtk.Application): self._nc = UpperNotificationViewController( self._entropy, self._entropy_ws, self._notification) + # Bottom NotificationBox controller. + # Bottom notifications are only used for + # providing Activity control to User during + # the Activity itself. + self._bottom_nc = BottomNotificationViewController( + self._window, self._bottom_notification, + self._pref_button) self._avc = ApplicationsViewController( self._activity_rwsem, - self._entropy, self._entropy_ws, self._nc, self._service, + self._entropy, self._entropy_ws, + self._nc, self._bottom_nc, self._service, self._pref_view_c, icons, self._not_found_box, self._search_entry, self._search_entry_completion, self._search_entry_store, self._app_store, self._view) @@ -303,13 +311,6 @@ class Rigo(Gtk.Application): self._avc.connect("view-filled", self._on_view_filled) self._avc.connect("view-want-change", self._on_view_change) - # Bottom NotificationBox controller. - # Bottom notifications are only used for - # providing Activity control to User during - # the Activity itself. - self._bottom_nc = BottomNotificationViewController( - self._window, self._bottom_notification, - self._pref_button) self._service.set_bottom_notification_controller( self._bottom_nc)