class Rigo(Gtk.Application): class RigoHandler(object): def __init__(self, rigo_app, rigo_service): self._app = rigo_app self._service = rigo_service def onDeleteWindow(self, window, event): # if UI is locked, do not allow to close Rigo if self._app.is_ui_locked() or \ self._service.local_activity() != LocalActivityStates.READY: rc = self._app._show_yesno_dialog( None, escape_markup(_("Hey hey hey!")), escape_markup(_("Rigo is working, are you sure?"))) if rc == Gtk.ResponseType.NO: return True while True: try: entropy.tools.kill_threads() Gtk.main_quit((window, event)) except KeyboardInterrupt: continue break def __init__(self): self._current_state_lock = False self._current_state = RigoViewStates.STATIC_VIEW_STATE self._state_transitions = { RigoViewStates.BROWSER_VIEW_STATE: ( self._enter_browser_state, self._exit_browser_state), RigoViewStates.STATIC_VIEW_STATE: ( self._enter_static_state, self._exit_static_state), RigoViewStates.APPLICATION_VIEW_STATE: ( self._enter_application_state, self._exit_application_state), RigoViewStates.WORK_VIEW_STATE: ( self._enter_work_state, self._exit_work_state), RigoViewStates.CONFUPDATES_VIEW_STATE: ( self._enter_confupdates_state, self._exit_confupdates_state), RigoViewStates.NOTICEBOARD_VIEW_STATE: ( self._enter_noticeboard_state, self._exit_noticeboard_state), RigoViewStates.PREFERENCES_VIEW_STATE: ( self._enter_preferences_state, self._exit_preferences_state), RigoViewStates.REPOSITORY_VIEW_STATE: ( self._enter_repository_state, self._exit_repository_state), RigoViewStates.GROUPS_VIEW_STATE: ( self._enter_groups_state, self._exit_groups_state) } self._state_metadata = { RigoViewStates.BROWSER_VIEW_STATE: { "title": _("Search"), }, RigoViewStates.STATIC_VIEW_STATE: { "title": _("Rigo Application Browser"), }, RigoViewStates.APPLICATION_VIEW_STATE: { "title": _("Application"), }, RigoViewStates.WORK_VIEW_STATE: { "title": _("Working Hard"), }, RigoViewStates.CONFUPDATES_VIEW_STATE: { "title": _("Wake Up"), }, RigoViewStates.NOTICEBOARD_VIEW_STATE: { "title": _("Important Stuff"), }, RigoViewStates.PREFERENCES_VIEW_STATE: { "title": _("Breaking Stuff"), }, RigoViewStates.REPOSITORY_VIEW_STATE: { "title": _("Repository Stuff"), }, RigoViewStates.GROUPS_VIEW_STATE: { "title": _("Application Groups"), }, } self._state_mutex = Lock() icons = get_sc_icon_theme(DATA_DIR) self._activity_rwsem = ReadersWritersSemaphore() # This relies on the fact that the installed packages repository # is lazily loaded (thus, schema update code is). self._entropy = Client() self._entropy_ws = EntropyWebService(self._entropy) preload_task = ParallelTask(self._entropy_ws.preload) preload_task.name = "PreloadEntropyWebService" preload_task.daemon = True preload_task.start() self._service = RigoServiceController( self, self._activity_rwsem, self._entropy, self._entropy_ws) app_handler = Rigo.RigoHandler(self, self._service) self._builder = Gtk.Builder() self._builder.add_from_file(os.path.join(DATA_DIR, "ui/gtk3/rigo.ui")) self._builder.connect_signals(app_handler) self._window = self._builder.get_object("rigoWindow") self._window.set_name("rigo-view") self._apps_view = self._builder.get_object("appsViewVbox") self._scrolled_view = self._builder.get_object("appsViewScrolledWindow") self._app_view = self._builder.get_object("appViewScrollWin") self._app_view.set_name("rigo-view") self._app_view_port = self._builder.get_object("appViewVport") self._app_view_port.set_name("rigo-view") self._not_found_box = self._builder.get_object("appsViewNotFoundVbox") self._config_scrolled_view = self._builder.get_object( "configViewScrolledWindow") self._config_view = self._builder.get_object("configViewVbox") self._config_view.set_name("rigo-view") self._repo_scrolled_view = self._builder.get_object( "repoViewScrolledWindow") self._repo_view = self._builder.get_object("repoViewVbox") self._repo_view.set_name("rigo-view") self._notice_scrolled_view = self._builder.get_object( "noticeViewScrolledWindow") self._notice_view = self._builder.get_object("noticeViewVbox") self._notice_view.set_name("rigo-view") self._pref_scrolled_view = self._builder.get_object( "preferencesViewScrolledWindow") self._pref_view = self._builder.get_object("preferencesViewVbox") self._pref_view.set_name("rigo-view") self._group_scrolled_view = self._builder.get_object( "groupViewScrolledWindow") self._group_view = self._builder.get_object("groupViewVbox") self._group_view.set_name("rigo-view") self._search_entry = self._builder.get_object("searchEntry") self._search_entry_completion = self._builder.get_object( "searchEntryCompletion") self._search_entry_store = self._builder.get_object( "searchEntryStore") self._static_view = self._builder.get_object("staticViewVbox") self._notification = self._builder.get_object("notificationBox") self._bottom_notification = \ self._builder.get_object("bottomNotificationBox") self._work_view = self._builder.get_object("workViewVbox") self._work_view.set_name("rigo-view") self._pref_button = self._builder.get_object( "prefButton") def _pref_button_activate(widget): self._change_view_state( RigoViewStates.PREFERENCES_VIEW_STATE) self._pref_button.connect( "clicked", _pref_button_activate) # Preferences model, view and controller self._pref_store = PreferencesListStore() self._view_pref = PreferencesTreeView( icons, PreferencesListStore.ICON_SIZE) self._pref_scrolled_view.add(self._view_pref) def _pref_queue_draw(*args): self._view_pref.queue_draw() self._pref_store.connect("redraw-request", _pref_queue_draw) self._pref_view_c = PreferenceViewController( self._pref_store, self._view_pref) self._app_view_c = ApplicationViewController( self._entropy, self._entropy_ws, self._pref_view_c, self._service, self._builder) self._view = AppTreeView( self._entropy, self._service, self._app_view_c, icons, True, AppListStore.ICON_SIZE, store=None) self._scrolled_view.add(self._view) self._view.set_scrolled_view(self._scrolled_view) self._app_store = AppListStore( self._entropy, self._entropy_ws, self._service, self._view, icons) def _queue_draw(*args): self._view.queue_draw() self._app_store.connect("redraw-request", _queue_draw) self._app_view_c.set_store(self._app_store) self._app_view_c.connect("application-show", self._on_application_show) # Configuration file updates model, view and controller self._config_store = ConfigUpdatesListStore() self._view_config = ConfigUpdatesTreeView( icons, ConfigUpdatesListStore.ICON_SIZE) self._config_scrolled_view.add(self._view_config) def _config_queue_draw(*args): self._view_config.queue_draw() self._config_store.connect("redraw-request", _config_queue_draw) self._config_view_c = ConfigUpdatesViewController( self._entropy, self._config_store, self._view_config) self._config_view_c.connect( "view-cleared", self._on_view_cleared) self._service.set_configuration_controller(self._config_view_c) # Repository model, view and controller self._repo_store = RepositoryListStore() self._view_repo = RepositoryTreeView( icons, RepositoryListStore.ICON_SIZE) self._repo_scrolled_view.add(self._view_repo) def _repo_queue_draw(*args): self._view_repo.queue_draw() self._repo_store.connect("redraw-request", _repo_queue_draw) self._repo_view_c = RepositoryViewController( self._pref_view_c, self._service, self._repo_store, self._view_repo) # NoticeBoard model, view and controller self._notice_store = NoticeBoardListStore() self._view_notice = NoticeBoardTreeView( icons, NoticeBoardListStore.ICON_SIZE) self._notice_scrolled_view.add(self._view_notice) def _notice_queue_draw(*args): self._view_notice.queue_draw() self._notice_store.connect("redraw-request", _notice_queue_draw) self._notice_view_c = NoticeBoardViewController( self._notice_store, self._view_notice) self._service.set_noticeboard_controller(self._notice_view_c) # Group model, view and controller self._group_store = GroupListStore() self._view_group = GroupTreeView( icons, GroupListStore.ICON_SIZE) self._group_scrolled_view.add(self._view_group) def _group_queue_draw(*args): self._view_group.queue_draw() self._group_store.connect("redraw-request", _group_queue_draw) self._group_view_c = GroupViewController( self._service, self._group_store, self._view_group, self._pref_view_c) self._welcome_box = WelcomeBox() settings = Gtk.Settings.get_default() settings.set_property("gtk-error-bell", False) # wire up the css provider to reconfigure on theme-changes self._window.connect("style-updated", self._on_style_updated, init_sc_css_provider, settings, Gdk.Screen.get_default(), DATA_DIR) # Force the initialization of the css provider asap. # This fixes a glitch with GTK 3.10 init_sc_css_provider( self._window, settings, Gdk.Screen.get_default(), DATA_DIR) 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._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) self._avc.connect("view-cleared", self._on_view_cleared) self._avc.connect("view-filled", self._on_view_filled) self._avc.connect("view-want-change", self._on_view_change) self._service.set_bottom_notification_controller( self._bottom_nc) self._app_view_c.set_notification_controller(self._nc) self._app_view_c.set_applications_controller(self._avc) self._config_view_c.set_notification_controller(self._nc) self._config_view_c.set_applications_controller(self._avc) self._repo_view_c.set_notification_controller(self._nc) self._repo_view_c.set_applications_controller(self._avc) self._notice_view_c.set_notification_controller(self._nc) self._notice_view_c.set_applications_controller(self._avc) self._group_view_c.set_applications_controller(self._avc) self._service.set_applications_controller(self._avc) self._service.set_application_controller(self._app_view_c) self._service.set_notification_controller(self._nc) self._service.connect("start-working", self._on_start_working) self._service.connect("repositories-updated", self._on_repo_updated) self._service.connect("applications-managed", self._on_applications_managed) self._work_view_c = WorkViewController( icons, self._service, self._work_view) self._service.set_work_controller(self._work_view_c) self._bottom_nc.connect("show-work-view", self._on_show_work_view) self._bottom_nc.connect("work-interrupt", self._on_work_interrupt) def is_ui_locked(self): """ Return whether the UI is currently locked. """ return self._current_state_lock def _thread_dumper(self): """ If --dumper is in argv, a recurring thread dump function will be spawned every 30 seconds. """ dumper_enable = self._nsargs.dumper if dumper_enable: task = None def _dumper(): def _dump(): task.kill() dump_signal(None, None) timer = Timer(10.0, _dump) timer.name = "MainThreadHearthbeatCheck" timer.daemon = True timer.start() GLib.idle_add(timer.cancel) task = TimeScheduled(5.0, _dumper) task.name = "ThreadDumper" task.daemon = True task.start() def _on_start_working(self, widget, state, lock): """ Emitted by RigoServiceController when we're asked to switch to the Work View and, if lock = True, lock UI. """ if lock: self._search_entry.set_sensitive(False) if state is not None: self._change_view_state(state, lock=lock) def _on_work_interrupt(self, widget): """ We've been explicitly asked to interrupt the currently ongoing work """ rc = self._show_yesno_dialog( self._window, escape_markup(_("Activity Interruption")), escape_markup( _("Are you sure you want to interrupt" " the ongoing Activity? The interruption will" " occur as soon as possible, potentially not" " immediately."))) if rc == Gtk.ResponseType.NO: return self._service.interrupt_activity() def _on_show_work_view(self, widget): """ We've been explicitly asked to switch to WORK_VIEW_STATE """ self._change_view_state(RigoViewStates.WORK_VIEW_STATE, _ignore_lock=True) def _on_repo_updated(self, widget, result, message): """ Emitted by RigoServiceController telling us that repositories have been updated. """ with self._state_mutex: self._current_state_lock = False self._search_entry.set_sensitive(True) if result != 0: msg = "<b>%s</b>: %s" % ( _("Repositories update error"), message,) message_type = Gtk.MessageType.ERROR else: msg = _("Repositories updated <b>successfully</b>!") message_type = Gtk.MessageType.INFO box = NotificationBox( msg, message_type=message_type, context_id=RigoServiceController.NOTIFICATION_CONTEXT_ID) box.add_destroy_button(_("Ok, thanks")) self._nc.append(box) def _on_applications_managed(self, widget, success, local_activity): """ Emitted by RigoServiceController telling us that enqueue application actions have been completed. """ msg = "N/A" if not success: if local_activity == LocalActivityStates.MANAGING_APPLICATIONS: msg = "<b>%s</b>: %s" % ( _("Application Management Error"), _("please check the management log"),) elif local_activity == LocalActivityStates.UPGRADING_SYSTEM: msg = "<b>%s</b>: %s" % ( _("System Upgrade Error"), _("please check the upgrade log"),) message_type = Gtk.MessageType.ERROR else: if local_activity == LocalActivityStates.MANAGING_APPLICATIONS: msg = _("Applications managed <b>successfully</b>!") elif local_activity == LocalActivityStates.UPGRADING_SYSTEM: msg = _("System Upgraded <b>successfully</b>!") message_type = Gtk.MessageType.INFO box = NotificationBox( msg, message_type=message_type, context_id=RigoServiceController.NOTIFICATION_CONTEXT_ID) box.add_destroy_button(_("Ok, thanks")) box.add_button(_("Show me"), self._on_show_work_view) self._nc.append(box) self._work_view_c.deactivate_app_box() def _on_view_cleared(self, *args): self._change_view_state(RigoViewStates.STATIC_VIEW_STATE) def _on_view_filled(self, *args): self._change_view_state(RigoViewStates.BROWSER_VIEW_STATE) def _on_view_change(self, widget, state, payload): self._change_view_state(state, payload=payload) def _on_application_show(self, *args): self._change_view_state(RigoViewStates.APPLICATION_VIEW_STATE) def _exit_browser_state(self): """ Action triggered when UI exits the Application Browser state (or mode). """ self._avc.deselect() self._apps_view.hide() def _enter_browser_state(self): """ Action triggered when UI exits the Application Browser state (or mode). """ self._apps_view.show() def _exit_confupdates_state(self): """ Action triggered when UI exits the Configuration Updates state (or mode). """ self._config_view.hide() def _enter_confupdates_state(self): """ Action triggered when UI enters the Configuration Updates state (or mode). """ self._config_view.show() def _exit_noticeboard_state(self): """ Action triggered when UI exits the NoticeBoard state (or mode). """ self._notice_view.hide() def _enter_noticeboard_state(self): """ Action triggered when UI enters the NoticeBoard state (or mode). """ self._notice_view.show() def _exit_repository_state(self): """ Action triggered when UI exits the Repository Management state (or mode). """ self._repo_view.hide() self._repo_view_c.clear() def _enter_repository_state(self): """ Action triggered when UI enters the Repository Management state (or mode). """ self._repo_view_c.load() self._repo_view.show() def _exit_preferences_state(self): """ Action triggered when UI exits the Preferences state (or mode). """ self._pref_view.hide() def _enter_preferences_state(self): """ Action triggered when UI enters the Preferences state (or mode). """ self._pref_view.show() def _exit_groups_state(self): """ Action triggered when UI exits the Groups state (or mode). """ self._group_view.hide() def _enter_groups_state(self): """ Action triggered when UI enters the Groups state (or mode). """ self._group_view_c.load() self._group_view.show() def _exit_static_state(self): """ Action triggered when UI exits the Static Browser state (or mode). AKA the Welcome Box. """ self._static_view.hide() # release all the childrens of static_view for child in self._static_view.get_children(): self._static_view.remove(child) def _enter_static_state(self): """ Action triggered when UI exits the Static Browser state (or mode). AKA the Welcome Box. """ # keep the current widget if any, or add the # welcome widget if not self._static_view.get_children(): self._welcome_box.show() self._static_view.pack_start(self._welcome_box, True, True, 10) self._static_view.show() def _enter_application_state(self): """ Action triggered when UI enters the Package Information state (or mode). Showing application information. """ # change search_entry first icon to emphasize the # back action self._search_entry.set_icon_from_stock( Gtk.EntryIconPosition.PRIMARY, "gtk-go-back") self._app_view.show() def _exit_application_state(self): """ Action triggered when UI exits the Package Information state (or mode). Hiding back application information. """ self._search_entry.set_icon_from_stock( Gtk.EntryIconPosition.PRIMARY, "gtk-find") self._app_view.hide() self._app_view_c.hide() def _enter_work_state(self): """ Action triggered when UI enters the Work View state (or mode). Either for Updating Repositories or Installing new Apps. """ self._work_view.show() def _exit_work_state(self): """ Action triggered when UI exits the Work View state (or mode). """ self._work_view.hide() def _change_view_state(self, state, lock=False, _ignore_lock=False, payload=None): """ Change Rigo Application UI state. You can pass a custom widget that will be shown in case of static view state. """ with self._state_mutex: if self._current_state_lock and not _ignore_lock: const_debug_write( __name__, "cannot change view state, UI locked") return False txc = self._state_transitions.get(state) if txc is None: raise AttributeError("wrong view state") enter_st, exit_st = txc current_enter_st, current_exit_st = \ self._state_transitions.get( self._current_state) # exit from current state current_exit_st() # enter the new state enter_st() self._current_state = state if lock: self._current_state_lock = True state_meta = self._state_metadata[state] self._window.set_title(escape_markup( state_meta["title"])) return True def _change_view_state_safe(self, state): """ Thread-safe version of change_view_state(). """ def _do_change(): return self._change_view_state(state) GLib.idle_add(_do_change) def _on_style_updated(self, widget, init_css_callback, *args): """ Gtk Style callback, nothing to see here. """ init_css_callback(widget, *args) def _show_ok_dialog(self, parent, title, message): """ Show ugly OK dialog window. """ dlg = Gtk.MessageDialog(parent=parent, type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK) dlg.set_markup(message) dlg.set_title(title) dlg.run() dlg.destroy() def _show_yesno_dialog(self, parent, title, message): """ Show ugly Yes/No dialog window. """ dlg = Gtk.MessageDialog(parent=parent, type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.YES_NO) dlg.set_markup(message) dlg.set_title(title) rc = dlg.run() dlg.destroy() return rc def _permissions_setup(self): """ Check execution privileges and spawn the Rigo UI. """ if not entropy.tools.is_user_in_entropy_group(): # otherwise the lock handling would potentially # fail. self._show_ok_dialog( None, escape_markup(_("Not authorized")), escape_markup(_("You are not authorized to run Rigo"))) entropy.tools.kill_threads() Gtk.main_quit() return if not self._service.service_available(): self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(_("RigoDaemon service is not available"))) entropy.tools.kill_threads() Gtk.main_quit() return supported_apis = self._service.supported_apis() daemon_api = self._service.api() if daemon_api not in supported_apis: self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup( _("API mismatch, please update Rigo and RigoDaemon"))) entropy.tools.kill_threads() Gtk.main_quit() return lock = EntropyResourcesLock(output=self._entropy) # always execute this from the MainThread, since the lock uses TLS acquired = lock.try_acquire_shared() is_exclusive = False if not acquired: # check whether RigoDaemon is running in excluive mode # and ignore non-atomicity here (failing with error # is acceptable) if not self._service.exclusive(): self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(_("Another Application Manager is active"))) entropy.tools.kill_threads() Gtk.main_quit() return is_exclusive = True # otherwise we can go ahead and handle our state later # check RigoDaemon, don't worry about races between Rigo Clients # it is fine to have multiple Rigo Clients connected. Mutual # exclusion is handled via Entropy Resources Lock (which is a file # based rwsem). activity = self._service.activity() if activity != DaemonActivityStates.AVAILABLE: msg = "" show_dialog = True if activity == DaemonActivityStates.NOT_AVAILABLE: msg = _("Background Service is currently not available") elif activity == DaemonActivityStates.UPDATING_REPOSITORIES: show_dialog = False task = ParallelTask( self._service._update_repositories, [], False, master=False) task.daemon = True task.name = "UpdateRepositoriesUnlocked" task.start() elif activity == DaemonActivityStates.MANAGING_APPLICATIONS: show_dialog = False task = ParallelTask( self._service._application_request, None, None, master=False) task.daemon = True task.name = "ApplicationRequestUnlocked" task.start() elif activity == DaemonActivityStates.UPGRADING_SYSTEM: show_dialog = False task = ParallelTask( self._service._upgrade_system, False, master=False) task.daemon = True task.name = "UpgradeSystemUnlocked" task.start() elif activity == DaemonActivityStates.INTERNAL_ROUTINES: msg = _("Background Service is currently busy") else: msg = _("Background Service is incompatible with Rigo") if show_dialog: self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(msg)) entropy.tools.kill_threads() Gtk.main_quit() return elif is_exclusive: msg = _("Background Service is currently unavailable") # no lock acquired, cannot continue the initialization self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(msg)) entropy.tools.kill_threads() Gtk.main_quit() return parser = argparse.ArgumentParser( description=_("Rigo Application Browser")) parser.add_argument( "package", nargs='?', type=file, metavar="<path>", help="package path") parser.add_argument( "--install", metavar="<dep string>", help="install given dependency") parser.add_argument( "--remove", metavar="<dep string>", help="remove given dependency") parser.add_argument( "--upgrade", help="upgrade the system", action="store_true", default=False) parser.add_argument( "--dumper", help="enable the main thread dumper (debug)", action="store_true", default=False) parser.add_argument( "--debug", help="enable Entropy Library debug mode", action="store_true", default=False) try: self._nsargs = parser.parse_args(sys.argv[1:]) except IOError as err: self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup("%s" % (err,))) entropy.tools.kill_threads() Gtk.main_quit() return self._thread_dumper() self._pref_view_c.setup() self._group_view_c.setup() self._config_view_c.setup() self._repo_view_c.setup() self._notice_view_c.setup() self._app_view_c.setup() self._avc.setup() self._nc.setup() self._work_view_c.setup() self._service.setup(acquired) self._easter_eggs() self._window.show() managing = self._start_managing() if not managing: self._change_view_state(RigoViewStates.GROUPS_VIEW_STATE) self._service.hello() def _easter_eggs(self): """ Moo! """ msg = None if entropy.tools.is_st_valentine(): msg = escape_markup(_("Happy St. Valentine <3 <3 !")) elif entropy.tools.is_xmas(): msg = escape_markup(_("Merry Xmas \o/ !")) elif entropy.tools.is_author_bday(): msg = escape_markup(_("Happy birthday to my authoooooor!")) elif entropy.tools.is_april_first(): msg = escape_markup(_("<=|=< (this is optimistically a fish)")) if msg is not None: box = NotificationBox( msg, message_type=Gtk.MessageType.INFO, context_id="EasterEggs") box.add_destroy_button(_("Woot, thanks")) self._nc.append(box) def _start_managing(self): """ Start managing applications passed via argv. """ managing = False if self._nsargs.install: dependency = self._nsargs.install task = ParallelTask( self._avc.install, dependency) task.name = "AppInstall-%s" % (dependency,) task.daemon = True task.start() managing = True if self._nsargs.remove: dependency = self._nsargs.remove task = ParallelTask( self._avc.remove, dependency) task.name = "AppRemove-%s" % (dependency,) task.daemon = True task.start() managing = True if self._nsargs.package: path = self._nsargs.package.name self._nsargs.package.close() # no need, unfortunately task = ParallelTask( self._avc.install_package, path) task.name = "AppInstallPackage-%s" % (path,) task.daemon = True task.start() managing = True if self._nsargs.upgrade: task = ParallelTask(self._avc.upgrade) task.name = "SystemUpgrade" task.daemon = True task.start() managing = True return managing def run(self): """ Run Rigo ;-) """ self._welcome_box.render() self._change_view_state(self._current_state) GLib.idle_add(self._permissions_setup) GLib.threads_init() Gdk.threads_enter() Gtk.main() Gdk.threads_leave() entropy.tools.kill_threads()
class Rigo(Gtk.Application): class RigoHandler(object): def __init__(self, rigo_app, rigo_service): self._app = rigo_app self._service = rigo_service def onDeleteWindow(self, window, event): # if UI is locked, do not allow to close Rigo if self._app.is_ui_locked() or \ self._service.local_activity() != LocalActivityStates.READY: rc = self._app._show_yesno_dialog( None, escape_markup(_("Hey hey hey!")), escape_markup(_("Rigo is working, are you sure?"))) if rc == Gtk.ResponseType.NO: return True while True: try: entropy.tools.kill_threads() Gtk.main_quit((window, event)) except KeyboardInterrupt: continue break def __init__(self): self._current_state_lock = False self._current_state = RigoViewStates.STATIC_VIEW_STATE self._state_transitions = { RigoViewStates.BROWSER_VIEW_STATE: (self._enter_browser_state, self._exit_browser_state), RigoViewStates.STATIC_VIEW_STATE: (self._enter_static_state, self._exit_static_state), RigoViewStates.APPLICATION_VIEW_STATE: (self._enter_application_state, self._exit_application_state), RigoViewStates.WORK_VIEW_STATE: (self._enter_work_state, self._exit_work_state), RigoViewStates.CONFUPDATES_VIEW_STATE: (self._enter_confupdates_state, self._exit_confupdates_state), RigoViewStates.NOTICEBOARD_VIEW_STATE: (self._enter_noticeboard_state, self._exit_noticeboard_state), RigoViewStates.PREFERENCES_VIEW_STATE: (self._enter_preferences_state, self._exit_preferences_state), RigoViewStates.REPOSITORY_VIEW_STATE: (self._enter_repository_state, self._exit_repository_state), RigoViewStates.GROUPS_VIEW_STATE: (self._enter_groups_state, self._exit_groups_state) } self._state_metadata = { RigoViewStates.BROWSER_VIEW_STATE: { "title": _("Search"), }, RigoViewStates.STATIC_VIEW_STATE: { "title": _("Rigo Application Browser"), }, RigoViewStates.APPLICATION_VIEW_STATE: { "title": _("Application"), }, RigoViewStates.WORK_VIEW_STATE: { "title": _("Working Hard"), }, RigoViewStates.CONFUPDATES_VIEW_STATE: { "title": _("Wake Up"), }, RigoViewStates.NOTICEBOARD_VIEW_STATE: { "title": _("Important Stuff"), }, RigoViewStates.PREFERENCES_VIEW_STATE: { "title": _("Breaking Stuff"), }, RigoViewStates.REPOSITORY_VIEW_STATE: { "title": _("Repository Stuff"), }, RigoViewStates.GROUPS_VIEW_STATE: { "title": _("Application Groups"), }, } self._state_mutex = Lock() icons = get_sc_icon_theme(DATA_DIR) self._activity_rwsem = ReadersWritersSemaphore() # This relies on the fact that the installed packages repository # is lazily loaded (thus, schema update code is). self._entropy = Client() self._entropy_ws = EntropyWebService(self._entropy) preload_task = ParallelTask(self._entropy_ws.preload) preload_task.name = "PreloadEntropyWebService" preload_task.daemon = True preload_task.start() self._service = RigoServiceController(self, self._activity_rwsem, self._entropy, self._entropy_ws) app_handler = Rigo.RigoHandler(self, self._service) self._builder = Gtk.Builder() self._builder.add_from_file(os.path.join(DATA_DIR, "ui/gtk3/rigo.ui")) self._builder.connect_signals(app_handler) self._window = self._builder.get_object("rigoWindow") self._window.set_name("rigo-view") self._apps_view = self._builder.get_object("appsViewVbox") self._scrolled_view = self._builder.get_object( "appsViewScrolledWindow") self._app_view = self._builder.get_object("appViewScrollWin") self._app_view.set_name("rigo-view") self._app_view_port = self._builder.get_object("appViewVport") self._app_view_port.set_name("rigo-view") self._not_found_box = self._builder.get_object("appsViewNotFoundVbox") self._config_scrolled_view = self._builder.get_object( "configViewScrolledWindow") self._config_view = self._builder.get_object("configViewVbox") self._config_view.set_name("rigo-view") self._repo_scrolled_view = self._builder.get_object( "repoViewScrolledWindow") self._repo_view = self._builder.get_object("repoViewVbox") self._repo_view.set_name("rigo-view") self._notice_scrolled_view = self._builder.get_object( "noticeViewScrolledWindow") self._notice_view = self._builder.get_object("noticeViewVbox") self._notice_view.set_name("rigo-view") self._pref_scrolled_view = self._builder.get_object( "preferencesViewScrolledWindow") self._pref_view = self._builder.get_object("preferencesViewVbox") self._pref_view.set_name("rigo-view") self._group_scrolled_view = self._builder.get_object( "groupViewScrolledWindow") self._group_view = self._builder.get_object("groupViewVbox") self._group_view.set_name("rigo-view") self._search_entry = self._builder.get_object("searchEntry") self._search_entry_completion = self._builder.get_object( "searchEntryCompletion") self._search_entry_store = self._builder.get_object("searchEntryStore") self._static_view = self._builder.get_object("staticViewVbox") self._notification = self._builder.get_object("notificationBox") self._bottom_notification = \ self._builder.get_object("bottomNotificationBox") self._work_view = self._builder.get_object("workViewVbox") self._work_view.set_name("rigo-view") self._pref_button = self._builder.get_object("prefButton") def _pref_button_activate(widget): self._change_view_state(RigoViewStates.PREFERENCES_VIEW_STATE) self._pref_button.connect("clicked", _pref_button_activate) # Preferences model, view and controller self._pref_store = PreferencesListStore() self._view_pref = PreferencesTreeView(icons, PreferencesListStore.ICON_SIZE) self._pref_scrolled_view.add(self._view_pref) def _pref_queue_draw(*args): self._view_pref.queue_draw() self._pref_store.connect("redraw-request", _pref_queue_draw) self._pref_view_c = PreferenceViewController(self._pref_store, self._view_pref) self._app_view_c = ApplicationViewController(self._entropy, self._entropy_ws, self._pref_view_c, self._service, self._builder) self._view = AppTreeView(self._entropy, self._service, self._app_view_c, icons, True, AppListStore.ICON_SIZE, store=None) self._scrolled_view.add(self._view) self._view.set_scrolled_view(self._scrolled_view) self._app_store = AppListStore(self._entropy, self._entropy_ws, self._service, self._view, icons) def _queue_draw(*args): self._view.queue_draw() self._app_store.connect("redraw-request", _queue_draw) self._app_view_c.set_store(self._app_store) self._app_view_c.connect("application-show", self._on_application_show) # Configuration file updates model, view and controller self._config_store = ConfigUpdatesListStore() self._view_config = ConfigUpdatesTreeView( icons, ConfigUpdatesListStore.ICON_SIZE) self._config_scrolled_view.add(self._view_config) def _config_queue_draw(*args): self._view_config.queue_draw() self._config_store.connect("redraw-request", _config_queue_draw) self._config_view_c = ConfigUpdatesViewController( self._entropy, self._config_store, self._view_config) self._config_view_c.connect("view-cleared", self._on_view_cleared) self._service.set_configuration_controller(self._config_view_c) # Repository model, view and controller self._repo_store = RepositoryListStore() self._view_repo = RepositoryTreeView(icons, RepositoryListStore.ICON_SIZE) self._repo_scrolled_view.add(self._view_repo) def _repo_queue_draw(*args): self._view_repo.queue_draw() self._repo_store.connect("redraw-request", _repo_queue_draw) self._repo_view_c = RepositoryViewController(self._pref_view_c, self._service, self._repo_store, self._view_repo) # NoticeBoard model, view and controller self._notice_store = NoticeBoardListStore() self._view_notice = NoticeBoardTreeView(icons, NoticeBoardListStore.ICON_SIZE) self._notice_scrolled_view.add(self._view_notice) def _notice_queue_draw(*args): self._view_notice.queue_draw() self._notice_store.connect("redraw-request", _notice_queue_draw) self._notice_view_c = NoticeBoardViewController( self._notice_store, self._view_notice) self._service.set_noticeboard_controller(self._notice_view_c) # Group model, view and controller self._group_store = GroupListStore() self._view_group = GroupTreeView(icons, GroupListStore.ICON_SIZE) self._group_scrolled_view.add(self._view_group) def _group_queue_draw(*args): self._view_group.queue_draw() self._group_store.connect("redraw-request", _group_queue_draw) self._group_view_c = GroupViewController(self._service, self._group_store, self._view_group, self._pref_view_c) self._welcome_box = WelcomeBox() settings = Gtk.Settings.get_default() settings.set_property("gtk-error-bell", False) # wire up the css provider to reconfigure on theme-changes self._window.connect("style-updated", self._on_style_updated, init_sc_css_provider, settings, Gdk.Screen.get_default(), DATA_DIR) # Force the initialization of the css provider asap. # This fixes a glitch with GTK 3.10 init_sc_css_provider(self._window, settings, Gdk.Screen.get_default(), DATA_DIR) 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._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) self._avc.connect("view-cleared", self._on_view_cleared) self._avc.connect("view-filled", self._on_view_filled) self._avc.connect("view-want-change", self._on_view_change) self._service.set_bottom_notification_controller(self._bottom_nc) self._app_view_c.set_notification_controller(self._nc) self._app_view_c.set_applications_controller(self._avc) self._config_view_c.set_notification_controller(self._nc) self._config_view_c.set_applications_controller(self._avc) self._repo_view_c.set_notification_controller(self._nc) self._repo_view_c.set_applications_controller(self._avc) self._notice_view_c.set_notification_controller(self._nc) self._notice_view_c.set_applications_controller(self._avc) self._group_view_c.set_applications_controller(self._avc) self._service.set_applications_controller(self._avc) self._service.set_application_controller(self._app_view_c) self._service.set_notification_controller(self._nc) self._service.connect("start-working", self._on_start_working) self._service.connect("repositories-updated", self._on_repo_updated) self._service.connect("applications-managed", self._on_applications_managed) self._work_view_c = WorkViewController(icons, self._service, self._work_view) self._service.set_work_controller(self._work_view_c) self._bottom_nc.connect("show-work-view", self._on_show_work_view) self._bottom_nc.connect("work-interrupt", self._on_work_interrupt) def is_ui_locked(self): """ Return whether the UI is currently locked. """ return self._current_state_lock def _thread_dumper(self): """ If --dumper is in argv, a recurring thread dump function will be spawned every 30 seconds. """ dumper_enable = self._nsargs.dumper if dumper_enable: task = None def _dumper(): def _dump(): task.kill() dump_signal(None, None) timer = Timer(10.0, _dump) timer.name = "MainThreadHearthbeatCheck" timer.daemon = True timer.start() GLib.idle_add(timer.cancel) task = TimeScheduled(5.0, _dumper) task.name = "ThreadDumper" task.daemon = True task.start() def _on_start_working(self, widget, state, lock): """ Emitted by RigoServiceController when we're asked to switch to the Work View and, if lock = True, lock UI. """ if lock: self._search_entry.set_sensitive(False) if state is not None: self._change_view_state(state, lock=lock) def _on_work_interrupt(self, widget): """ We've been explicitly asked to interrupt the currently ongoing work """ rc = self._show_yesno_dialog( self._window, escape_markup(_("Activity Interruption")), escape_markup( _("Are you sure you want to interrupt" " the ongoing Activity? The interruption will" " occur as soon as possible, potentially not" " immediately."))) if rc == Gtk.ResponseType.NO: return self._service.interrupt_activity() def _on_show_work_view(self, widget): """ We've been explicitly asked to switch to WORK_VIEW_STATE """ self._change_view_state(RigoViewStates.WORK_VIEW_STATE, _ignore_lock=True) def _on_repo_updated(self, widget, result, message): """ Emitted by RigoServiceController telling us that repositories have been updated. """ with self._state_mutex: self._current_state_lock = False self._search_entry.set_sensitive(True) if result != 0: msg = "<b>%s</b>: %s" % ( _("Repositories update error"), message, ) message_type = Gtk.MessageType.ERROR else: msg = _("Repositories updated <b>successfully</b>!") message_type = Gtk.MessageType.INFO box = NotificationBox( msg, message_type=message_type, context_id=RigoServiceController.NOTIFICATION_CONTEXT_ID) box.add_destroy_button(_("Ok, thanks")) self._nc.append(box) def _on_applications_managed(self, widget, success, local_activity): """ Emitted by RigoServiceController telling us that enqueue application actions have been completed. """ msg = "N/A" if not success: if local_activity == LocalActivityStates.MANAGING_APPLICATIONS: msg = "<b>%s</b>: %s" % ( _("Application Management Error"), _("please check the management log"), ) elif local_activity == LocalActivityStates.UPGRADING_SYSTEM: msg = "<b>%s</b>: %s" % ( _("System Upgrade Error"), _("please check the upgrade log"), ) message_type = Gtk.MessageType.ERROR else: if local_activity == LocalActivityStates.MANAGING_APPLICATIONS: msg = _("Applications managed <b>successfully</b>!") elif local_activity == LocalActivityStates.UPGRADING_SYSTEM: msg = _("System Upgraded <b>successfully</b>!") message_type = Gtk.MessageType.INFO box = NotificationBox( msg, message_type=message_type, context_id=RigoServiceController.NOTIFICATION_CONTEXT_ID) box.add_destroy_button(_("Ok, thanks")) box.add_button(_("Show me"), self._on_show_work_view) self._nc.append(box) self._work_view_c.deactivate_app_box() def _on_view_cleared(self, *args): self._change_view_state(RigoViewStates.STATIC_VIEW_STATE) def _on_view_filled(self, *args): self._change_view_state(RigoViewStates.BROWSER_VIEW_STATE) def _on_view_change(self, widget, state, payload): self._change_view_state(state, payload=payload) def _on_application_show(self, *args): self._change_view_state(RigoViewStates.APPLICATION_VIEW_STATE) def _exit_browser_state(self): """ Action triggered when UI exits the Application Browser state (or mode). """ self._avc.deselect() self._apps_view.hide() def _enter_browser_state(self): """ Action triggered when UI exits the Application Browser state (or mode). """ self._apps_view.show() def _exit_confupdates_state(self): """ Action triggered when UI exits the Configuration Updates state (or mode). """ self._config_view.hide() def _enter_confupdates_state(self): """ Action triggered when UI enters the Configuration Updates state (or mode). """ self._config_view.show() def _exit_noticeboard_state(self): """ Action triggered when UI exits the NoticeBoard state (or mode). """ self._notice_view.hide() def _enter_noticeboard_state(self): """ Action triggered when UI enters the NoticeBoard state (or mode). """ self._notice_view.show() def _exit_repository_state(self): """ Action triggered when UI exits the Repository Management state (or mode). """ self._repo_view.hide() self._repo_view_c.clear() def _enter_repository_state(self): """ Action triggered when UI enters the Repository Management state (or mode). """ self._repo_view_c.load() self._repo_view.show() def _exit_preferences_state(self): """ Action triggered when UI exits the Preferences state (or mode). """ self._pref_view.hide() def _enter_preferences_state(self): """ Action triggered when UI enters the Preferences state (or mode). """ self._pref_view.show() def _exit_groups_state(self): """ Action triggered when UI exits the Groups state (or mode). """ self._group_view.hide() def _enter_groups_state(self): """ Action triggered when UI enters the Groups state (or mode). """ self._group_view_c.load() self._group_view.show() def _exit_static_state(self): """ Action triggered when UI exits the Static Browser state (or mode). AKA the Welcome Box. """ self._static_view.hide() # release all the childrens of static_view for child in self._static_view.get_children(): self._static_view.remove(child) def _enter_static_state(self): """ Action triggered when UI exits the Static Browser state (or mode). AKA the Welcome Box. """ # keep the current widget if any, or add the # welcome widget if not self._static_view.get_children(): self._welcome_box.show() self._static_view.pack_start(self._welcome_box, True, True, 10) self._static_view.show() def _enter_application_state(self): """ Action triggered when UI enters the Package Information state (or mode). Showing application information. """ # change search_entry first icon to emphasize the # back action self._search_entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY, "gtk-go-back") self._app_view.show() def _exit_application_state(self): """ Action triggered when UI exits the Package Information state (or mode). Hiding back application information. """ self._search_entry.set_icon_from_stock(Gtk.EntryIconPosition.PRIMARY, "gtk-find") self._app_view.hide() self._app_view_c.hide() def _enter_work_state(self): """ Action triggered when UI enters the Work View state (or mode). Either for Updating Repositories or Installing new Apps. """ self._work_view.show() def _exit_work_state(self): """ Action triggered when UI exits the Work View state (or mode). """ self._work_view.hide() def _change_view_state(self, state, lock=False, _ignore_lock=False, payload=None): """ Change Rigo Application UI state. You can pass a custom widget that will be shown in case of static view state. """ with self._state_mutex: if self._current_state_lock and not _ignore_lock: const_debug_write(__name__, "cannot change view state, UI locked") return False txc = self._state_transitions.get(state) if txc is None: raise AttributeError("wrong view state") enter_st, exit_st = txc current_enter_st, current_exit_st = \ self._state_transitions.get( self._current_state) # exit from current state current_exit_st() # enter the new state enter_st() self._current_state = state if lock: self._current_state_lock = True state_meta = self._state_metadata[state] self._window.set_title(escape_markup(state_meta["title"])) return True def _change_view_state_safe(self, state): """ Thread-safe version of change_view_state(). """ def _do_change(): return self._change_view_state(state) GLib.idle_add(_do_change) def _on_style_updated(self, widget, init_css_callback, *args): """ Gtk Style callback, nothing to see here. """ init_css_callback(widget, *args) def _show_ok_dialog(self, parent, title, message): """ Show ugly OK dialog window. """ dlg = Gtk.MessageDialog(parent=parent, type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK) dlg.set_markup(message) dlg.set_title(title) dlg.run() dlg.destroy() def _show_yesno_dialog(self, parent, title, message): """ Show ugly Yes/No dialog window. """ dlg = Gtk.MessageDialog(parent=parent, type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.YES_NO) dlg.set_markup(message) dlg.set_title(title) rc = dlg.run() dlg.destroy() return rc def _permissions_setup(self): """ Check execution privileges and spawn the Rigo UI. """ if not entropy.tools.is_user_in_entropy_group(): # otherwise the lock handling would potentially # fail. self._show_ok_dialog( None, escape_markup(_("Not authorized")), escape_markup(_("You are not authorized to run Rigo"))) entropy.tools.kill_threads() Gtk.main_quit() return if not self._service.service_available(): self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(_("RigoDaemon service is not available"))) entropy.tools.kill_threads() Gtk.main_quit() return supported_apis = self._service.supported_apis() daemon_api = self._service.api() if daemon_api not in supported_apis: self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup( _("API mismatch, please update Rigo and RigoDaemon"))) entropy.tools.kill_threads() Gtk.main_quit() return lock = EntropyResourcesLock(output=self._entropy) # always execute this from the MainThread, since the lock uses TLS acquired = lock.try_acquire_shared() is_exclusive = False if not acquired: # check whether RigoDaemon is running in excluive mode # and ignore non-atomicity here (failing with error # is acceptable) if not self._service.exclusive(): self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(_("Another Application Manager is active"))) entropy.tools.kill_threads() Gtk.main_quit() return is_exclusive = True # otherwise we can go ahead and handle our state later # check RigoDaemon, don't worry about races between Rigo Clients # it is fine to have multiple Rigo Clients connected. Mutual # exclusion is handled via Entropy Resources Lock (which is a file # based rwsem). activity = self._service.activity() if activity != DaemonActivityStates.AVAILABLE: msg = "" show_dialog = True if activity == DaemonActivityStates.NOT_AVAILABLE: msg = _("Background Service is currently not available") elif activity == DaemonActivityStates.UPDATING_REPOSITORIES: show_dialog = False task = ParallelTask(self._service._update_repositories, [], False, master=False) task.daemon = True task.name = "UpdateRepositoriesUnlocked" task.start() elif activity == DaemonActivityStates.MANAGING_APPLICATIONS: show_dialog = False task = ParallelTask(self._service._application_request, None, None, master=False) task.daemon = True task.name = "ApplicationRequestUnlocked" task.start() elif activity == DaemonActivityStates.UPGRADING_SYSTEM: show_dialog = False task = ParallelTask(self._service._upgrade_system, False, master=False) task.daemon = True task.name = "UpgradeSystemUnlocked" task.start() elif activity == DaemonActivityStates.INTERNAL_ROUTINES: msg = _("Background Service is currently busy") else: msg = _("Background Service is incompatible with Rigo") if show_dialog: self._show_ok_dialog(None, escape_markup(_("Rigo")), escape_markup(msg)) entropy.tools.kill_threads() Gtk.main_quit() return elif is_exclusive: msg = _("Background Service is currently unavailable") # no lock acquired, cannot continue the initialization self._show_ok_dialog(None, escape_markup(_("Rigo")), escape_markup(msg)) entropy.tools.kill_threads() Gtk.main_quit() return parser = argparse.ArgumentParser( description=_("Rigo Application Browser")) parser.add_argument("package", nargs='?', type=file, metavar="<path>", help="package path") parser.add_argument("--install", metavar="<dep string>", help="install given dependency") parser.add_argument("--remove", metavar="<dep string>", help="remove given dependency") parser.add_argument("--upgrade", help="upgrade the system", action="store_true", default=False) parser.add_argument("--dumper", help="enable the main thread dumper (debug)", action="store_true", default=False) parser.add_argument("--debug", help="enable Entropy Library debug mode", action="store_true", default=False) try: self._nsargs = parser.parse_args(sys.argv[1:]) except IOError as err: self._show_ok_dialog(None, escape_markup(_("Rigo")), escape_markup("%s" % (err, ))) entropy.tools.kill_threads() Gtk.main_quit() return self._thread_dumper() self._pref_view_c.setup() self._group_view_c.setup() self._config_view_c.setup() self._repo_view_c.setup() self._notice_view_c.setup() self._app_view_c.setup() self._avc.setup() self._nc.setup() self._work_view_c.setup() self._service.setup(acquired) self._easter_eggs() self._window.show() managing = self._start_managing() if not managing: self._change_view_state(RigoViewStates.GROUPS_VIEW_STATE) self._service.hello() def _easter_eggs(self): """ Moo! """ msg = None if entropy.tools.is_st_valentine(): msg = escape_markup(_("Happy St. Valentine <3 <3 !")) elif entropy.tools.is_xmas(): msg = escape_markup(_("Merry Xmas \o/ !")) elif entropy.tools.is_author_bday(): msg = escape_markup(_("Happy birthday to my authoooooor!")) elif entropy.tools.is_april_first(): msg = escape_markup(_("<=|=< (this is optimistically a fish)")) if msg is not None: box = NotificationBox(msg, message_type=Gtk.MessageType.INFO, context_id="EasterEggs") box.add_destroy_button(_("Woot, thanks")) self._nc.append(box) def _start_managing(self): """ Start managing applications passed via argv. """ managing = False if self._nsargs.install: dependency = self._nsargs.install task = ParallelTask(self._avc.install, dependency) task.name = "AppInstall-%s" % (dependency, ) task.daemon = True task.start() managing = True if self._nsargs.remove: dependency = self._nsargs.remove task = ParallelTask(self._avc.remove, dependency) task.name = "AppRemove-%s" % (dependency, ) task.daemon = True task.start() managing = True if self._nsargs.package: path = self._nsargs.package.name self._nsargs.package.close() # no need, unfortunately task = ParallelTask(self._avc.install_package, path) task.name = "AppInstallPackage-%s" % (path, ) task.daemon = True task.start() managing = True if self._nsargs.upgrade: task = ParallelTask(self._avc.upgrade) task.name = "SystemUpgrade" task.daemon = True task.start() managing = True return managing def run(self): """ Run Rigo ;-) """ self._welcome_box.render() self._change_view_state(self._current_state) GLib.idle_add(self._permissions_setup) Gtk.main() entropy.tools.kill_threads()
def __init__(self): self._current_state_lock = False self._current_state = RigoViewStates.STATIC_VIEW_STATE self._state_transitions = { RigoViewStates.BROWSER_VIEW_STATE: ( self._enter_browser_state, self._exit_browser_state), RigoViewStates.STATIC_VIEW_STATE: ( self._enter_static_state, self._exit_static_state), RigoViewStates.APPLICATION_VIEW_STATE: ( self._enter_application_state, self._exit_application_state), RigoViewStates.WORK_VIEW_STATE: ( self._enter_work_state, self._exit_work_state), RigoViewStates.CONFUPDATES_VIEW_STATE: ( self._enter_confupdates_state, self._exit_confupdates_state), RigoViewStates.NOTICEBOARD_VIEW_STATE: ( self._enter_noticeboard_state, self._exit_noticeboard_state), RigoViewStates.PREFERENCES_VIEW_STATE: ( self._enter_preferences_state, self._exit_preferences_state), RigoViewStates.REPOSITORY_VIEW_STATE: ( self._enter_repository_state, self._exit_repository_state), RigoViewStates.GROUPS_VIEW_STATE: ( self._enter_groups_state, self._exit_groups_state) } self._state_metadata = { RigoViewStates.BROWSER_VIEW_STATE: { "title": _("Search"), }, RigoViewStates.STATIC_VIEW_STATE: { "title": _("Rigo Application Browser"), }, RigoViewStates.APPLICATION_VIEW_STATE: { "title": _("Application"), }, RigoViewStates.WORK_VIEW_STATE: { "title": _("Working Hard"), }, RigoViewStates.CONFUPDATES_VIEW_STATE: { "title": _("Wake Up"), }, RigoViewStates.NOTICEBOARD_VIEW_STATE: { "title": _("Important Stuff"), }, RigoViewStates.PREFERENCES_VIEW_STATE: { "title": _("Breaking Stuff"), }, RigoViewStates.REPOSITORY_VIEW_STATE: { "title": _("Repository Stuff"), }, RigoViewStates.GROUPS_VIEW_STATE: { "title": _("Application Groups"), }, } self._state_mutex = Lock() icons = get_sc_icon_theme(DATA_DIR) self._activity_rwsem = ReadersWritersSemaphore() # This relies on the fact that the installed packages repository # is lazily loaded (thus, schema update code is). self._entropy = Client() self._entropy_ws = EntropyWebService(self._entropy) preload_task = ParallelTask(self._entropy_ws.preload) preload_task.name = "PreloadEntropyWebService" preload_task.daemon = True preload_task.start() self._service = RigoServiceController( self, self._activity_rwsem, self._entropy, self._entropy_ws) app_handler = Rigo.RigoHandler(self, self._service) self._builder = Gtk.Builder() self._builder.add_from_file(os.path.join(DATA_DIR, "ui/gtk3/rigo.ui")) self._builder.connect_signals(app_handler) self._window = self._builder.get_object("rigoWindow") self._window.set_name("rigo-view") self._apps_view = self._builder.get_object("appsViewVbox") self._scrolled_view = self._builder.get_object("appsViewScrolledWindow") self._app_view = self._builder.get_object("appViewScrollWin") self._app_view.set_name("rigo-view") self._app_view_port = self._builder.get_object("appViewVport") self._app_view_port.set_name("rigo-view") self._not_found_box = self._builder.get_object("appsViewNotFoundVbox") self._config_scrolled_view = self._builder.get_object( "configViewScrolledWindow") self._config_view = self._builder.get_object("configViewVbox") self._config_view.set_name("rigo-view") self._repo_scrolled_view = self._builder.get_object( "repoViewScrolledWindow") self._repo_view = self._builder.get_object("repoViewVbox") self._repo_view.set_name("rigo-view") self._notice_scrolled_view = self._builder.get_object( "noticeViewScrolledWindow") self._notice_view = self._builder.get_object("noticeViewVbox") self._notice_view.set_name("rigo-view") self._pref_scrolled_view = self._builder.get_object( "preferencesViewScrolledWindow") self._pref_view = self._builder.get_object("preferencesViewVbox") self._pref_view.set_name("rigo-view") self._group_scrolled_view = self._builder.get_object( "groupViewScrolledWindow") self._group_view = self._builder.get_object("groupViewVbox") self._group_view.set_name("rigo-view") self._search_entry = self._builder.get_object("searchEntry") self._search_entry_completion = self._builder.get_object( "searchEntryCompletion") self._search_entry_store = self._builder.get_object( "searchEntryStore") self._static_view = self._builder.get_object("staticViewVbox") self._notification = self._builder.get_object("notificationBox") self._bottom_notification = \ self._builder.get_object("bottomNotificationBox") self._work_view = self._builder.get_object("workViewVbox") self._work_view.set_name("rigo-view") self._pref_button = self._builder.get_object( "prefButton") def _pref_button_activate(widget): self._change_view_state( RigoViewStates.PREFERENCES_VIEW_STATE) self._pref_button.connect( "clicked", _pref_button_activate) # Preferences model, view and controller self._pref_store = PreferencesListStore() self._view_pref = PreferencesTreeView( icons, PreferencesListStore.ICON_SIZE) self._pref_scrolled_view.add(self._view_pref) def _pref_queue_draw(*args): self._view_pref.queue_draw() self._pref_store.connect("redraw-request", _pref_queue_draw) self._pref_view_c = PreferenceViewController( self._pref_store, self._view_pref) self._app_view_c = ApplicationViewController( self._entropy, self._entropy_ws, self._pref_view_c, self._service, self._builder) self._view = AppTreeView( self._entropy, self._service, self._app_view_c, icons, True, AppListStore.ICON_SIZE, store=None) self._scrolled_view.add(self._view) self._view.set_scrolled_view(self._scrolled_view) self._app_store = AppListStore( self._entropy, self._entropy_ws, self._service, self._view, icons) def _queue_draw(*args): self._view.queue_draw() self._app_store.connect("redraw-request", _queue_draw) self._app_view_c.set_store(self._app_store) self._app_view_c.connect("application-show", self._on_application_show) # Configuration file updates model, view and controller self._config_store = ConfigUpdatesListStore() self._view_config = ConfigUpdatesTreeView( icons, ConfigUpdatesListStore.ICON_SIZE) self._config_scrolled_view.add(self._view_config) def _config_queue_draw(*args): self._view_config.queue_draw() self._config_store.connect("redraw-request", _config_queue_draw) self._config_view_c = ConfigUpdatesViewController( self._entropy, self._config_store, self._view_config) self._config_view_c.connect( "view-cleared", self._on_view_cleared) self._service.set_configuration_controller(self._config_view_c) # Repository model, view and controller self._repo_store = RepositoryListStore() self._view_repo = RepositoryTreeView( icons, RepositoryListStore.ICON_SIZE) self._repo_scrolled_view.add(self._view_repo) def _repo_queue_draw(*args): self._view_repo.queue_draw() self._repo_store.connect("redraw-request", _repo_queue_draw) self._repo_view_c = RepositoryViewController( self._pref_view_c, self._service, self._repo_store, self._view_repo) # NoticeBoard model, view and controller self._notice_store = NoticeBoardListStore() self._view_notice = NoticeBoardTreeView( icons, NoticeBoardListStore.ICON_SIZE) self._notice_scrolled_view.add(self._view_notice) def _notice_queue_draw(*args): self._view_notice.queue_draw() self._notice_store.connect("redraw-request", _notice_queue_draw) self._notice_view_c = NoticeBoardViewController( self._notice_store, self._view_notice) self._service.set_noticeboard_controller(self._notice_view_c) # Group model, view and controller self._group_store = GroupListStore() self._view_group = GroupTreeView( icons, GroupListStore.ICON_SIZE) self._group_scrolled_view.add(self._view_group) def _group_queue_draw(*args): self._view_group.queue_draw() self._group_store.connect("redraw-request", _group_queue_draw) self._group_view_c = GroupViewController( self._service, self._group_store, self._view_group, self._pref_view_c) self._welcome_box = WelcomeBox() settings = Gtk.Settings.get_default() settings.set_property("gtk-error-bell", False) # wire up the css provider to reconfigure on theme-changes self._window.connect("style-updated", self._on_style_updated, init_sc_css_provider, settings, Gdk.Screen.get_default(), DATA_DIR) # Force the initialization of the css provider asap. # This fixes a glitch with GTK 3.10 init_sc_css_provider( self._window, settings, Gdk.Screen.get_default(), DATA_DIR) 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._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) self._avc.connect("view-cleared", self._on_view_cleared) self._avc.connect("view-filled", self._on_view_filled) self._avc.connect("view-want-change", self._on_view_change) self._service.set_bottom_notification_controller( self._bottom_nc) self._app_view_c.set_notification_controller(self._nc) self._app_view_c.set_applications_controller(self._avc) self._config_view_c.set_notification_controller(self._nc) self._config_view_c.set_applications_controller(self._avc) self._repo_view_c.set_notification_controller(self._nc) self._repo_view_c.set_applications_controller(self._avc) self._notice_view_c.set_notification_controller(self._nc) self._notice_view_c.set_applications_controller(self._avc) self._group_view_c.set_applications_controller(self._avc) self._service.set_applications_controller(self._avc) self._service.set_application_controller(self._app_view_c) self._service.set_notification_controller(self._nc) self._service.connect("start-working", self._on_start_working) self._service.connect("repositories-updated", self._on_repo_updated) self._service.connect("applications-managed", self._on_applications_managed) self._work_view_c = WorkViewController( icons, self._service, self._work_view) self._service.set_work_controller(self._work_view_c) self._bottom_nc.connect("show-work-view", self._on_show_work_view) self._bottom_nc.connect("work-interrupt", self._on_work_interrupt)
def __init__(self): self._current_state_lock = False self._current_state = RigoViewStates.STATIC_VIEW_STATE self._state_transitions = { RigoViewStates.BROWSER_VIEW_STATE: (self._enter_browser_state, self._exit_browser_state), RigoViewStates.STATIC_VIEW_STATE: (self._enter_static_state, self._exit_static_state), RigoViewStates.APPLICATION_VIEW_STATE: (self._enter_application_state, self._exit_application_state), RigoViewStates.WORK_VIEW_STATE: (self._enter_work_state, self._exit_work_state), RigoViewStates.CONFUPDATES_VIEW_STATE: (self._enter_confupdates_state, self._exit_confupdates_state), RigoViewStates.NOTICEBOARD_VIEW_STATE: (self._enter_noticeboard_state, self._exit_noticeboard_state), RigoViewStates.PREFERENCES_VIEW_STATE: (self._enter_preferences_state, self._exit_preferences_state), RigoViewStates.REPOSITORY_VIEW_STATE: (self._enter_repository_state, self._exit_repository_state), RigoViewStates.GROUPS_VIEW_STATE: (self._enter_groups_state, self._exit_groups_state) } self._state_metadata = { RigoViewStates.BROWSER_VIEW_STATE: { "title": _("Search"), }, RigoViewStates.STATIC_VIEW_STATE: { "title": _("Rigo Application Browser"), }, RigoViewStates.APPLICATION_VIEW_STATE: { "title": _("Application"), }, RigoViewStates.WORK_VIEW_STATE: { "title": _("Working Hard"), }, RigoViewStates.CONFUPDATES_VIEW_STATE: { "title": _("Wake Up"), }, RigoViewStates.NOTICEBOARD_VIEW_STATE: { "title": _("Important Stuff"), }, RigoViewStates.PREFERENCES_VIEW_STATE: { "title": _("Breaking Stuff"), }, RigoViewStates.REPOSITORY_VIEW_STATE: { "title": _("Repository Stuff"), }, RigoViewStates.GROUPS_VIEW_STATE: { "title": _("Application Groups"), }, } self._state_mutex = Lock() icons = get_sc_icon_theme(DATA_DIR) self._activity_rwsem = ReadersWritersSemaphore() # This relies on the fact that the installed packages repository # is lazily loaded (thus, schema update code is). self._entropy = Client() self._entropy_ws = EntropyWebService(self._entropy) preload_task = ParallelTask(self._entropy_ws.preload) preload_task.name = "PreloadEntropyWebService" preload_task.daemon = True preload_task.start() self._service = RigoServiceController(self, self._activity_rwsem, self._entropy, self._entropy_ws) app_handler = Rigo.RigoHandler(self, self._service) self._builder = Gtk.Builder() self._builder.add_from_file(os.path.join(DATA_DIR, "ui/gtk3/rigo.ui")) self._builder.connect_signals(app_handler) self._window = self._builder.get_object("rigoWindow") self._window.set_name("rigo-view") self._apps_view = self._builder.get_object("appsViewVbox") self._scrolled_view = self._builder.get_object( "appsViewScrolledWindow") self._app_view = self._builder.get_object("appViewScrollWin") self._app_view.set_name("rigo-view") self._app_view_port = self._builder.get_object("appViewVport") self._app_view_port.set_name("rigo-view") self._not_found_box = self._builder.get_object("appsViewNotFoundVbox") self._config_scrolled_view = self._builder.get_object( "configViewScrolledWindow") self._config_view = self._builder.get_object("configViewVbox") self._config_view.set_name("rigo-view") self._repo_scrolled_view = self._builder.get_object( "repoViewScrolledWindow") self._repo_view = self._builder.get_object("repoViewVbox") self._repo_view.set_name("rigo-view") self._notice_scrolled_view = self._builder.get_object( "noticeViewScrolledWindow") self._notice_view = self._builder.get_object("noticeViewVbox") self._notice_view.set_name("rigo-view") self._pref_scrolled_view = self._builder.get_object( "preferencesViewScrolledWindow") self._pref_view = self._builder.get_object("preferencesViewVbox") self._pref_view.set_name("rigo-view") self._group_scrolled_view = self._builder.get_object( "groupViewScrolledWindow") self._group_view = self._builder.get_object("groupViewVbox") self._group_view.set_name("rigo-view") self._search_entry = self._builder.get_object("searchEntry") self._search_entry_completion = self._builder.get_object( "searchEntryCompletion") self._search_entry_store = self._builder.get_object("searchEntryStore") self._static_view = self._builder.get_object("staticViewVbox") self._notification = self._builder.get_object("notificationBox") self._bottom_notification = \ self._builder.get_object("bottomNotificationBox") self._work_view = self._builder.get_object("workViewVbox") self._work_view.set_name("rigo-view") self._pref_button = self._builder.get_object("prefButton") def _pref_button_activate(widget): self._change_view_state(RigoViewStates.PREFERENCES_VIEW_STATE) self._pref_button.connect("clicked", _pref_button_activate) # Preferences model, view and controller self._pref_store = PreferencesListStore() self._view_pref = PreferencesTreeView(icons, PreferencesListStore.ICON_SIZE) self._pref_scrolled_view.add(self._view_pref) def _pref_queue_draw(*args): self._view_pref.queue_draw() self._pref_store.connect("redraw-request", _pref_queue_draw) self._pref_view_c = PreferenceViewController(self._pref_store, self._view_pref) self._app_view_c = ApplicationViewController(self._entropy, self._entropy_ws, self._pref_view_c, self._service, self._builder) self._view = AppTreeView(self._entropy, self._service, self._app_view_c, icons, True, AppListStore.ICON_SIZE, store=None) self._scrolled_view.add(self._view) self._view.set_scrolled_view(self._scrolled_view) self._app_store = AppListStore(self._entropy, self._entropy_ws, self._service, self._view, icons) def _queue_draw(*args): self._view.queue_draw() self._app_store.connect("redraw-request", _queue_draw) self._app_view_c.set_store(self._app_store) self._app_view_c.connect("application-show", self._on_application_show) # Configuration file updates model, view and controller self._config_store = ConfigUpdatesListStore() self._view_config = ConfigUpdatesTreeView( icons, ConfigUpdatesListStore.ICON_SIZE) self._config_scrolled_view.add(self._view_config) def _config_queue_draw(*args): self._view_config.queue_draw() self._config_store.connect("redraw-request", _config_queue_draw) self._config_view_c = ConfigUpdatesViewController( self._entropy, self._config_store, self._view_config) self._config_view_c.connect("view-cleared", self._on_view_cleared) self._service.set_configuration_controller(self._config_view_c) # Repository model, view and controller self._repo_store = RepositoryListStore() self._view_repo = RepositoryTreeView(icons, RepositoryListStore.ICON_SIZE) self._repo_scrolled_view.add(self._view_repo) def _repo_queue_draw(*args): self._view_repo.queue_draw() self._repo_store.connect("redraw-request", _repo_queue_draw) self._repo_view_c = RepositoryViewController(self._pref_view_c, self._service, self._repo_store, self._view_repo) # NoticeBoard model, view and controller self._notice_store = NoticeBoardListStore() self._view_notice = NoticeBoardTreeView(icons, NoticeBoardListStore.ICON_SIZE) self._notice_scrolled_view.add(self._view_notice) def _notice_queue_draw(*args): self._view_notice.queue_draw() self._notice_store.connect("redraw-request", _notice_queue_draw) self._notice_view_c = NoticeBoardViewController( self._notice_store, self._view_notice) self._service.set_noticeboard_controller(self._notice_view_c) # Group model, view and controller self._group_store = GroupListStore() self._view_group = GroupTreeView(icons, GroupListStore.ICON_SIZE) self._group_scrolled_view.add(self._view_group) def _group_queue_draw(*args): self._view_group.queue_draw() self._group_store.connect("redraw-request", _group_queue_draw) self._group_view_c = GroupViewController(self._service, self._group_store, self._view_group, self._pref_view_c) self._welcome_box = WelcomeBox() settings = Gtk.Settings.get_default() settings.set_property("gtk-error-bell", False) # wire up the css provider to reconfigure on theme-changes self._window.connect("style-updated", self._on_style_updated, init_sc_css_provider, settings, Gdk.Screen.get_default(), DATA_DIR) # Force the initialization of the css provider asap. # This fixes a glitch with GTK 3.10 init_sc_css_provider(self._window, settings, Gdk.Screen.get_default(), DATA_DIR) 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._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) self._avc.connect("view-cleared", self._on_view_cleared) self._avc.connect("view-filled", self._on_view_filled) self._avc.connect("view-want-change", self._on_view_change) self._service.set_bottom_notification_controller(self._bottom_nc) self._app_view_c.set_notification_controller(self._nc) self._app_view_c.set_applications_controller(self._avc) self._config_view_c.set_notification_controller(self._nc) self._config_view_c.set_applications_controller(self._avc) self._repo_view_c.set_notification_controller(self._nc) self._repo_view_c.set_applications_controller(self._avc) self._notice_view_c.set_notification_controller(self._nc) self._notice_view_c.set_applications_controller(self._avc) self._group_view_c.set_applications_controller(self._avc) self._service.set_applications_controller(self._avc) self._service.set_application_controller(self._app_view_c) self._service.set_notification_controller(self._nc) self._service.connect("start-working", self._on_start_working) self._service.connect("repositories-updated", self._on_repo_updated) self._service.connect("applications-managed", self._on_applications_managed) self._work_view_c = WorkViewController(icons, self._service, self._work_view) self._service.set_work_controller(self._work_view_c) self._bottom_nc.connect("show-work-view", self._on_show_work_view) self._bottom_nc.connect("work-interrupt", self._on_work_interrupt)