class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "toggled": ("show_all", "savespace_enable"), "cursor-changed": ("locale_view",), } @quickstart.threads.thread def set_locale(self, locale, sel, itr): """ Sets the given locale. """ try: self.Locale.set(locale) self.default = itr # Create stamp self.Locale.create_stamp([".alan2-locale-changed"]) GObject.idle_add(self.RebootDialog.show) except: sel.select_iter(self.default) GObject.idle_add(self.objects.region_spinner.hide) GObject.idle_add(self.scene_container.set_sensitive, True) @quickstart.threads.thread def savespace_purge(self, locale): """ Purges foreign locales. """ self.Locale.savespace_purge(locale) GObject.idle_add(self.objects.other_spinner.hide) GObject.idle_add(self.scene_container.set_sensitive, True) def on_locale_view_cursor_changed(self, locale_view): """ Fired when the user changes the locale. """ # selection sel = self.objects.locale_view.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.locales.get_value(itr, 0) if selected == self.Locale.default: return if self.objects.savespace_enable.get_active(): # Display warning if self.objects.savespace_warning.run() == Gtk.ResponseType.NO: self.objects.savespace_warning.hide() sel.select_iter(self.default) return self.objects.savespace_enable.set_active(False) self.objects.savespace_warning.hide() GObject.idle_add(self.objects.region_spinner.show) GObject.idle_add(self.scene_container.set_sensitive, False) self.set_locale(selected, sel, itr) def on_show_all_toggled(self, checkbutton): """ Fired when the 'Show all locales' checkbutton has been clicked. """ GObject.idle_add(self.build_locale_list, self.objects.show_all.get_active()) def on_savespace_enable_toggled(self, checkbutton): """ Fired when the 'Enable savespace' checkbutton has been clicked. """ locale = self.objects.locales.get_value(self.default, 0) if checkbutton.get_active(): self.Locale.savespace_enable(locale) # Purge window if self.objects.savespace_window.run() == Gtk.ResponseType.YES: # Purge!! self.savespace_purge(locale) GObject.idle_add(self.objects.other_spinner.show) GObject.idle_add(self.scene_container.set_sensitive, False) self.objects.savespace_window.hide() else: self.Locale.savespace_disable() def build_locale_list(self, all=False): """ Populates the listbox with locales. """ self.objects.locales.clear() for locale, human in self.Locale.human_form(all=all).items(): if all: codepage = self.Locale.codepages[locale] else: codepage = "" itr = self.objects.locales.append((locale, human, codepage)) # Save iter if this is the default... if locale == self.Locale.default: self.default = itr if self.default: sel = self.objects.locale_view.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.locale_view.scroll_to_cell, sel.get_selected_rows()[1][0]) def on_locked(self, unlockbar): """ Fired when the scene has been locked. """ GObject.idle_add(self.objects.content.set_sensitive, False) def on_unlocked(self, unlockbar): """ Fired when the scene has been unlocked. """ GObject.idle_add(self.objects.content.set_sensitive, True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Check for savespace... if os.path.exists("/etc/dpkg/dpkg.cfg.d/keeptalking"): self.objects.savespace_enable.set_active(True) # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.keeptalking2.change-locale") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Locale = Locale() self.default = None # Make the locale_view treeview working... locale_renderer = Gtk.CellRendererText() self.locale_column = Gtk.TreeViewColumn("Locale", locale_renderer, text=1) self.objects.locales.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.locale_view.append_column(self.locale_column) type_renderer = Gtk.CellRendererText() self.type_column = Gtk.TreeViewColumn("Type", type_renderer, text=2) self.objects.locale_view.append_column(self.type_column) # Populate the locale list GObject.idle_add(self.build_locale_list) def on_scene_called(self): """ Show the scene! """ # We are locked self.unlockbar.emit("locked") self.cancellable = Gio.Cancellable() self.RebootDialog = RebootDialog(self.cancellable) self.RebootDialog.bind_property( "visible", self.scene_container, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN ) def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ self.unlockbar.cancel_authorization() self.cancellable.cancel() return True
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "toggled": ("show_all", "savespace_enable"), "cursor-changed": ("locale_view", ), } @quickstart.threads.thread def set_locale(self, locale, sel, itr): """ Sets the given locale. """ try: self.Locale.set(locale) self.default = itr # Create stamp self.Locale.create_stamp([".alan2-locale-changed"]) GObject.idle_add(self.RebootDialog.show) except: sel.select_iter(self.default) GObject.idle_add(self.objects.region_spinner.hide) GObject.idle_add(self.scene_container.set_sensitive, True) @quickstart.threads.thread def savespace_purge(self, locale): """ Purges foreign locales. """ self.Locale.savespace_purge(locale) GObject.idle_add(self.objects.other_spinner.hide) GObject.idle_add(self.scene_container.set_sensitive, True) def on_locale_view_cursor_changed(self, locale_view): """ Fired when the user changes the locale. """ # selection sel = self.objects.locale_view.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.locales.get_value(itr, 0) if selected == self.Locale.default: return if self.objects.savespace_enable.get_active(): # Display warning if self.objects.savespace_warning.run() == Gtk.ResponseType.NO: self.objects.savespace_warning.hide() sel.select_iter(self.default) return self.objects.savespace_enable.set_active(False) self.objects.savespace_warning.hide() GObject.idle_add(self.objects.region_spinner.show) GObject.idle_add(self.scene_container.set_sensitive, False) self.set_locale(selected, sel, itr) def on_show_all_toggled(self, checkbutton): """ Fired when the 'Show all locales' checkbutton has been clicked. """ GObject.idle_add(self.build_locale_list, self.objects.show_all.get_active()) def on_savespace_enable_toggled(self, checkbutton): """ Fired when the 'Enable savespace' checkbutton has been clicked. """ locale = self.objects.locales.get_value(self.default, 0) if checkbutton.get_active(): self.Locale.savespace_enable(locale) # Purge window if self.objects.savespace_window.run() == Gtk.ResponseType.YES: # Purge!! self.savespace_purge(locale) GObject.idle_add(self.objects.other_spinner.show) GObject.idle_add(self.scene_container.set_sensitive, False) self.objects.savespace_window.hide() else: self.Locale.savespace_disable() def build_locale_list(self, all=False): """ Populates the listbox with locales. """ self.objects.locales.clear() for locale, human in self.Locale.human_form(all=all).items(): if all: codepage = self.Locale.codepages[locale] else: codepage = "" itr = self.objects.locales.append((locale, human, codepage)) # Save iter if this is the default... if locale == self.Locale.default: self.default = itr if self.default: sel = self.objects.locale_view.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.locale_view.scroll_to_cell, sel.get_selected_rows()[1][0]) def on_locked(self, unlockbar): """ Fired when the scene has been locked. """ GObject.idle_add(self.objects.content.set_sensitive, False) def on_unlocked(self, unlockbar): """ Fired when the scene has been unlocked. """ GObject.idle_add(self.objects.content.set_sensitive, True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Check for savespace... if os.path.exists("/etc/dpkg/dpkg.cfg.d/keeptalking"): self.objects.savespace_enable.set_active(True) # Create unlockbar self.unlockbar = UnlockBar( "org.semplicelinux.keeptalking2.change-locale") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Locale = Locale() self.default = None # Make the locale_view treeview working... locale_renderer = Gtk.CellRendererText() self.locale_column = Gtk.TreeViewColumn("Locale", locale_renderer, text=1) self.objects.locales.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.locale_view.append_column(self.locale_column) type_renderer = Gtk.CellRendererText() self.type_column = Gtk.TreeViewColumn("Type", type_renderer, text=2) self.objects.locale_view.append_column(self.type_column) # Populate the locale list GObject.idle_add(self.build_locale_list) def on_scene_called(self): """ Show the scene! """ # We are locked self.unlockbar.emit("locked") self.cancellable = Gio.Cancellable() self.RebootDialog = RebootDialog(self.cancellable) self.RebootDialog.bind_property("visible", self.scene_container, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN) def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ self.unlockbar.cancel_authorization() self.cancellable.cancel() return True
class Scene(quickstart.scenes.BaseScene): """ Keyboard settings. """ events = { "changed": ("model_combo", ), "cursor-changed": ("layout_view", "variant_view"), } def setxkbmap(self): """ Invokes setxkbmap and sets the currently selected layout, variant and model. """ subprocess.call([ "setxkbmap", self.Keyboard.default_layout, self.Keyboard.default_variant if self.Keyboard.default_variant else "", "-model" if self.Keyboard.default_model else "", self.Keyboard.default_model if self.Keyboard.default_model else "" ]) def on_model_combo_changed(self, combobox): """ Fired when the user changes the keyboard model. """ itr = self.objects.model_combo.get_active_iter() if not itr: return selected = self.objects.models.get_value(itr, 0) if selected == self.Keyboard.default_variant: return try: self.Keyboard.set(model=selected) self.default_model = itr self.setxkbmap() except: self.objects.model_combo.set_active_iter(self.default_model) def on_variant_view_cursor_changed(self, treeview): """ Fired when the user changes the variant. """ if self.building_list: return # selection sel = treeview.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.variants.get_value(itr, 0) if selected == self.Keyboard.default_variant: return try: self.Keyboard.set(variant=selected) self.default_variant = itr self.setxkbmap() except: sel.select_iter(self.default_variant) def on_layout_view_cursor_changed(self, treeview): """ Fired when the user changes the locale. """ # selection sel = treeview.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.layouts.get_value(itr, 0) if selected == self.Keyboard.default_layout: return # Reset default variant self.default_variant = None try: self.Keyboard.set(layout=selected, variant='') self.default = itr self.setxkbmap() except: sel.select_iter(self.default) # Rebuild variant list GObject.idle_add(self.build_variant_list, selected) def build_variant_list(self, layout): """ Populates the variant list. """ self.building_list = True self.objects.variants.clear() models, layouts = self.Keyboard.supported() variants = layouts[layout]["variants"] for variant in variants: for item, key in variant.items(): itr = self.objects.variants.append([item, key]) # Save the default variant if item == self.Keyboard.default_variant: self.default_variant = itr # No variant reciter = self.objects.variants.prepend( ['', layouts[layout]["description"]]) sel = self.objects.variant_view.get_selection() if self.default_variant: sel.select_iter(self.default) else: # No variant, use reciter sel.select_iter(reciter) GObject.idle_add(self.objects.variant_view.scroll_to_cell, sel.get_selected_rows()[1][0]) self.building_list = False def build_model_list(self): """ Populates the model list. """ pc105 = None self.objects.models.clear() models = self.Keyboard.supported()[0] for item, key in models.items(): itr = self.objects.models.append([item, key]) # Save the default model if item == self.Keyboard.default_model: self.default_model = itr elif item == "pc105": # Fallback pc105 = itr if self.default_model: self.objects.model_combo.set_active_iter(self.default_model) else: self.objects.model_combo.set_active_iter(pc105) def build_layout_list(self): """ Populates the layout list. """ self.objects.layouts.clear() models, layouts = self.Keyboard.supported() for item, key in layouts.items(): itr = self.objects.layouts.append([item, key["description"]]) # Save iter if it's the default... if item == self.Keyboard.default_layout: self.default = itr if self.default: sel = self.objects.layout_view.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.layout_view.scroll_to_cell, sel.get_selected_rows()[1][0]) # Also build the variant view! GObject.idle_add(self.build_variant_list, self.Keyboard.default_layout) def on_locked(self, unlockbar): """ Fired when the scene has been locked. """ GObject.idle_add(self.objects.content.set_sensitive, False) def on_unlocked(self, unlockbar): """ Fired when the scene has been unlocked. """ GObject.idle_add(self.objects.content.set_sensitive, True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.locale1.set-keyboard") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Keyboard = Keyboard() self.building_list = False self.default = None self.default_variant = None self.default_model = None # Make the layout_view treeview working... layout_renderer = Gtk.CellRendererText() self.layout_column = Gtk.TreeViewColumn("Layout", layout_renderer, text=1) self.objects.layouts.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.layout_view.append_column(self.layout_column) # Do the same for the variant_view... variant_renderer = Gtk.CellRendererText() self.variant_column = Gtk.TreeViewColumn("Variant", variant_renderer, text=1) self.objects.variants.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.variant_view.append_column(self.variant_column) # And something similar for the model combobox... model_renderer = Gtk.CellRendererText() self.objects.model_combo.pack_start(model_renderer, True) self.objects.model_combo.add_attribute(model_renderer, "text", 1) self.objects.models.set_sort_column_id(1, Gtk.SortType.ASCENDING) # Populate the layout list GObject.idle_add(self.build_layout_list) # Populate the model list GObject.idle_add(self.build_model_list) def on_scene_called(self): """ Show the scene! """ # We are locked self.unlockbar.emit("locked") def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ self.unlockbar.cancel_authorization() return True
class Scene(quickstart.scenes.BaseScene): """ Keyboard settings. """ events = { "changed": ("model_combo", ), "cursor-changed": ("layout_view", "variant_view"), } def setxkbmap(self): """ Invokes setxkbmap and sets the currently selected layout, variant and model. """ subprocess.call( [ "setxkbmap", self.Keyboard.default_layout, self.Keyboard.default_variant if self.Keyboard.default_variant else "", "-model" if self.Keyboard.default_model else "", self.Keyboard.default_model if self.Keyboard.default_model else "" ] ) def on_model_combo_changed(self, combobox): """ Fired when the user changes the keyboard model. """ itr = self.objects.model_combo.get_active_iter() if not itr: return selected = self.objects.models.get_value(itr, 0) if selected == self.Keyboard.default_variant: return try: self.Keyboard.set(model=selected) self.default_model = itr self.setxkbmap() except: self.objects.model_combo.set_active_iter(self.default_model) def on_variant_view_cursor_changed(self, treeview): """ Fired when the user changes the variant. """ if self.building_list: return # selection sel = treeview.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.variants.get_value(itr, 0) if selected == self.Keyboard.default_variant: return try: self.Keyboard.set(variant=selected) self.default_variant = itr self.setxkbmap() except: sel.select_iter(self.default_variant) def on_layout_view_cursor_changed(self, treeview): """ Fired when the user changes the locale. """ # selection sel = treeview.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.layouts.get_value(itr, 0) if selected == self.Keyboard.default_layout: return # Reset default variant self.default_variant = None try: self.Keyboard.set(layout=selected, variant='') self.default = itr self.setxkbmap() except: sel.select_iter(self.default) # Rebuild variant list GObject.idle_add(self.build_variant_list, selected) def build_variant_list(self, layout): """ Populates the variant list. """ self.building_list = True self.objects.variants.clear() models, layouts = self.Keyboard.supported() variants = layouts[layout]["variants"] for variant in variants: for item, key in variant.items(): itr = self.objects.variants.append([item, key]) # Save the default variant if item == self.Keyboard.default_variant: self.default_variant = itr # No variant reciter = self.objects.variants.prepend(['', layouts[layout]["description"]]) sel = self.objects.variant_view.get_selection() if self.default_variant: sel.select_iter(self.default) else: # No variant, use reciter sel.select_iter(reciter) GObject.idle_add(self.objects.variant_view.scroll_to_cell, sel.get_selected_rows()[1][0]) self.building_list = False def build_model_list(self): """ Populates the model list. """ pc105 = None self.objects.models.clear() models = self.Keyboard.supported()[0] for item, key in models.items(): itr = self.objects.models.append([item, key]) # Save the default model if item == self.Keyboard.default_model: self.default_model = itr elif item == "pc105": # Fallback pc105 = itr if self.default_model: self.objects.model_combo.set_active_iter(self.default_model) else: self.objects.model_combo.set_active_iter(pc105) def build_layout_list(self): """ Populates the layout list. """ self.objects.layouts.clear() models, layouts = self.Keyboard.supported() for item, key in layouts.items(): itr = self.objects.layouts.append([item, key["description"]]) # Save iter if it's the default... if item == self.Keyboard.default_layout: self.default = itr if self.default: sel = self.objects.layout_view.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.layout_view.scroll_to_cell, sel.get_selected_rows()[1][0]) # Also build the variant view! GObject.idle_add(self.build_variant_list, self.Keyboard.default_layout) def on_locked(self, unlockbar): """ Fired when the scene has been locked. """ GObject.idle_add(self.objects.content.set_sensitive, False) def on_unlocked(self, unlockbar): """ Fired when the scene has been unlocked. """ GObject.idle_add(self.objects.content.set_sensitive, True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.locale1.set-keyboard") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Keyboard = Keyboard() self.building_list = False self.default = None self.default_variant = None self.default_model = None # Make the layout_view treeview working... layout_renderer = Gtk.CellRendererText() self.layout_column = Gtk.TreeViewColumn("Layout", layout_renderer, text=1) self.objects.layouts.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.layout_view.append_column(self.layout_column) # Do the same for the variant_view... variant_renderer = Gtk.CellRendererText() self.variant_column = Gtk.TreeViewColumn("Variant", variant_renderer, text=1) self.objects.variants.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.variant_view.append_column(self.variant_column) # And something similar for the model combobox... model_renderer = Gtk.CellRendererText() self.objects.model_combo.pack_start(model_renderer, True) self.objects.model_combo.add_attribute(model_renderer, "text", 1) self.objects.models.set_sort_column_id(1, Gtk.SortType.ASCENDING) # Populate the layout list GObject.idle_add(self.build_layout_list) # Populate the model list GObject.idle_add(self.build_model_list) def on_scene_called(self): """ Show the scene! """ # We are locked self.unlockbar.emit("locked") def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ self.unlockbar.cancel_authorization() return True
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "clicked": ( "change_fullname_button", "change_password_button", "change_groups_button", "delete_user_button", ), "response": ( "delete_user_dialog", "groups_dialog", ), "delete-event": ( "delete_user_dialog", "groups_dialog", ), "row_selected": ("user_list",), } on_edit_mode = False current_user = None current_user_properties = None current_user_box = None caller_user_box = None def handle_delete_events(self, window, event): """ Handles a dialog delete-event. """ return True on_delete_user_dialog_delete_event = on_groups_dialog_delete_event = handle_delete_events def on_locked(self, unlockbar): """ Fired when the Polkit auth has been revoked. """ # Switch again to the caller user self.objects.user_list.select_row(self.caller_user_box) def on_unlocked(self, unlockbar): """ Fired when the Polkit auth has been given. """ pass @quickstart.threads.on_idle def restore_edit_mode(self): """ Restores the full "edit" mode on the user details part. """ self.objects.fullname.show() self.objects.new_fullname.hide() fullname = self.current_user_properties.Get("(ss)", USER_IFACE, "Fullname" ) self.objects.fullname.set_markup(fullname if fullname else NO_FULLNAME_STRING) self.objects.change_fullname_button.set_image(self.objects.edit_image) self.on_edit_mode = False def on_change_fullname_button_clicked(self, button): """ Fired when the 'Edit fullname' button has been clicked. """ if not self.on_edit_mode: self.objects.fullname.hide() self.objects.new_fullname.show() self.objects.new_fullname.set_text( self.objects.fullname.get_text() if self.objects.fullname.get_text() != NO_FULLNAME_STRING.replace("<i>","").replace("</i>","") else "" ) self.objects.new_fullname.grab_focus() self.objects.change_fullname_button.set_image(self.objects.apply_image) self.on_edit_mode = True else: # Save changes self.current_user_properties.Set("(sss)", USER_IFACE, "Fullname", self.objects.new_fullname.get_text() ) self.current_user_box.set_username_and_fullname( self.current_user_box.user.get_text(), self.objects.new_fullname.get_text() ) self.restore_edit_mode() def on_change_password_button_clicked(self, button): """ Fired when the 'Click to change' button on the password field has been clicked. """ self.current_user.ChangePassword('(s)', os.environ["DISPLAY"]) def on_delete_user_button_clicked(self, button): """ Fired when the delete user button has been clicked. """ self.objects.delete_user_dialog.show() def on_delete_user_dialog_response(self, dialog, response_id): """ Fired when the delete user dialog got a response. """ if response_id == Gtk.ResponseType.OK: # Delete user # FIXME: error checking? self.current_user.DeleteUser("(b)", self.objects.delete_user_with_home.get_active()) # Reset delete_user_with_home self.objects.delete_user_with_home.set_active(False) dialog.hide() return False def on_change_groups_button_clicked(self, dialog): """ Fired when the change groups button has been clicked. """ self.objects.groups_store.clear() # Build group list user_in = self.GroupConfig.GetGroupsForUser('(s)', self.current_user_box._user) for group, name in self.GroupConfig.GetGroups().items(): name = name[0] if name in ("sudo",): # Skip continue self.objects.groups_store.insert_with_valuesv(-1, [0, 1, 2], [group, (name in user_in), name]) self.objects.groups_dialog.show() def on_groups_dialog_response(self, dialog, response_id): """ Fired when the groups dialog got a response. """ dialog.hide() return False def on_group_enabled_toggle_toggled(self, toggle, path): """ Fired when a toggle in the groups dialog has been... toggled. """ itr = self.objects.groups_store.get_iter(path) # Connect to group object group_properties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/group/%s" % self.objects.groups_store.get_value(itr, 0), "org.freedesktop.DBus.Properties", self.bus_cancellable ) # Get current group members members = group_properties.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members') if not toggle.get_active(): # Add user if not self.current_user_box._user in members: members.append(self.current_user_box._user) else: # Remove user if self.current_user_box._user in members: members.remove(self.current_user_box._user) # Set the new list try: group_properties.Set('(ssas)', 'org.semplicelinux.usersd.group', 'Members', members) except: # Failed/Aborted return # Finally set the enabled boolean in the row self.objects.groups_store.set_value(itr, 1, not toggle.get_active()) @quickstart.threads.on_idle def build_user_list(self): """ Builds the user list. """ # Clear self.objects.user_list.foreach(lambda x: x.destroy()) # "Add new" self.objects.user_list.add(AddNewBox()) try: for uid, details in self.UsersConfig.GetUsers().items(): if uid < 1000 or uid == 65534: # Skip system groups or nobody continue box = Gtk.ListBoxRow() box.add(UserBox(uid, details[0], details[1], details[2])) box.show() self.objects.user_list.add(box) # Current user? if uid == CURRENT_UID: # Yes! self.caller_user_box = box self.objects.user_list.select_row(self.caller_user_box) except: # Unable to build user list, probably caused by a non-working DBus connection self.objects.main.set_sensitive(False) self.objects.content.hide() self.objects.unable_to_connect_warning.show() def on_user_list_row_selected(self, listbox, row): """ Fired when a user has been selected. """ if row == None: return userbox = row.get_child() if userbox.add_new: return self.UsersConfig.ShowUserCreationUI('(sas)', os.environ["DISPLAY"], DEFAULT_GROUPS) self.current_user_box = userbox # Caller users cannot remove themselves if row == self.caller_user_box: self.objects.delete_user_button.hide() else: self.objects.delete_user_button.show() # Obtain DBus object for the current user self.current_user = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/user/%s" % userbox.uid, USER_IFACE, self.bus_cancellable ) self.current_user_properties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/user/%s" % userbox.uid, "org.freedesktop.DBus.Properties", self.bus_cancellable ) self.objects.fullname.set_markup(userbox.fullname.get_text() if userbox.fullname.get_text() else NO_FULLNAME_STRING) self.objects.user.set_text(userbox.user.get_text()) # Administrator switch if self.SudoGroup: self.objects.administrator.set_active( (userbox.user.get_text() in self.SudoGroup.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members')) ) def determine_row_sorting(self, row1, row2): """ Used to sort the user list by UIDs. """ child1 = row1.get_child() child2 = row2.get_child() if child2.add_new: return 1 elif child1.add_new: return 0 elif child1.uid > child2.uid: return 1 else: return 0 def prepare_scene(self): """ Fired when the module has just been loaded and we should setup things. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = { "UserListChanged": self.build_user_list } # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.usersd.manage") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) # Bind the locked state to the sensitiveness of the use_scrolled ScrolledWindow self.unlockbar.bind_property( "lock", self.objects.user_scrolled, "sensitive", GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN ) # ListBox sort self.objects.user_list.set_sort_func(self.determine_row_sorting) # Delete user dialog self.objects.delete_user_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN ) self.objects.delete_user_dialog.add_buttons( _("_Cancel"), Gtk.ResponseType.CANCEL, _("_Delete user"), Gtk.ResponseType.OK ) self.objects.delete_user_dialog.get_widget_for_response(Gtk.ResponseType.OK).get_style_context().add_class("destructive-action") # Groups dialog self.objects.groups_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN ) self.objects.groups_dialog.add_buttons( _("_Close"), Gtk.ResponseType.CLOSE ) self.group_enabled_toggle = Gtk.CellRendererToggle() self.objects.groups_treeview.append_column( Gtk.TreeViewColumn( "Enabled", self.group_enabled_toggle, active=1 ) ) self.group_enabled_toggle.connect("toggled", self.on_group_enabled_toggle_toggled) self.objects.groups_treeview.append_column( Gtk.TreeViewColumn( "Group description", Gtk.CellRendererText(), text=2 ) ) self.objects.groups_store.set_sort_column_id(0, Gtk.SortType.ASCENDING) self.objects.administrator.connect("notify::active", self.on_administrator_changed) def on_administrator_changed(self, widget, param): """ Fired when the user wants to change the sudo membership of the selected user. """ members = self.SudoGroup.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members') changed = False if widget.get_active(): # Add if not self.current_user_box._user in members: members.append(self.current_user_box._user) changed = True else: # Remove if self.current_user_box._user in members: members.remove(self.current_user_box._user) changed = True if changed: # Commit changes self.SudoGroup.Set('(ssas)', 'org.semplicelinux.usersd.group', 'Members', members) def on_scene_called(self): """ Fired when the user wants to see this scene. """ # Recover from a failed connection state self.objects.main.set_sensitive(True) self.objects.content.show() self.objects.unable_to_connect_warning.hide() # We are locked! self.unlockbar.emit("locked") # Estabilish DBus connection self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.UsersConfig = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd", "org.semplicelinux.usersd.user", self.bus_cancellable ) self.UsersConfig.connect( "g-signal", lambda proxy, sender, signal, params: self.signal_handlers[signal]() if signal in self.signal_handlers else None ) self.GroupConfig = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd", "org.semplicelinux.usersd.group", self.bus_cancellable ) # Estabilish DBus connection to the "sudo" group try: self.SudoGroup = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, self.GroupConfig.LookupGroup("(s)", "sudo"), "org.freedesktop.DBus.Properties", self.bus_cancellable ) except: self.SudoGroup = None if not self.SudoGroup: self.objects.administrator.set_sensitive(False) else: self.objects.administrator.set_sensitive(True) self.build_user_list()
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "clicked": ( "change_fullname_button", "change_password_button", "change_groups_button", "delete_user_button", ), "response": ( "delete_user_dialog", "groups_dialog", ), "delete-event": ( "delete_user_dialog", "groups_dialog", ), "row_selected": ("user_list", ), } on_edit_mode = False current_user = None current_user_properties = None current_user_box = None caller_user_box = None def handle_delete_events(self, window, event): """ Handles a dialog delete-event. """ return True on_delete_user_dialog_delete_event = on_groups_dialog_delete_event = handle_delete_events def on_locked(self, unlockbar): """ Fired when the Polkit auth has been revoked. """ # Switch again to the caller user self.objects.user_list.select_row(self.caller_user_box) def on_unlocked(self, unlockbar): """ Fired when the Polkit auth has been given. """ pass @quickstart.threads.on_idle def restore_edit_mode(self): """ Restores the full "edit" mode on the user details part. """ self.objects.fullname.show() self.objects.new_fullname.hide() fullname = self.current_user_properties.Get("(ss)", USER_IFACE, "Fullname") self.objects.fullname.set_markup( fullname if fullname else NO_FULLNAME_STRING) self.objects.change_fullname_button.set_image(self.objects.edit_image) self.on_edit_mode = False def on_change_fullname_button_clicked(self, button): """ Fired when the 'Edit fullname' button has been clicked. """ if not self.on_edit_mode: self.objects.fullname.hide() self.objects.new_fullname.show() self.objects.new_fullname.set_text( self.objects.fullname.get_text( ) if self.objects.fullname.get_text() != NO_FULLNAME_STRING. replace("<i>", "").replace("</i>", "") else "") self.objects.new_fullname.grab_focus() self.objects.change_fullname_button.set_image( self.objects.apply_image) self.on_edit_mode = True else: # Save changes self.current_user_properties.Set( "(sss)", USER_IFACE, "Fullname", self.objects.new_fullname.get_text()) self.current_user_box.set_username_and_fullname( self.current_user_box.user.get_text(), self.objects.new_fullname.get_text()) self.restore_edit_mode() def on_change_password_button_clicked(self, button): """ Fired when the 'Click to change' button on the password field has been clicked. """ self.current_user.ChangePassword('(s)', os.environ["DISPLAY"]) def on_delete_user_button_clicked(self, button): """ Fired when the delete user button has been clicked. """ self.objects.delete_user_dialog.show() def on_delete_user_dialog_response(self, dialog, response_id): """ Fired when the delete user dialog got a response. """ if response_id == Gtk.ResponseType.OK: # Delete user # FIXME: error checking? self.current_user.DeleteUser( "(b)", self.objects.delete_user_with_home.get_active()) # Reset delete_user_with_home self.objects.delete_user_with_home.set_active(False) dialog.hide() return False def on_change_groups_button_clicked(self, dialog): """ Fired when the change groups button has been clicked. """ self.objects.groups_store.clear() # Build group list user_in = self.GroupConfig.GetGroupsForUser( '(s)', self.current_user_box._user) for group, name in self.GroupConfig.GetGroups().items(): name = name[0] if name in ("sudo", ): # Skip continue self.objects.groups_store.insert_with_valuesv( -1, [0, 1, 2], [group, (name in user_in), name]) self.objects.groups_dialog.show() def on_groups_dialog_response(self, dialog, response_id): """ Fired when the groups dialog got a response. """ dialog.hide() return False def on_group_enabled_toggle_toggled(self, toggle, path): """ Fired when a toggle in the groups dialog has been... toggled. """ itr = self.objects.groups_store.get_iter(path) # Connect to group object group_properties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/group/%s" % self.objects.groups_store.get_value(itr, 0), "org.freedesktop.DBus.Properties", self.bus_cancellable) # Get current group members members = group_properties.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members') if not toggle.get_active(): # Add user if not self.current_user_box._user in members: members.append(self.current_user_box._user) else: # Remove user if self.current_user_box._user in members: members.remove(self.current_user_box._user) # Set the new list try: group_properties.Set('(ssas)', 'org.semplicelinux.usersd.group', 'Members', members) except: # Failed/Aborted return # Finally set the enabled boolean in the row self.objects.groups_store.set_value(itr, 1, not toggle.get_active()) @quickstart.threads.on_idle def build_user_list(self): """ Builds the user list. """ # Clear self.objects.user_list.foreach(lambda x: x.destroy()) # "Add new" self.objects.user_list.add(AddNewBox()) try: for uid, details in self.UsersConfig.GetUsers().items(): if uid < 1000 or uid == 65534: # Skip system groups or nobody continue box = Gtk.ListBoxRow() box.add(UserBox(uid, details[0], details[1], details[2])) box.show() self.objects.user_list.add(box) # Current user? if uid == CURRENT_UID: # Yes! self.caller_user_box = box self.objects.user_list.select_row(self.caller_user_box) except: # Unable to build user list, probably caused by a non-working DBus connection self.objects.main.set_sensitive(False) self.objects.content.hide() self.objects.unable_to_connect_warning.show() def on_user_list_row_selected(self, listbox, row): """ Fired when a user has been selected. """ if row == None: return userbox = row.get_child() if userbox.add_new: return self.UsersConfig.ShowUserCreationUI('(sas)', os.environ["DISPLAY"], DEFAULT_GROUPS) self.current_user_box = userbox # Caller users cannot remove themselves if row == self.caller_user_box: self.objects.delete_user_button.hide() else: self.objects.delete_user_button.show() # Obtain DBus object for the current user self.current_user = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/user/%s" % userbox.uid, USER_IFACE, self.bus_cancellable) self.current_user_properties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/user/%s" % userbox.uid, "org.freedesktop.DBus.Properties", self.bus_cancellable) self.objects.fullname.set_markup(userbox.fullname.get_text( ) if userbox.fullname.get_text() else NO_FULLNAME_STRING) self.objects.user.set_text(userbox.user.get_text()) # Administrator switch if self.SudoGroup: self.objects.administrator.set_active( (userbox.user.get_text() in self.SudoGroup.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members'))) def determine_row_sorting(self, row1, row2): """ Used to sort the user list by UIDs. """ child1 = row1.get_child() child2 = row2.get_child() if child2.add_new: return 1 elif child1.add_new: return 0 elif child1.uid > child2.uid: return 1 else: return 0 def prepare_scene(self): """ Fired when the module has just been loaded and we should setup things. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = {"UserListChanged": self.build_user_list} # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.usersd.manage") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) # Bind the locked state to the sensitiveness of the use_scrolled ScrolledWindow self.unlockbar.bind_property( "lock", self.objects.user_scrolled, "sensitive", GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN) # ListBox sort self.objects.user_list.set_sort_func(self.determine_row_sorting) # Delete user dialog self.objects.delete_user_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN) self.objects.delete_user_dialog.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, _("_Delete user"), Gtk.ResponseType.OK) self.objects.delete_user_dialog.get_widget_for_response( Gtk.ResponseType.OK).get_style_context().add_class( "destructive-action") # Groups dialog self.objects.groups_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN) self.objects.groups_dialog.add_buttons(_("_Close"), Gtk.ResponseType.CLOSE) self.group_enabled_toggle = Gtk.CellRendererToggle() self.objects.groups_treeview.append_column( Gtk.TreeViewColumn("Enabled", self.group_enabled_toggle, active=1)) self.group_enabled_toggle.connect("toggled", self.on_group_enabled_toggle_toggled) self.objects.groups_treeview.append_column( Gtk.TreeViewColumn("Group description", Gtk.CellRendererText(), text=2)) self.objects.groups_store.set_sort_column_id(0, Gtk.SortType.ASCENDING) self.objects.administrator.connect("notify::active", self.on_administrator_changed) def on_administrator_changed(self, widget, param): """ Fired when the user wants to change the sudo membership of the selected user. """ members = self.SudoGroup.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members') changed = False if widget.get_active(): # Add if not self.current_user_box._user in members: members.append(self.current_user_box._user) changed = True else: # Remove if self.current_user_box._user in members: members.remove(self.current_user_box._user) changed = True if changed: # Commit changes self.SudoGroup.Set('(ssas)', 'org.semplicelinux.usersd.group', 'Members', members) def on_scene_called(self): """ Fired when the user wants to see this scene. """ # Recover from a failed connection state self.objects.main.set_sensitive(True) self.objects.content.show() self.objects.unable_to_connect_warning.hide() # We are locked! self.unlockbar.emit("locked") # Estabilish DBus connection self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.UsersConfig = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd", "org.semplicelinux.usersd.user", self.bus_cancellable) self.UsersConfig.connect( "g-signal", lambda proxy, sender, signal, params: self.signal_handlers[signal] () if signal in self.signal_handlers else None) self.GroupConfig = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd", "org.semplicelinux.usersd.group", self.bus_cancellable) # Estabilish DBus connection to the "sudo" group try: self.SudoGroup = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, self.GroupConfig.LookupGroup("(s)", "sudo"), "org.freedesktop.DBus.Properties", self.bus_cancellable) except: self.SudoGroup = None if not self.SudoGroup: self.objects.administrator.set_sensitive(False) else: self.objects.administrator.set_sensitive(True) self.build_user_list()
class Scene(quickstart.scenes.BaseScene): """ Time & Date settings. """ events = { "delete-event": ("select_timezone_dialog", ), "realize": ("select_timezone_dialog", ), "button-press-event": ("main", ), "clicked": ("time_button", "location_button", "apply_timezone"), "toggled": ("calendar_button", "ntp_enabled"), "value-changed": ("hours_adjustment", "minutes_adjustment", "seconds_adjustment"), "wrapped": ("hours", ), "output": ("hours", "minutes", "seconds") } @quickstart.threads.on_idle def build_timezone_list(self): """ Builds the timezone list. """ self.objects.timezones.clear() for item, key in self.TimeZone.supported.items(): for zone in key: zone1 = "%s/%s" % (item, zone) itr = self.objects.timezones.append([zone1, zone]) # Save the iter if it's the default if zone1 == self.TimeZone.default: # save the iter! ;) self.default = itr if self.default: sel = self.objects.timezone_treeview.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.timezone_treeview.scroll_to_cell, sel.get_selected_rows()[1][0]) GObject.idle_add(self.objects.timezone_treeview.grab_focus) def on_apply_timezone_clicked(self, button): """ Fired when the apply_timezone button has been clicked. """ GObject.idle_add(self.objects.select_timezone_dialog.hide) # Get selected sel = self.objects.timezone_treeview.get_selection() if not sel: return model, itr = sel.get_selected() if not itr: return selected = self.objects.timezones.get_value(itr, 0) if selected == self.objects.location_button.get_label(): return # Ugly! try: self.TimeDate.SetTimezone('(sb)', selected, True) self.default = itr self.current_datetime = datetime.datetime.now() except: sel.select_iter(self.default) self.objects.location_button.set_label( self.objects.timezones.get_value(self.default, 0)) def on_select_timezone_dialog_realize(self, widget): """ Fired when the select_timezone_dialog is going to be shown. """ self.build_timezone_list() def on_select_timezone_dialog_delete_event(self, widget, event): """ Fired when the select_timezone_dialog is going to be destroyed. """ widget.hide() return True # do not destroy def on_main_button_press_event(self, eventbox, event): """ Fired when the user has clicked on the eventbox. We use this call to return to the 'read-only' mode on the time view. """ if self.objects.time_modify.props.visible and event.type == Gdk.EventType.BUTTON_PRESS: self.refresh_infos() self.objects.time_modify.hide() self.objects.time_button.show() def on_spinbutton_output(self, spinbutton): """ Fired when a spinbutton has been changed. We use this method to ensure to have two digits everytime. Code comes from http://stackoverflow.com/a/9998968 (thanks!) (not connected to anything, due to a current limitation of quickstart we will associate on_*widget*_output to this method later) """ spinbutton.set_text('{:02d}'.format( int(spinbutton.get_adjustment().get_value()))) return True on_hours_output = on_spinbutton_output on_minutes_output = on_spinbutton_output on_seconds_output = on_spinbutton_output def on_hours_wrapped(self, spinbutton): """ Fired when the hours spinbutton has been wrapped. """ if self.timezone12: if int(spinbutton.get_text()) == 1: # +1 self.hour_offset = 0 if self.hour_offset == 12 else 12 else: # -1 self.hour_offset = 12 if self.hour_offset == 0 else 0 # Reset time self.on_hours_adjustment_value_changed(spinbutton.get_adjustment()) def set_time(self): """ Actually sets the time. """ print("Setting time...") self.TimeDate.SetTime( '(xbb)', time.mktime(self.current_datetime.timetuple()) * 1000000, False, False) def on_hours_adjustment_value_changed(self, adjustment): """ Fired when the hours adjustment has been changed. """ if self.hours_input: value = int(adjustment.get_value()) if self.timezone12: value += self.hour_offset self.current_datetime = self.current_datetime.replace( hour=value if not self.timezone12 else ( 0 if value == 24 else value)) if self.timezone12: self.objects.timezone12_edit.set_text( self.current_datetime.strftime("%p")) self.set_time() def on_minutes_adjustment_value_changed(self, adjustment): """ Fired when the minutes adjustment has been changed. """ if self.minutes_input: self.current_datetime = self.current_datetime.replace( minute=int(adjustment.get_value())) self.set_time() def on_seconds_adjustment_value_changed(self, adjustment): """ Fired when the seconds adjustment has been changed. """ if self.seconds_input: self.current_datetime = self.current_datetime.replace( second=int(adjustment.get_value())) self.set_time() def on_location_button_clicked(self, button): """ Fired when the location_button has been clicked. """ GObject.idle_add(self.objects.select_timezone_dialog.present) def on_time_button_clicked(self, button): """ Fired when the time_button has been clicked. """ self.hours_input = self.minutes_input = self.seconds_input = False button.hide() self.objects.time_modify.show() # Load the adjustments with current data time = self.current_datetime.time() # Handle 12-hour timezones if not self.timezone12: # 24 hour self.objects.hours_adjustment.set_value(time.hour) self.objects.timezone12_edit.hide() else: # 12 hour self.objects.hours_adjustment.set_value(int(time.strftime("%I"))) self.objects.timezone12_edit.set_text( self.current_datetime.strftime("%p")) self.objects.minutes_adjustment.set_value(time.minute) self.objects.seconds_adjustment.set_value(time.second) def on_calendar_button_toggled(self, button): """ Fired when the calendar_button has been toggled. """ if button.get_active(): self.calendar_popover.show_all() def on_day_selected(self, calendar): """ Fired when a day in the calendar has been selected. """ date = calendar.get_date() self.current_datetime = self.current_datetime.replace(year=date[0], month=date[1] + 1, day=date[2]) self.objects.calendar_button.set_label( self.current_datetime.date().strftime("%A %d %B %Y")) # Update time if we should if self.date_input: self.set_time() def on_ntp_enabled_toggled(self, checkbox): """ Fired when the ntp_enabled checkbox has been toggled. """ if self.unlockbar.current_state == ActionResponse.UNLOCK: print("Setting NTP to %s" % checkbox.get_active()) try: self.TimeDate.SetNTP('(bb)', checkbox.get_active(), False) except GLib.GError as error: # FIXME? Here it raises FileNotFound error, I think # is some Debian bug? pass # Set sensitivity on the manual container self.objects.manual_container.set_sensitive(not checkbox.get_active()) @quickstart.threads.on_idle def refresh_infos(self): """ Refreshes the information displayed. """ _datetime = datetime.datetime.now() self.objects.time.set_text(_datetime.time().strftime("%X")) @quickstart.threads.on_idle def update_date(self): """ Updates the date. """ date = self.current_datetime.date() self.date_input = False self.calendar.select_month(date.month - 1, date.year) self.calendar.select_day(date.day) self.date_input = True @quickstart.threads.on_idle def update_time(self): """ Updates the time. """ self.current_datetime += datetime.timedelta(seconds=1) time = self.current_datetime.time() if time.hour == 00 and time.minute == 00 and time.second == 00: # Day changed self.update_date() # Update the current visible time object if self.objects.time_modify.props.visible: if time.minute == 00 and time.second == 00: self.hours_input = False self.objects.hours_adjustment.set_value(time.hour) if time.second == 00: self.minutes_input = False self.objects.minutes_adjustment.set_value(time.minute) self.seconds_input = False self.objects.seconds_adjustment.set_value(time.second) self.hours_input = self.minutes_input = self.seconds_input = True else: self.objects.time.set_text(time.strftime("%X")) def on_locked(self, unlockbar): """ Fired when the user (or policykit) has locked the special temporary privilegies. """ # Locked, disable sensitivity on everything... self.objects.container.set_sensitive(False) def on_unlocked(self, unlockbar): """ Fired when the user has got the special temporary privilegies. """ # Unlocked, re-set sensitivity self.objects.container.set_sensitive(True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main self.default = None self.TimeZone = TimeZone() self.hour_offset = 0 self.timezone12 = None # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.timedate1.set-time") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main_box.pack_start(self.unlockbar, False, False, 0) # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.TimeDate = Gio.DBusProxy.new_sync(self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", BUS_NAME, self.bus_cancellable) self.TimeDateProperties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", "org.freedesktop.DBus.Properties", self.bus_cancellable ) # Really we should create a new proxy to get the properties?! #self.refresh_infos() # Set-up select timezone dialog self.objects.timezone_treeview.append_column( Gtk.TreeViewColumn("Timezone", Gtk.CellRendererText(), text=0)) self.objects.timezones.set_sort_column_id(0, Gtk.SortType.ASCENDING) # Create calendar popover self.calendar = Gtk.Calendar() self.calendar.connect("day-selected", self.on_day_selected) self.calendar_popover = Gtk.Popover.new(self.objects.calendar_button) self.calendar_popover.set_modal(True) self.calendar_popover.connect( "closed", lambda x: self.objects.calendar_button.set_active(False)) self.calendar_popover.add(self.calendar) # Set appropriate font size and weight context = self.objects.time.create_pango_context() desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE * 80) # Size self.objects.time.override_font(desc) self.objects.hours.override_font(desc) self.objects.minutes.override_font(desc) self.objects.seconds.override_font(desc) self.objects.timezone12_edit.override_font(desc) # Set mask on the main eventbox self.objects.main.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) # Styling self.objects.time_button.get_style_context().add_class("no-borders") self.objects.location_button.get_style_context().add_class( "no-borders") self.objects.calendar_button.get_style_context().add_class( "no-borders") def on_scene_called(self): """ Called when switching to this scene. We will handle here all timeouts to ensure we syncronize the time label with the actual time. """ self.objects.main.show_all() self.objects.time_modify.hide() # We are locked self.unlockbar.emit("locked") self.current_datetime = datetime.datetime.now() self.timezone12 = (not self.current_datetime.strftime("%p") == "") adj = self.objects.hours.get_adjustment() if self.timezone12: adj.set_upper(12) adj.set_lower(1) else: adj.set_upper(23) adj.set_lower(0) if self.current_datetime.time().hour > 12: self.hour_offset = 12 self.update_date() self.update_time() # NTP ntp = bool(self.TimeDateProperties.Get('(ss)', BUS_NAME, 'NTP')) self.objects.ntp_enabled.set_active(ntp) # Timezone self.objects.location_button.set_label( self.TimeDateProperties.Get('(ss)', BUS_NAME, 'Timezone')) self.label_timeout = GLib.timeout_add_seconds(1, self.update_time) def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ GLib.source_remove(self.label_timeout) self.unlockbar.cancel_authorization() self.bus_cancellable.cancel() return True
class Scene(quickstart.scenes.BaseScene): """ Time & Date settings. """ events = { "delete-event" : ("select_timezone_dialog",), "realize": ("select_timezone_dialog",), "button-press-event" : ("main",), "clicked" : ("time_button", "location_button", "apply_timezone"), "toggled" : ("calendar_button", "ntp_enabled"), "value-changed" : ("hours_adjustment", "minutes_adjustment", "seconds_adjustment"), "wrapped" : ("hours",), "output" : ("hours", "minutes", "seconds") } @quickstart.threads.on_idle def build_timezone_list(self): """ Builds the timezone list. """ self.objects.timezones.clear() for item, key in self.TimeZone.supported.items(): for zone in key: zone1 = "%s/%s" % (item, zone) itr = self.objects.timezones.append([zone1, zone]) # Save the iter if it's the default if zone1 == self.TimeZone.default: # save the iter! ;) self.default = itr if self.default: sel = self.objects.timezone_treeview.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.timezone_treeview.scroll_to_cell, sel.get_selected_rows()[1][0]) GObject.idle_add(self.objects.timezone_treeview.grab_focus) def on_apply_timezone_clicked(self, button): """ Fired when the apply_timezone button has been clicked. """ GObject.idle_add(self.objects.select_timezone_dialog.hide) # Get selected sel = self.objects.timezone_treeview.get_selection() if not sel: return model, itr = sel.get_selected() if not itr: return selected = self.objects.timezones.get_value(itr, 0) if selected == self.objects.location_button.get_label(): return # Ugly! try: self.TimeDate.SetTimezone( '(sb)', selected, True ) self.default = itr self.current_datetime = datetime.datetime.now() except: sel.select_iter(self.default) self.objects.location_button.set_label(self.objects.timezones.get_value(self.default, 0)) def on_select_timezone_dialog_realize(self, widget): """ Fired when the select_timezone_dialog is going to be shown. """ self.build_timezone_list() def on_select_timezone_dialog_delete_event(self, widget, event): """ Fired when the select_timezone_dialog is going to be destroyed. """ widget.hide() return True # do not destroy def on_main_button_press_event(self, eventbox, event): """ Fired when the user has clicked on the eventbox. We use this call to return to the 'read-only' mode on the time view. """ if self.objects.time_modify.props.visible and event.type == Gdk.EventType.BUTTON_PRESS: self.refresh_infos() self.objects.time_modify.hide() self.objects.time_button.show() def on_spinbutton_output(self, spinbutton): """ Fired when a spinbutton has been changed. We use this method to ensure to have two digits everytime. Code comes from http://stackoverflow.com/a/9998968 (thanks!) (not connected to anything, due to a current limitation of quickstart we will associate on_*widget*_output to this method later) """ spinbutton.set_text('{:02d}'.format(int(spinbutton.get_adjustment().get_value()))) return True on_hours_output = on_spinbutton_output on_minutes_output = on_spinbutton_output on_seconds_output = on_spinbutton_output def on_hours_wrapped(self, spinbutton): """ Fired when the hours spinbutton has been wrapped. """ if self.timezone12: if int(spinbutton.get_text()) == 1: # +1 self.hour_offset = 0 if self.hour_offset == 12 else 12 else: # -1 self.hour_offset = 12 if self.hour_offset == 0 else 0 # Reset time self.on_hours_adjustment_value_changed(spinbutton.get_adjustment()) def set_time(self): """ Actually sets the time. """ print("Setting time...") self.TimeDate.SetTime('(xbb)', time.mktime(self.current_datetime.timetuple()) * 1000000, False, False) def on_hours_adjustment_value_changed(self, adjustment): """ Fired when the hours adjustment has been changed. """ if self.hours_input: value = int(adjustment.get_value()) if self.timezone12: value += self.hour_offset self.current_datetime = self.current_datetime.replace( hour=value if not self.timezone12 else ( 0 if value == 24 else value ) ) if self.timezone12: self.objects.timezone12_edit.set_text(self.current_datetime.strftime("%p")) self.set_time() def on_minutes_adjustment_value_changed(self, adjustment): """ Fired when the minutes adjustment has been changed. """ if self.minutes_input: self.current_datetime = self.current_datetime.replace(minute=int(adjustment.get_value())) self.set_time() def on_seconds_adjustment_value_changed(self, adjustment): """ Fired when the seconds adjustment has been changed. """ if self.seconds_input: self.current_datetime = self.current_datetime.replace(second=int(adjustment.get_value())) self.set_time() def on_location_button_clicked(self, button): """ Fired when the location_button has been clicked. """ GObject.idle_add(self.objects.select_timezone_dialog.present) def on_time_button_clicked(self, button): """ Fired when the time_button has been clicked. """ self.hours_input = self.minutes_input = self.seconds_input = False button.hide() self.objects.time_modify.show() # Load the adjustments with current data time = self.current_datetime.time() # Handle 12-hour timezones if not self.timezone12: # 24 hour self.objects.hours_adjustment.set_value(time.hour) self.objects.timezone12_edit.hide() else: # 12 hour self.objects.hours_adjustment.set_value(int(time.strftime("%I"))) self.objects.timezone12_edit.set_text(self.current_datetime.strftime("%p")) self.objects.minutes_adjustment.set_value(time.minute) self.objects.seconds_adjustment.set_value(time.second) def on_calendar_button_toggled(self, button): """ Fired when the calendar_button has been toggled. """ if button.get_active(): self.calendar_popover.show_all() def on_day_selected(self, calendar): """ Fired when a day in the calendar has been selected. """ date = calendar.get_date() self.current_datetime = self.current_datetime.replace( year=date[0], month=date[1]+1, day=date[2] ) self.objects.calendar_button.set_label(self.current_datetime.date().strftime("%A %d %B %Y")) # Update time if we should if self.date_input: self.set_time() def on_ntp_enabled_toggled(self, checkbox): """ Fired when the ntp_enabled checkbox has been toggled. """ if self.unlockbar.current_state == ActionResponse.UNLOCK: print("Setting NTP to %s" % checkbox.get_active()) try: self.TimeDate.SetNTP('(bb)', checkbox.get_active(), False) except GLib.GError as error: # FIXME? Here it raises FileNotFound error, I think # is some Debian bug? pass # Set sensitivity on the manual container self.objects.manual_container.set_sensitive(not checkbox.get_active()) @quickstart.threads.on_idle def refresh_infos(self): """ Refreshes the information displayed. """ _datetime = datetime.datetime.now() self.objects.time.set_text(_datetime.time().strftime("%X")) @quickstart.threads.on_idle def update_date(self): """ Updates the date. """ date = self.current_datetime.date() self.date_input = False self.calendar.select_month(date.month-1, date.year) self.calendar.select_day(date.day) self.date_input = True @quickstart.threads.on_idle def update_time(self): """ Updates the time. """ self.current_datetime += datetime.timedelta(seconds=1) time = self.current_datetime.time() if time.hour == 00 and time.minute == 00 and time.second == 00: # Day changed self.update_date() # Update the current visible time object if self.objects.time_modify.props.visible: if time.minute == 00 and time.second == 00: self.hours_input = False self.objects.hours_adjustment.set_value(time.hour) if time.second == 00: self.minutes_input = False self.objects.minutes_adjustment.set_value(time.minute) self.seconds_input = False self.objects.seconds_adjustment.set_value(time.second) self.hours_input = self.minutes_input = self.seconds_input = True else: self.objects.time.set_text(time.strftime("%X")) def on_locked(self, unlockbar): """ Fired when the user (or policykit) has locked the special temporary privilegies. """ # Locked, disable sensitivity on everything... self.objects.container.set_sensitive(False) def on_unlocked(self, unlockbar): """ Fired when the user has got the special temporary privilegies. """ # Unlocked, re-set sensitivity self.objects.container.set_sensitive(True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main self.default = None self.TimeZone = TimeZone() self.hour_offset = 0 self.timezone12 = None # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.timedate1.set-time") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main_box.pack_start(self.unlockbar, False, False, 0) # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.TimeDate = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", BUS_NAME, self.bus_cancellable ) self.TimeDateProperties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", "org.freedesktop.DBus.Properties", self.bus_cancellable ) # Really we should create a new proxy to get the properties?! #self.refresh_infos() # Set-up select timezone dialog self.objects.timezone_treeview.append_column( Gtk.TreeViewColumn( "Timezone", Gtk.CellRendererText(), text=0 ) ) self.objects.timezones.set_sort_column_id(0, Gtk.SortType.ASCENDING) # Create calendar popover self.calendar = Gtk.Calendar() self.calendar.connect("day-selected", self.on_day_selected) self.calendar_popover = Gtk.Popover.new(self.objects.calendar_button) self.calendar_popover.set_modal(True) self.calendar_popover.connect("closed", lambda x: self.objects.calendar_button.set_active(False)) self.calendar_popover.add(self.calendar) # Set appropriate font size and weight context = self.objects.time.create_pango_context() desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE*80) # Size self.objects.time.override_font(desc) self.objects.hours.override_font(desc) self.objects.minutes.override_font(desc) self.objects.seconds.override_font(desc) self.objects.timezone12_edit.override_font(desc) # Set mask on the main eventbox self.objects.main.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) # Styling self.objects.time_button.get_style_context().add_class("no-borders") self.objects.location_button.get_style_context().add_class("no-borders") self.objects.calendar_button.get_style_context().add_class("no-borders") def on_scene_called(self): """ Called when switching to this scene. We will handle here all timeouts to ensure we syncronize the time label with the actual time. """ self.objects.main.show_all() self.objects.time_modify.hide() # We are locked self.unlockbar.emit("locked") self.current_datetime = datetime.datetime.now() self.timezone12 = (not self.current_datetime.strftime("%p") == "") adj = self.objects.hours.get_adjustment() if self.timezone12: adj.set_upper(12) adj.set_lower(1) else: adj.set_upper(23) adj.set_lower(0) if self.current_datetime.time().hour > 12: self.hour_offset = 12 self.update_date() self.update_time() # NTP ntp = bool(self.TimeDateProperties.Get('(ss)', BUS_NAME, 'NTP')) self.objects.ntp_enabled.set_active(ntp) # Timezone self.objects.location_button.set_label(self.TimeDateProperties.Get('(ss)', BUS_NAME, 'Timezone')) self.label_timeout = GLib.timeout_add_seconds(1, self.update_time) def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ GLib.source_remove(self.label_timeout) self.unlockbar.cancel_authorization() self.bus_cancellable.cancel() return True