class Module: name = "display" comment = _("Manage display settings") category = "hardware" def __init__(self, content_box): keywords = _("display, screen, monitor, layout, resolution, dual, lcd") self.sidePage = SidePage(_("Display"), "cs-display", keywords, content_box, module=self) self.display_c_widget = None def on_module_selected(self): if not self.loaded: print("Loading Display module") self.sidePage.stack = SettingsStack() self.sidePage.add_widget(self.sidePage.stack) page = SettingsPage() self.sidePage.stack.add_titled(page, "layout", _("Layout")) try: settings = page.add_section(_("Layout")) widget = SettingsWidget() widget.set_border_width(0) widget.set_margin_start(0) widget.set_margin_end(0) content = self.sidePage.content_box.c_manager.get_c_widget( "display") widget.pack_start(content, True, True, 0) self.display_c_widget = content settings.add_row(widget) except Exception as detail: print(detail) page = SettingsPage() self.sidePage.stack.add_titled(page, "settings", _("Settings")) settings = page.add_section(_("Settings")) switch = GSettingsSwitch( _("Disable automatic screen rotation"), "org.cinnamon.settings-daemon.peripherals.touchscreen", "orientation-lock") switch.set_tooltip_text( _("Select this option to disable automatic screen rotation on hardware equipped with supported accelerometers." )) settings.add_row(switch) def on_navigate_out_of_module(self): if self.display_c_widget: self.display_c_widget.hide()
class Module: name = "display" comment = _("Manage display settings") category = "hardware" def __init__(self, content_box): keywords = _("display, screen, monitor, layout, resolution, dual, lcd") self.sidePage = SidePage(_("Display"), "cs-display", keywords, content_box, module=self) def on_module_selected(self): if not self.loaded: print("Loading Display module") self.sidePage.stack = SettingsStack() self.sidePage.add_widget(self.sidePage.stack) page = SettingsPage() self.sidePage.stack.add_titled(page, "layout", _("Layout")) try: settings = page.add_section(_("Layout")) widget = SettingsWidget() content = self.sidePage.content_box.c_manager.get_c_widget( "display") widget.pack_start(content, True, True, 0) settings.add_row(widget) except Exception as detail: print(detail) page = SettingsPage() self.sidePage.stack.add_titled(page, "settings", _("Settings")) settings = page.add_section(_("Settings")) ui_scales = [[0, _("Auto")], [1, _("Normal")], [2, _("Double (Hi-DPI)")]] combo = GSettingsComboBox(_("User interface scaling:"), "org.cinnamon.desktop.interface", "scaling-factor", ui_scales, valtype=int) settings.add_row(combo) switch = GSettingsSwitch( _("Disable automatic screen rotation"), "org.cinnamon.settings-daemon.peripherals.touchscreen", "orientation-lock") switch.set_tooltip_text( _("Select this option to disable automatic screen rotation on hardware equipped with supported accelerometers." )) settings.add_row(switch)
class Module: name = "online-accounts" comment = _("Connect to your online accounts") category = "prefs" def __init__(self, content_box): keywords = _("google, facebook, twitter, yahoo, web, online, chat, calendar, mail, contact, owncloud, kerberos, imap, smtp, pocket, readitlater, account") self.sidePage = SidePage(_("Online Accounts"), "cs-online-accounts", keywords, content_box, 560, module=self) def on_module_selected(self): if not self.loaded: print("Loading Online Account module") page = SettingsPage() self.sidePage.add_widget(page) image = Gtk.Image.new_from_icon_name("help-contents-symbolic", Gtk.IconSize.BUTTON) button = Gtk.Button(_("Information about GNOME Online Accounts")) button.set_image(image) button.set_always_show_image(True) button.connect("clicked", self.on_button_clicked) page.pack_start(button, False, True, 0) try: content = self.sidePage.content_box.c_manager.get_c_widget("online-accounts") content.set_no_show_all(True) page.pack_start(content, True, True, 0) except Exception as detail: print(detail) page.expand = True def on_button_clicked(self, button): gladefile = "/usr/share/cinnamon/cinnamon-settings/cinnamon-online-accounts-info.ui" self.builder = Gtk.Builder() self.builder.set_translation_domain('cinnamon') self.builder.add_from_file(gladefile) self.window = self.builder.get_object("main_window") self.window.set_title(_("Online Accounts")) self.window.set_icon_name("cs-online-accounts") self.window.show()
class Module: name = "backgrounds" category = "appear" comment = _("Change your desktop's background") def __init__(self, content_box): keywords = _("background, picture, slideshow") self.sidePage = SidePage(_("Backgrounds"), "cs-backgrounds", keywords, content_box, module=self) def on_module_selected(self): if not self.loaded: print("Loading Backgrounds module") self.sidePage.stack = SettingsStack() self.sidePage.add_widget(self.sidePage.stack) self.shown_collection = None # Which collection is displayed in the UI self._background_schema = Gio.Settings( schema="org.cinnamon.desktop.background") self._slideshow_schema = Gio.Settings( schema="org.cinnamon.desktop.background.slideshow") self._slideshow_schema.connect("changed::slideshow-enabled", self.on_slideshow_enabled_changed) self.add_folder_dialog = Gtk.FileChooserDialog( title=_("Add Folder"), action=Gtk.FileChooserAction.SELECT_FOLDER, buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) self.xdg_pictures_directory = os.path.expanduser("~/Pictures") xdg_config = os.path.expanduser("~/.config/user-dirs.dirs") if os.path.exists(xdg_config) and os.path.exists( "/usr/bin/xdg-user-dir"): path = subprocess.check_output(["xdg-user-dir", "PICTURES" ]).decode("utf-8").rstrip("\n") if os.path.exists(path): self.xdg_pictures_directory = path self.get_user_backgrounds() # Images mainbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 2) mainbox.expand = True mainbox.set_border_width(8) self.sidePage.stack.add_titled(mainbox, "images", _("Images")) left_vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) right_vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) folder_scroller = Gtk.ScrolledWindow.new(None, None) folder_scroller.set_shadow_type(Gtk.ShadowType.IN) folder_scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) folder_scroller.set_property("min-content-width", 150) self.folder_tree = Gtk.TreeView.new() self.folder_tree.set_headers_visible(False) folder_scroller.add(self.folder_tree) button_toolbar = Gtk.Toolbar.new() button_toolbar.set_icon_size(1) Gtk.StyleContext.add_class( Gtk.Widget.get_style_context(button_toolbar), "inline-toolbar") self.add_folder_button = Gtk.ToolButton.new(None, None) self.add_folder_button.set_icon_name("list-add-symbolic") self.add_folder_button.set_tooltip_text(_("Add new folder")) self.add_folder_button.connect("clicked", lambda w: self.add_new_folder()) self.remove_folder_button = Gtk.ToolButton.new(None, None) self.remove_folder_button.set_icon_name("list-remove-symbolic") self.remove_folder_button.set_tooltip_text( _("Remove selected folder")) self.remove_folder_button.connect("clicked", lambda w: self.remove_folder()) button_toolbar.insert(self.add_folder_button, 0) button_toolbar.insert(self.remove_folder_button, 1) image_scroller = Gtk.ScrolledWindow.new(None, None) image_scroller.set_shadow_type(Gtk.ShadowType.IN) image_scroller.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.icon_view = ThreadedIconView() image_scroller.add(self.icon_view) self.icon_view.connect("selection-changed", self.on_wallpaper_selection_changed) right_vbox.pack_start(image_scroller, True, True, 0) left_vbox.pack_start(folder_scroller, True, True, 0) left_vbox.pack_start(button_toolbar, False, False, 0) mainbox.pack_start(left_vbox, False, False, 2) mainbox.pack_start(right_vbox, True, True, 2) left_vbox.set_border_width(2) right_vbox.set_border_width(2) self.collection_store = Gtk.ListStore( bool, # is separator str, # Icon name str, # Display name str, # Path str) # Type of collection cell = Gtk.CellRendererText() cell.set_alignment(0, 0) pb_cell = Gtk.CellRendererPixbuf() self.folder_column = Gtk.TreeViewColumn() self.folder_column.pack_start(pb_cell, False) self.folder_column.pack_start(cell, True) self.folder_column.add_attribute(pb_cell, "icon-name", 1) self.folder_column.add_attribute(cell, "text", 2) self.folder_column.set_alignment(0) self.folder_tree.append_column(self.folder_column) self.folder_tree.connect("cursor-changed", self.on_folder_source_changed) self.get_system_backgrounds() tree_separator = [True, None, None, None, None] self.collection_store.append(tree_separator) if len(self.user_backgrounds) > 0: for item in self.user_backgrounds: self.collection_store.append(item) self.folder_tree.set_model(self.collection_store) self.folder_tree.set_row_separator_func(self.is_row_separator, None) self.get_initial_path() # Settings page = SettingsPage() settings = page.add_section(_("Background Settings")) size_group = Gtk.SizeGroup.new(Gtk.SizeGroupMode.HORIZONTAL) self.sidePage.stack.add_titled(page, "settings", _("Settings")) widget = GSettingsSwitch( _("Play backgrounds as a slideshow"), "org.cinnamon.desktop.background.slideshow", "slideshow-enabled") settings.add_row(widget) widget = GSettingsSpinButton( _("Delay"), "org.cinnamon.desktop.background.slideshow", "delay", _("minutes"), 1, 1440) settings.add_reveal_row( widget, "org.cinnamon.desktop.background.slideshow", "slideshow-enabled") widget = GSettingsSwitch( _("Play images in random order"), "org.cinnamon.desktop.background.slideshow", "random-order") settings.add_reveal_row( widget, "org.cinnamon.desktop.background.slideshow", "slideshow-enabled") widget = GSettingsComboBox(_("Picture aspect"), "org.cinnamon.desktop.background", "picture-options", BACKGROUND_PICTURE_OPTIONS, size_group=size_group) settings.add_row(widget) widget = ColorsWidget(size_group) settings.add_row(widget) def is_row_separator(self, model, iter, data): return model.get_value(iter, 0) def on_slideshow_enabled_changed(self, settings, key): if self._slideshow_schema.get_boolean("slideshow-enabled"): self.icon_view.set_sensitive(False) self.icon_view.set_selection_mode(Gtk.SelectionMode.NONE) else: self.icon_view.set_sensitive(True) self.icon_view.set_selection_mode(Gtk.SelectionMode.SINGLE) def get_system_backgrounds(self): picture_list = [] folder_list = [] properties_dir = "/usr/share/cinnamon-background-properties" backgrounds = [] if os.path.exists(properties_dir): for i in os.listdir(properties_dir): if i.endswith(".xml"): xml_path = os.path.join(properties_dir, i) display_name = i.replace(".xml", "").replace( "-", " ").replace("_", " ").split(" ")[-1].capitalize() icon = "preferences-desktop-wallpaper-symbolic" order = 10 # Special case for Linux Mint. We don't want to use 'start-here' here as it wouldn't work depending on the theme. # Also, other distros should get equal treatment. If they define cinnamon-backgrounds and use their own distro name, we should add support for it. if display_name == "Retro": icon = "document-open-recent-symbolic" order = 20 # place retro bgs at the end if display_name == "Linuxmint": display_name = "Linux Mint" icon = "linuxmint-logo-badge-symbolic" order = 0 backgrounds.append([[ False, icon, display_name, xml_path, BACKGROUND_COLLECTION_TYPE_XML ], display_name, order]) backgrounds.sort(key=lambda x: (x[2], x[1])) for background in backgrounds: self.collection_store.append(background[0]) def get_user_backgrounds(self): self.user_backgrounds = [] path = os.path.expanduser("~/.cinnamon/backgrounds/user-folders.lst") if os.path.exists(path): with open(path) as f: folders = f.readlines() for line in folders: folder_path = line.strip("\n") folder_name = folder_path.split("/")[-1] if folder_path == self.xdg_pictures_directory: icon = "folder-pictures-symbolic" else: icon = "folder-symbolic" self.user_backgrounds.append([ False, icon, folder_name, folder_path, BACKGROUND_COLLECTION_TYPE_DIRECTORY ]) else: # Add XDG PICTURE DIR self.user_backgrounds.append([ False, "folder-pictures-symbolic", self.xdg_pictures_directory.split("/")[-1], self.xdg_pictures_directory, BACKGROUND_COLLECTION_TYPE_DIRECTORY ]) self.update_folder_list() def format_source(self, type, path): # returns 'type://path' return ("%s://%s" % (type, path)) def get_initial_path(self): try: image_source = self._slideshow_schema.get_string("image-source") tree_iter = self.collection_store.get_iter_first() collection = self.collection_store[tree_iter] collection_type = collection[STORE_TYPE] collection_path = collection[STORE_PATH] collection_source = self.format_source(collection_type, collection_path) self.remove_folder_button.set_sensitive(True) if image_source != "" and "://" in image_source: while tree_iter != None: if collection_source == image_source: tree_path = self.collection_store.get_path(tree_iter) self.folder_tree.set_cursor(tree_path) if collection_type == BACKGROUND_COLLECTION_TYPE_XML: self.remove_folder_button.set_sensitive(False) self.update_icon_view(collection_path, collection_type) return tree_iter = self.collection_store.iter_next(tree_iter) collection = self.collection_store[tree_iter] collection_type = collection[STORE_TYPE] collection_path = collection[STORE_PATH] collection_source = self.format_source( collection_type, collection_path) else: self._slideshow_schema.set_string("image-source", collection_source) tree_path = self.collection_store.get_path(tree_iter) self.folder_tree.get_selection().select_path(tree_path) if collection_type == BACKGROUND_COLLECTION_TYPE_XML: self.remove_folder_button.set_sensitive(False) self.update_icon_view(collection_path, collection_type) except Exception as detail: print(detail) def on_row_activated(self, tree, path, column): self.folder_tree.set_selection(path) def on_folder_source_changed(self, tree): self.remove_folder_button.set_sensitive(True) if tree.get_selection() is not None: folder_paths, iter = tree.get_selection().get_selected() if iter: collection_path = folder_paths[iter][STORE_PATH] collection_type = folder_paths[iter][STORE_TYPE] collection_source = self.format_source(collection_type, collection_path) if os.path.exists(collection_path): if collection_source != self._slideshow_schema.get_string( "image-source"): self._slideshow_schema.set_string( "image-source", collection_source) if collection_type == BACKGROUND_COLLECTION_TYPE_XML: self.remove_folder_button.set_sensitive(False) self.update_icon_view(collection_path, collection_type) def get_selected_wallpaper(self): selected_items = self.icon_view.get_selected_items() if len(selected_items) == 1: path = selected_items[0] iter = self.icon_view.get_model().get_iter(path) return self.icon_view.get_model().get(iter, 0)[0] return None def on_wallpaper_selection_changed(self, iconview): wallpaper = self.get_selected_wallpaper() if wallpaper: for key in wallpaper: if key == "filename": self._background_schema.set_string( "picture-uri", "file://" + wallpaper[key]) elif key == "options": self._background_schema.set_string("picture-options", wallpaper[key]) def add_new_folder(self): res = self.add_folder_dialog.run() if res == Gtk.ResponseType.OK: folder_path = self.add_folder_dialog.get_filename() folder_name = folder_path.split("/")[-1] # Make sure it's not already added.. for background in self.user_backgrounds: if background[STORE_PATH] == folder_path: self.add_folder_dialog.hide() return if folder_path == self.xdg_pictures_directory: icon = "folder-pictures-symbolic" else: icon = "folder-symbolic" self.user_backgrounds.append([ False, icon, folder_name, folder_path, BACKGROUND_COLLECTION_TYPE_DIRECTORY ]) self.collection_store.append([ False, icon, folder_name, folder_path, BACKGROUND_COLLECTION_TYPE_DIRECTORY ]) self.update_folder_list() self.add_folder_dialog.hide() def remove_folder(self): if self.folder_tree.get_selection() is not None: self.icon_view.clear() folder_paths, iter = self.folder_tree.get_selection().get_selected( ) if iter: path = folder_paths[iter][STORE_PATH] self.collection_store.remove(iter) for item in self.user_backgrounds: if item[STORE_PATH] == path: self.user_backgrounds.remove(item) self.update_folder_list() break def update_folder_list(self): path = os.path.expanduser("~/.cinnamon/backgrounds") if not os.path.exists(path): os.makedirs(path, mode=0o755, exist_ok=True) path = os.path.expanduser("~/.cinnamon/backgrounds/user-folders.lst") if len(self.user_backgrounds) == 0: file_data = "" else: first_path = self.user_backgrounds[0][STORE_PATH] file_data = first_path + "\n" for folder in self.user_backgrounds: if folder[STORE_PATH] == first_path: continue else: file_data += "%s\n" % folder[STORE_PATH] with open(path, "w") as f: f.write(file_data) def update_icon_view(self, path=None, type=None): if path != self.shown_collection: self.shown_collection = path picture_list = [] if os.path.exists(path): if type == BACKGROUND_COLLECTION_TYPE_DIRECTORY: files = os.listdir(path) files.sort() for i in files: filename = os.path.join(path, i) picture_list.append({"filename": filename}) elif type == BACKGROUND_COLLECTION_TYPE_XML: picture_list += self.parse_xml_backgrounds_list(path) self.icon_view.set_pictures_list(picture_list, path) if self._slideshow_schema.get_boolean("slideshow-enabled"): self.icon_view.set_sensitive(False) else: self.icon_view.set_sensitive(True) def splitLocaleCode(self, localeCode): try: loc = localeCode.partition("_") loc = (loc[0], loc[2]) except: loc = ("en", "US") return loc def getLocalWallpaperName(self, names, loc): result = "" mainLocFound = False for wp in names: wpLoc = wp[0] wpName = wp[1] if wpLoc == ("", ""): if not mainLocFound: result = wpName elif wpLoc[0] == loc[0]: if wpLoc[1] == loc[1]: return wpName elif wpLoc[1] == "": result = wpName mainLocFound = True return result def parse_xml_backgrounds_list(self, filename): try: locAttrName = "{http://www.w3.org/XML/1998/namespace}lang" loc = self.splitLocaleCode(locale.getdefaultlocale()[0]) res = [] subLocaleFound = False f = open(filename) rootNode = ElementTree.fromstring(f.read()) f.close() if rootNode.tag == "wallpapers": for wallpaperNode in rootNode: if wallpaperNode.tag == "wallpaper" and wallpaperNode.get( "deleted") != "true": wallpaperData = {"metadataFile": filename} names = [] for prop in wallpaperNode: if type(prop.tag) == str: if prop.tag != "name": wallpaperData[prop.tag] = prop.text else: propAttr = prop.attrib wpName = prop.text locName = self.splitLocaleCode( propAttr.get(locAttrName) ) if locAttrName in propAttr else ("", "") names.append((locName, wpName)) wallpaperData["name"] = self.getLocalWallpaperName( names, loc) if "filename" in wallpaperData and wallpaperData[ "filename"] != "" and os.path.exists( wallpaperData["filename"]) and os.access( wallpaperData["filename"], os.R_OK): if wallpaperData["name"] == "": wallpaperData["name"] = os.path.basename( wallpaperData["filename"]) res.append(wallpaperData) return res except Exception as detail: print("Could not parse %s!" % filename) print(detail) return []
class Module: name = "sound" category = "hardware" comment = _("Manage sound settings") def __init__(self, content_box): keywords = _( "sound, media, music, speakers, audio, microphone, headphone") self.sidePage = SidePage(_("Sound"), "cs-sound", keywords, content_box, module=self) self.sound_settings = Gio.Settings(CINNAMON_DESKTOP_SOUNDS) def on_module_selected(self): if not self.loaded: print("Loading Sound module") self.outputDeviceList = Gtk.ListStore( str, # name str, # device bool, # active int, # id GdkPixbuf.Pixbuf) # icon self.inputDeviceList = Gtk.ListStore( str, # name str, # device bool, # active int, # id GdkPixbuf.Pixbuf) # icon self.appList = {} self.inializeController() self.buildLayout() self.checkAppState() self.checkInputState() def buildLayout(self): self.sidePage.stack = SettingsStack() self.sidePage.add_widget(self.sidePage.stack) ## Output page page = SettingsPage() self.sidePage.stack.add_titled(page, "output", _("Output")) self.outputSelector = self.buildDeviceSelect("output", self.outputDeviceList) outputSection = page.add_section(_("Device")) outputSection.add_row(self.outputSelector) devSettings = page.add_section(_("Device settings")) # output profiles self.profile = ProfileSelector(self.controller) devSettings.add_row(self.profile) sizeGroup = Gtk.SizeGroup.new(Gtk.SizeGroupMode.HORIZONTAL) # ouput volume max_volume = self.sound_settings.get_int(MAXIMUM_VOLUME_KEY) self.outVolume = VolumeBar(self.controller.get_vol_max_norm(), max_volume, sizeGroup=sizeGroup) devSettings.add_row(self.outVolume) # balance self.balance = BalanceBar("balance", sizeGroup=sizeGroup) devSettings.add_row(self.balance) self.fade = BalanceBar("fade", sizeGroup=sizeGroup) devSettings.add_row(self.fade) self.woofer = BalanceBar("lfe", 0, self.controller.get_vol_max_norm(), sizeGroup=sizeGroup) devSettings.add_row(self.woofer) ## Input page page = SettingsPage() self.sidePage.stack.add_titled(page, "input", _("Input")) self.inputStack = Gtk.Stack() page.pack_start(self.inputStack, True, True, 0) inputBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=15) self.inputSelector = self.buildDeviceSelect("input", self.inputDeviceList) deviceSection = SettingsSection("Device") inputBox.pack_start(deviceSection, False, False, 0) deviceSection.add_row(self.inputSelector) devSettings = SettingsSection(_("Device settings")) inputBox.pack_start(devSettings, False, False, 0) sizeGroup = Gtk.SizeGroup.new(Gtk.SizeGroupMode.HORIZONTAL) # input volume self.inVolume = VolumeBar(self.controller.get_vol_max_norm(), max_volume, sizeGroup=sizeGroup) devSettings.add_row(self.inVolume) # input level self.inLevel = VolumeLevelBar(sizeGroup) devSettings.add_row(self.inLevel) self.inputStack.add_named(inputBox, "inputBox") noInputsMessage = Gtk.Box() box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12) image = Gtk.Image.new_from_icon_name("action-unavailable-symbolic", Gtk.IconSize.DIALOG) image.set_pixel_size(96) box.pack_start(image, False, False, 0) box.set_valign(Gtk.Align.CENTER) label = Gtk.Label(_("No inputs sources are currently available.")) box.pack_start(label, False, False, 0) noInputsMessage.pack_start(box, True, True, 0) self.inputStack.add_named(noInputsMessage, "noInputsMessage") self.inputStack.show_all() ## Sounds page page = SettingsPage() self.sidePage.stack.add_titled(page, "sounds", _("Sounds")) soundsVolumeSection = page.add_section(_("Sounds Volume")) self.soundsVolume = VolumeBar(self.controller.get_vol_max_norm(), 100) soundsVolumeSection.add_row(self.soundsVolume) soundsSection = SoundBox(_("Sounds")) page.pack_start(soundsSection, True, True, 0) sizeGroup = Gtk.SizeGroup.new(Gtk.SizeGroupMode.HORIZONTAL) for effect in EFFECT_LIST: soundsSection.add_row(Effect(effect, sizeGroup)) ## Applications page page = SettingsPage() self.sidePage.stack.add_titled(page, "applications", _("Applications")) self.appStack = Gtk.Stack() page.pack_start(self.appStack, True, True, 0) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.appSettings = SoundBox(_("Applications")) box.pack_start(self.appSettings, True, True, 0) self.appStack.add_named(box, "appSettings") noAppsMessage = Gtk.Box() box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12) image = Gtk.Image.new_from_icon_name("action-unavailable-symbolic", Gtk.IconSize.DIALOG) image.set_pixel_size(96) box.pack_start(image, False, False, 0) box.set_valign(Gtk.Align.CENTER) label = Gtk.Label( _("No application is currently playing or recording audio.")) box.pack_start(label, False, False, 0) noAppsMessage.pack_start(box, True, True, 0) self.appStack.add_named(noAppsMessage, "noAppsMessage") ## Settings page page = SettingsPage() self.sidePage.stack.add_titled(page, "settings", _("Settings")) amplificationSection = page.add_section(_("Amplification")) self.maxVolume = Slider(_("Maximum volume: %d") % max_volume + "%", _("Reduced"), _("Amplified"), 1, 150, None, step=1, page=10, value=max_volume, gicon=None, iconName=None) self.maxVolume.adjustment.connect("value-changed", self.onMaxVolumeChanged) self.maxVolume.setMark(100) amplificationSection.add_row(self.maxVolume) def onMaxVolumeChanged(self, adjustment): newValue = int(round(adjustment.get_value())) self.sound_settings.set_int(MAXIMUM_VOLUME_KEY, newValue) self.maxVolume.label.set_label( _("Maximum volume: %d") % newValue + "%") self.outVolume.adjustment.set_upper(newValue) self.outVolume.slider.clear_marks() if (newValue > 100): self.outVolume.setMark(100) def inializeController(self): self.controller = Cvc.MixerControl(name="cinnamon") self.controller.connect("state-changed", self.setChannelMap) self.controller.connect("output-added", self.deviceAdded, "output") self.controller.connect("input-added", self.deviceAdded, "input") self.controller.connect("output-removed", self.deviceRemoved, "output") self.controller.connect("input-removed", self.deviceRemoved, "input") self.controller.connect("active-output-update", self.activeOutputUpdate) self.controller.connect("active-input-update", self.activeInputUpdate) self.controller.connect("default-sink-changed", self.defaultSinkChanged) self.controller.connect("default-source-changed", self.defaultSourceChanged) self.controller.connect("stream-added", self.streamAdded) self.controller.connect("stream-removed", self.streamRemoved) self.controller.open() def buildDeviceSelect(self, type, model): select = Gtk.IconView.new_with_model(model) select.set_margin(0) select.set_pixbuf_column(4) select.set_text_column(0) select.set_column_spacing(0) select.connect("selection-changed", self.setActiveDevice, type) return select def setActiveDevice(self, view, type): selected = view.get_selected_items() if len(selected) == 0: return model = view.get_model() newDeviceId = model.get_value(model.get_iter(selected[0]), 3) newDevice = getattr(self.controller, "lookup_" + type + "_id")(newDeviceId) if newDevice != None and newDeviceId != getattr(self, type + "Id"): getattr(self.controller, "change_" + type)(newDevice) self.profile.setDevice(newDevice) def deviceAdded(self, c, id, type): device = getattr(self.controller, "lookup_" + type + "_id")(id) iconTheme = Gtk.IconTheme.get_default() gicon = device.get_gicon() iconName = device.get_icon_name() icon = None if gicon is not None: lookup = iconTheme.lookup_by_gicon(gicon, 32, 0) if lookup is not None: icon = lookup.load_icon() if icon is None: if (iconName is not None and "bluetooth" in iconName): icon = iconTheme.load_icon("bluetooth", 32, 0) else: icon = iconTheme.load_icon("audio-card", 32, 0) getattr(self, type + "DeviceList").append([ device.get_description() + "\n" + device.get_origin(), "", False, id, icon ]) if type == "input": self.checkInputState() def deviceRemoved(self, c, id, type): store = getattr(self, type + "DeviceList") for row in store: if row[3] == id: store.remove(row.iter) if type == "input": self.checkInputState() return def checkInputState(self): if len(self.inputDeviceList) == 0: self.inputStack.set_visible_child_name("noInputsMessage") else: self.inputStack.set_visible_child_name("inputBox") def activeOutputUpdate(self, c, id): self.outputId = id device = self.controller.lookup_output_id(id) self.profile.setDevice(device) # select current device in device selector i = 0 for row in self.outputDeviceList: if row[3] == id: self.outputSelector.select_path( Gtk.TreePath.new_from_string(str(i))) i = i + 1 self.setChannelMap() def activeInputUpdate(self, c, id): self.inputId = id # select current device in device selector i = 0 for row in self.inputDeviceList: if row[3] == id: self.inputSelector.select_path( Gtk.TreePath.new_from_string(str(i))) i = i + 1 def defaultSinkChanged(self, c, id): defaultSink = self.controller.get_default_sink() if defaultSink == None: return self.outVolume.setStream(defaultSink) self.setChannelMap() def defaultSourceChanged(self, c, id): defaultSource = self.controller.get_default_source() if defaultSource == None: return self.inVolume.setStream(defaultSource) self.inLevel.setStream(defaultSource) def setChannelMap(self, a=None, b=None): if self.controller.get_state() == Cvc.MixerControlState.READY: channelMap = self.controller.get_default_sink().get_channel_map() self.balance.setChannelMap(channelMap) self.fade.setChannelMap(channelMap) self.woofer.setChannelMap(channelMap) def streamAdded(self, c, id): stream = self.controller.lookup_stream_id(id) if stream in self.controller.get_sink_inputs(): name = stream.props.name # FIXME: We use to filter out by PA_PROP_APPLICATION_ID. But # most streams report this as null now... why?? if name in ("speech-dispatcher", "libcanberra"): # speech-dispatcher: orca/speechd/spd-say # libcanberra: cinnamon effects, test sounds return if id in self.appList.keys(): # Don't add an input more than once return if name == None: name = _("Unknown") label = "%s: " % name self.appList[id] = VolumeBar(self.controller.get_vol_max_norm(), 100, label, stream.get_gicon()) self.appList[id].setStream(stream) self.appSettings.add_row(self.appList[id]) self.appSettings.list_box.invalidate_headers() self.appSettings.show_all() elif stream == self.controller.get_event_sink_input(): self.soundsVolume.setStream(stream) self.checkAppState() def streamRemoved(self, c, id): if id in self.appList: self.appList[id].get_parent().destroy() self.appSettings.list_box.invalidate_headers() del self.appList[id] self.checkAppState() def checkAppState(self): if len(self.appList) == 0: self.appStack.set_visible_child_name("noAppsMessage") else: self.appStack.set_visible_child_name("appSettings")
class Module: name = "panel" category = "prefs" comment = _("Manage Cinnamon panel settings") def __init__(self, content_box): keywords = _("panel, height, bottom, top, autohide, size, layout") self.sidePage = SidePage(_("Panel"), "cs-panel", keywords, content_box, module=self) def on_module_selected(self): if not self.loaded: print("Loading Panel module") self.settings = Gio.Settings.new("org.cinnamon") try: if len(sys.argv) > 2 and sys.argv[1] == "panel": self.panel_id = sys.argv[2] else: self.panel_id = self.settings.get_strv( "panels-enabled")[0].split(":")[0] except: self.panel_id = "" self.panels = [] self.previous_button = Gtk.Button(_("Previous panel")) self.next_button = Gtk.Button(_("Next panel")) controller = SettingsWidget() controller.fill_row() controller.pack_start(self.previous_button, False, False, 0) controller.pack_end(self.next_button, False, False, 0) self.previous_button.connect("clicked", self.on_previous_panel) self.next_button.connect("clicked", self.on_next_panel) self.revealer = SettingsRevealer() page = SettingsPage() page.add(controller) page.set_margin_bottom(0) self.revealer.add(page) self.sidePage.add_widget(self.revealer) self.config_stack = Gtk.Stack() self.config_stack.set_transition_duration(150) self.revealer.add(self.config_stack) page = SettingsPage() self.sidePage.add_widget(page) section = page.add_section(_("General Panel Options")) buttons = SettingsWidget() self.add_panel_button = Gtk.Button(label=_("Add new panel")) buttons.pack_start(self.add_panel_button, False, False, 2) toggle_button = Gtk.ToggleButton(label=_("Panel edit mode")) self.settings.bind("panel-edit-mode", toggle_button, "active", Gio.SettingsBindFlags.DEFAULT) buttons.pack_end(toggle_button, False, False, 2) section.add_row(buttons) section.add_row( GSettingsSwitch( _("Allow the pointer to pass through the edges of panels"), "org.cinnamon", "no-adjacent-panel-barriers")) self.add_panel_button.set_sensitive(False) self.settings.connect("changed::panels-enabled", self.on_panel_list_changed) self.proxy = None try: Gio.DBusProxy.new_for_bus(Gio.BusType.SESSION, Gio.DBusProxyFlags.NONE, None, "org.Cinnamon", "/org/Cinnamon", "org.Cinnamon", None, self._on_proxy_ready, None) except GLib.Error as e: print(e.message) self.proxy = None self.on_panel_list_changed() def _on_proxy_ready(self, object, result, data=None): self.proxy = Gio.DBusProxy.new_for_bus_finish(result) if not self.proxy.get_name_owner(): self.proxy = None if self.proxy: self.revealer.connect("unmap", self.restore_panels) self.revealer.connect("destroy", self.restore_panels) self.add_panel_button.connect("clicked", self.on_add_panel) if self.panel_id is not None: self.proxy.highlightPanel('(ib)', int(self.panel_id), True) def on_add_panel(self, widget): if self.proxy: self.proxy.addPanelQuery() def on_previous_panel(self, widget): if self.panel_id and self.proxy: self.proxy.highlightPanel('(ib)', int(self.panel_id), False) current = self.panels.index(self.current_panel) if current - 1 >= 0: self.current_panel = self.panels[current - 1] self.panel_id = self.current_panel.panel_id else: self.current_panel = self.panels[len(self.panels) - 1] self.panel_id = self.current_panel.panel_id self.config_stack.set_transition_type( Gtk.StackTransitionType.SLIDE_RIGHT) if self.proxy: self.proxy.highlightPanel('(ib)', int(self.panel_id), True) self.config_stack.set_visible_child(self.current_panel) def on_next_panel(self, widget): if self.panel_id and self.proxy: self.proxy.highlightPanel('(ib)', int(self.panel_id), False) current = self.panels.index(self.current_panel) if current + 1 < len(self.panels): self.current_panel = self.panels[current + 1] self.panel_id = self.current_panel.panel_id else: self.current_panel = self.panels[0] self.panel_id = self.current_panel.panel_id self.config_stack.set_transition_type( Gtk.StackTransitionType.SLIDE_LEFT) if self.proxy: self.proxy.highlightPanel('(ib)', int(self.panel_id), True) self.config_stack.set_visible_child(self.current_panel) def on_panel_list_changed(self, *args): if len(self.panels) > 0: for panel in self.panels: panel.destroy() self.panels = [] monitor_layout = [] panels = self.settings.get_strv("panels-enabled") n_mons = Gdk.Screen.get_default().get_n_monitors() for i in range(n_mons): monitor_layout.append(Monitor()) current_found = False for panel in panels: panel_id, monitor_id, position = panel.split(":") monitor_id = int(monitor_id) panel_page = PanelSettingsPage(panel_id, self.settings, position) self.config_stack.add_named(panel_page, panel_id) # we may already have a current panel id from the command line or if # if the panels-enabled key changed since everything was loaded if panel_id == self.panel_id: current_found = True self.current_panel = panel_page self.config_stack.set_visible_child(panel_page) # we don't currently show panels on monitors that aren't attached # if we decide to change this behavior, we should probably give some visual indication # that the panel is on a detached monitor if monitor_id < n_mons: if "top" in position: monitor_layout[monitor_id].top = panel_page elif "bottom" in position: monitor_layout[monitor_id].bottom = panel_page elif "left" in position: monitor_layout[monitor_id].left = panel_page else: monitor_layout[monitor_id].right = panel_page # Index the panels for the next/previous buttons for monitor in monitor_layout: for panel_page in (monitor.top, monitor.bottom, monitor.left, monitor.right): if panel_page != -1: self.panels.append(panel_page) # if there are no panels, there's no point in showing the stack if len(self.panels) == 0: self.next_button.hide() self.previous_button.hide() self.config_stack.hide() self.add_panel_button.set_sensitive(True) self.current_panel = None self.panel_id = None return self.config_stack.show() self.next_button.show() self.previous_button.show() # Disable the panel switch buttons if there's only one panel if len(self.panels) == 1: self.next_button.set_sensitive(False) self.previous_button.set_sensitive(False) else: self.next_button.set_sensitive(True) self.previous_button.set_sensitive(True) if not current_found: self.current_panel = self.panels[0] self.panel_id = self.current_panel.panel_id self.config_stack.set_visible_child(self.current_panel) self.revealer.set_reveal_child(len(self.panels) != 0) # If all panel positions are full, we want to disable the add button can_add = False for monitor in monitor_layout: if -1 in (monitor.top, monitor.bottom, monitor.left, monitor.right): can_add = True break self.add_panel_button.set_sensitive(can_add) try: current_idx = self.panels.index(self.panel_id) except: current_idx = 0 if self.proxy: self.proxy.highlightPanel('(ib)', int(self.panel_id), True) def restore_panels(self, widget): self.proxy.destroyDummyPanels() if self.panel_id: self.proxy.highlightPanel('(ib)', int(self.panel_id), False)
class Module: name = "calendar" comment = _("Manage date and time settings") category = "prefs" def __init__(self, content_box): keywords = _("time, date, calendar, format, network, sync") self.sidePage = SidePage(_("Date & Time"), "cs-date-time", keywords, content_box, 560, module=self) def on_module_selected(self): if not self.loaded: print("Loading Calendar module") page = SettingsPage() self.sidePage.add_widget(page) settings = page.add_section(_("Date and Time")) widget = SettingsWidget() self.tz_map = TimezoneMap.TimezoneMap.new() self.tz_map.set_size_request(-1, 205) widget.pack_start(self.tz_map, True, True, 0) settings.add_row(widget) self.tz_selector = TimeZoneSelector() settings.add_row(self.tz_selector) self.ntp_switch = Switch(_("Network time")) settings.add_row(self.ntp_switch) self.set_time_row = SettingsWidget() self.revealer = SettingsRevealer() settings.add_reveal_row(self.set_time_row, revealer=self.revealer) self.set_time_row.pack_start( Gtk.Label(_("Manually set date and time")), False, False, 0) self.date_chooser = DateChooserButton(True) self.time_chooser = TimeChooserButton(True) self.set_time_row.pack_end(self.time_chooser, False, False, 0) self.set_time_row.pack_end(self.date_chooser, False, False, 0) self.date_chooser.connect('date-changed', self.set_date_and_time) self.time_chooser.connect('time-changed', self.set_date_and_time) settings = page.add_section(_("Format")) settings.add_row( GSettingsSwitch(_("Use 24h clock"), "org.cinnamon.desktop.interface", "clock-use-24h")) settings.add_row( GSettingsSwitch(_("Display the date"), "org.cinnamon.desktop.interface", "clock-show-date")) settings.add_row( GSettingsSwitch(_("Display seconds"), "org.cinnamon.desktop.interface", "clock-show-seconds")) days = [[7, _("Use locale default")], [0, _("Sunday")], [1, _("Monday")]] settings.add_row( GSettingsComboBox(_("First day of week"), "org.cinnamon.desktop.interface", "first-day-of-week", days, valtype=int)) if os.path.exists('/usr/sbin/ntpd'): print('using csd backend') self.proxy_handler = CsdDBusProxyHandler(self._on_proxy_ready) else: print('using systemd backend') self.proxy_handler = SytemdDBusProxyHandler( self._on_proxy_ready) self.sync_24h_to_gnome() def _on_proxy_ready(self): self.zone = self.proxy_handler.get_timezone() if self.zone is None: self.tz_map.set_sensitive(False) self.tz_selector.set_sensitive(False) else: self.tz_map.set_timezone(self.zone) self.tz_map.connect('location-changed', self.on_map_location_changed) self.tz_selector.set_timezone(self.zone) self.tz_selector.connect('timezone-changed', self.on_selector_location_changed) can_use_ntp, is_using_ntp = self.proxy_handler.get_ntp() self.ntp_switch.set_sensitive(can_use_ntp) self.ntp_switch.content_widget.set_active(is_using_ntp) self.ntp_switch.content_widget.connect('notify::active', self.on_ntp_changed) self.revealer.set_reveal_child(not is_using_ntp) def sync_24h_to_gnome(self): # Firefox (and maybe other apps?) check gnome's 24h setting only. It'd be # messy to change it in firefox since our setting is a boolean and their's # is a string, so just update the gnome preference when the user changes ours. self.our_settings = Gio.Settings( schema_id="org.cinnamon.desktop.interface") self.gnome_settings = Gio.Settings( schema_id="org.gnome.desktop.interface") self.our_settings.connect("changed::clock-use-24h", self.update_gnome_24h) self.update_gnome_24h() def update_gnome_24h(self, settings=None, pspec=None): if self.our_settings.get_boolean("clock-use-24h"): self.gnome_settings.set_string("clock-format", "24h") else: self.gnome_settings.set_string("clock-format", "12h") def on_map_location_changed(self, *args): zone = self.tz_map.get_location().props.zone if zone == self.zone: return self.tz_selector.set_timezone(zone) self.set_timezone(zone) def on_selector_location_changed(self, *args): zone = self.tz_selector.get_timezone() if zone == self.zone: return self.set_timezone(zone) self.tz_map.set_timezone(zone) def set_timezone(self, zone): self.zone = zone self.proxy_handler.set_timezone(zone) def on_ntp_changed(self, *args): active = self.ntp_switch.content_widget.get_active() self.revealer.set_reveal_child(not active) self.proxy_handler.set_ntp(active) def set_date_and_time(self, *args): unaware = datetime.datetime.combine(self.date_chooser.get_date(), self.time_chooser.get_time()) tz = pytz.timezone(self.zone) self.datetime = tz.localize(unaware) seconds = int((self.datetime - datetime.datetime( 1970, 1, 1, tzinfo=datetime.timezone.utc)).total_seconds()) self.proxy_handler.set_time(seconds)
class Module: name = "power" category = "hardware" comment = _("Manage power settings") def __init__(self, content_box): keywords = _( "power, suspend, hibernate, laptop, desktop, brightness, screensaver" ) self.sidePage = SidePage(_("Power Management"), "cs-power", keywords, content_box, -1, module=self) def on_module_selected(self): if self.loaded: # self.loaded = False return print("Loading Power module") self.up_client = UPowerGlib.Client.new() self.csd_power_proxy = Gio.DBusProxy.new_sync( Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, "org.cinnamon.SettingsDaemon.Power", "/org/cinnamon/SettingsDaemon/Power", "org.cinnamon.SettingsDaemon.Power", None) self.settings = Gio.Settings.new("org.cinnamon") device_types = [x[UP_TYPE] for x in self.csd_power_proxy.GetDevices()] self.has_battery = UPowerGlib.DeviceKind.BATTERY in device_types or UPowerGlib.DeviceKind.UPS in device_types self.has_lid = self.up_client.get_lid_is_present() self.sidePage.stack = SettingsStack() # Power power_page = SettingsPage() section = power_page.add_section(_("Power Options")) lid_options, button_power_options, critical_options, can_suspend, can_hybrid_sleep = get_available_options( self.up_client) size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) if self.has_battery: header = SettingsWidget() label_ac = Gtk.Label() label_ac.set_markup("<b>%s</b>" % _("On A/C power")) size_group.add_widget(label_ac) label_battery = Gtk.Label() label_battery.set_markup("<b>%s</b>" % _("On battery power")) size_group.add_widget(label_battery) header.pack_end(label_battery, False, False, 0) header.pack_end(label_ac, False, False, 0) section.add_row(header) section.add_row( GSettings2ComboBox(_("Turn off the screen when inactive for"), CSD_SCHEMA, "sleep-display-ac", "sleep-display-battery", SLEEP_DELAY_OPTIONS, valtype="int", size_group=size_group)) section.add_row( GSettings2ComboBox(_("Suspend when inactive for"), CSD_SCHEMA, "sleep-inactive-ac-timeout", "sleep-inactive-battery-timeout", SLEEP_DELAY_OPTIONS, valtype="int", size_group=size_group)) if self.has_lid: section.add_row( GSettings2ComboBox(_("When the lid is closed"), CSD_SCHEMA, "lid-close-ac-action", "lid-close-battery-action", lid_options, size_group=size_group)) else: section.add_row( GSettingsComboBox(_("Turn off the screen when inactive for"), CSD_SCHEMA, "sleep-display-ac", SLEEP_DELAY_OPTIONS, valtype=int, size_group=size_group)) section.add_row( GSettingsComboBox(_("Suspend when inactive for"), CSD_SCHEMA, "sleep-inactive-ac-timeout", SLEEP_DELAY_OPTIONS, valtype=int, size_group=size_group)) if self.has_lid: section.add_row( GSettingsComboBox(_("When the lid is closed"), CSD_SCHEMA, "lid-close-ac-action", lid_options, size_group=size_group)) section = power_page.add_section(_("Extra options")) size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) section.add_row( GSettingsComboBox(_("When the power button is pressed"), CSD_SCHEMA, "button-power", button_power_options, size_group=size_group)) if self.has_lid: section.add_row( GSettingsSwitch( _("Perform lid-closed action even with external monitors attached" ), CSD_SCHEMA, "lid-close-suspend-with-external-monitor")) if self.has_battery and UPowerGlib.MAJOR_VERSION == 0 and UPowerGlib.MINOR_VERSION <= 99: section.add_row( GSettingsComboBox(_("When the battery is critically low"), CSD_SCHEMA, "critical-battery-action", critical_options, size_group=size_group)) if can_suspend and can_hybrid_sleep: switch = GSettingsSwitch(_("Enable Hybrid Sleep"), CSM_SCHEMA, "prefer-hybrid-sleep") switch.set_tooltip_text(_("Replaces Suspend with Hybrid Sleep")) section.add_row(switch) # Batteries self.battery_page = SettingsPage() self.show_battery_page = False self.battery_label_size_group = Gtk.SizeGroup( Gtk.SizeGroupMode.HORIZONTAL) self.build_battery_page() self.csd_power_proxy.connect("g-properties-changed", self.build_battery_page) proxy = Gio.DBusProxy.new_sync( Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, "org.cinnamon.SettingsDaemon.Power", "/org/cinnamon/SettingsDaemon/Power", "org.cinnamon.SettingsDaemon.Power.Screen", None) try: brightness = proxy.GetPercentage() except GLib.Error as e: print("Power module brightness page not available: %s" % e.message) if self.show_battery_page: self.sidePage.add_widget(self.sidePage.stack) self.sidePage.stack.add_titled(power_page, "power", _("Power")) self.sidePage.stack.add_titled(self.battery_page, "batteries", _("Batteries")) else: self.sidePage.add_widget(power_page) else: self.sidePage.add_widget(self.sidePage.stack) self.sidePage.stack.add_titled(power_page, "power", _("Power")) if self.show_battery_page: self.sidePage.stack.add_titled(self.battery_page, "batteries", _("Batteries")) page = SettingsPage() self.sidePage.stack.add_titled(page, "brightness", _("Brightness")) size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) section = page.add_section(_("Screen brightness")) section.add_row( BrightnessSlider(section, proxy, _("Screen brightness"))) section.add_row( GSettingsSwitch(_("On battery, dim screen when inactive"), CSD_SCHEMA, "idle-dim-battery")) section.add_reveal_row( GSettingsComboBox(_("Brightness level when inactive"), CSD_SCHEMA, "idle-brightness", IDLE_BRIGHTNESS_OPTIONS, valtype=int, size_group=size_group), CSD_SCHEMA, "idle-dim-battery") section.add_reveal_row( GSettingsComboBox(_("Dim screen after inactive for"), CSD_SCHEMA, "idle-dim-time", IDLE_DELAY_OPTIONS, valtype=int, size_group=size_group), CSD_SCHEMA, "idle-dim-battery") proxy = Gio.DBusProxy.new_sync( Gio.bus_get_sync(Gio.BusType.SESSION, None), Gio.DBusProxyFlags.NONE, None, "org.cinnamon.SettingsDaemon.Power", "/org/cinnamon/SettingsDaemon/Power", "org.cinnamon.SettingsDaemon.Power.Keyboard", None) try: brightness = proxy.GetPercentage() except GLib.Error as e: print("Power module no keyboard backlight: %s" % e.message) else: section = page.add_section(_("Keyboard backlight")) section.add_row( BrightnessSlider(section, proxy, _("Backlight brightness"))) def build_battery_page(self, *args): self.aliases = {} device_aliases = self.settings.get_strv("device-aliases") for alias in device_aliases: try: (device_id, device_nickname) = alias.split(":=") self.aliases[device_id] = device_nickname except: pass # ignore malformed aliases #destroy all widgets in this page for widget in self.battery_page.get_children(): widget.destroy() secondary_settings = None primary_settings = None # UPowerGlib segfaults when trying to get device. Use CSD instead devices = self.csd_power_proxy.GetDevices() have_primary = False ups_as_primary = False # first we look for a discharging UPS, which is promoted to the # primary device if it's discharging. Otherwise we use the first # listed laptop battery as the primary device for device in devices: if device[UP_TYPE] == UPowerGlib.DeviceKind.UPS and device[ UP_STATE] == UPowerGlib.DeviceState.DISCHARGING: ups_as_primary = True for device in devices: if device[UP_TYPE] == UPowerGlib.DeviceKind.LINE_POWER: pass # Do nothing elif device[ UP_TYPE] == UPowerGlib.DeviceKind.UPS and ups_as_primary: if not primary_settings: primary_settings = self.battery_page.add_section( _("Batteries")) primary_settings.add_row( self.set_device_ups_primary(device)) self.show_battery_page = True else: primary_settings.add_row( self.set_device_ups_primary(device)) elif device[ UP_TYPE] == UPowerGlib.DeviceKind.BATTERY and not ups_as_primary: if not have_primary: if not primary_settings: primary_settings = self.battery_page.add_section( _("Batteries")) primary_settings.add_row( self.set_device_battery_primary(device)) self.show_battery_page = True have_primary = True else: widget = self.set_device_battery_additional(device) if widget: primary_settings.add_row(widget) else: if not secondary_settings: secondary_settings = self.battery_page.add_section( _("Devices")) secondary_settings.add_row( self.add_battery_device_secondary(device)) self.show_battery_page = True else: secondary_settings.add_row( self.add_battery_device_secondary(device)) #show all the widgets in this page, but not the page itself visible = self.battery_page.get_visible() self.battery_page.show_all() self.battery_page.set_visible(visible) def set_device_ups_primary(self, device): device_id = device[UP_ID] percentage = device[UP_PERCENTAGE] battery_level = device[UP_BATTERY_LEVEL] state = device[UP_STATE] time = device[UP_SECONDS] vendor = device[UP_VENDOR] model = device[UP_MODEL] details = None if time > 0: time_string = get_timestring(time) if state == UPowerGlib.DeviceState.DISCHARGING: if percentage < 20: details = _("Caution low UPS, %s remaining") % time_string else: details = _("Using UPS power - %s remaining") % time_string else: details = UPowerGlib.Device.state_to_string(state) else: if state == UPowerGlib.DeviceState.DISCHARGING: if percentage < 20: details = _("Caution low UPS") else: details = _("Using UPS power") else: details = UPowerGlib.Device.state_to_string(state) desc = _("UPS") if (model != "" or vendor != ""): desc = "%s %s" % (vendor, model) widget = self.create_battery_row(device_id, "battery", desc, percentage, battery_level, details) return widget def set_device_battery_primary(self, device): device_id = device[UP_ID] percentage = device[UP_PERCENTAGE] battery_level = device[UP_BATTERY_LEVEL] state = device[UP_STATE] time = device[UP_SECONDS] vendor = device[UP_VENDOR] model = device[UP_MODEL] details = None if time > 0: time_string = get_timestring(time) if state == UPowerGlib.DeviceState.CHARGING or state == UPowerGlib.DeviceState.PENDING_CHARGE: details = _("Charging - %s until fully charged") % time_string elif state == UPowerGlib.DeviceState.DISCHARGING or state == UPowerGlib.DeviceState.PENDING_DISCHARGE: if percentage < 20: details = _( "Caution low battery, %s remaining") % time_string else: details = _( "Using battery power - %s remaining") % time_string else: details = UPowerGlib.Device.state_to_string(state) else: if state == UPowerGlib.DeviceState.CHARGING or state == UPowerGlib.DeviceState.PENDING_CHARGE: details = _("Charging") elif state == UPowerGlib.DeviceState.DISCHARGING or state == UPowerGlib.DeviceState.PENDING_DISCHARGE: details = _("Using battery power") elif state == UPowerGlib.DeviceState.FULLY_CHARGED: details = _("Charging - fully charged") elif state == UPowerGlib.DeviceState.EMPTY: details = _("Empty") else: details = UPowerGlib.Device.state_to_string(state) desc = _("Battery") if (model != "" or vendor != ""): desc = "%s %s" % (vendor, model) widget = self.create_battery_row(device_id, "battery", desc, percentage, battery_level, details) return widget def set_device_battery_additional(self, device): state = device[UP_STATE] details = None if state == UPowerGlib.DeviceState.FULLY_CHARGED: details = _("Fully charged") elif state == UPowerGlib.DeviceState.EMPTY: details = _("Empty") if details: widget = SettingsWidget() icon = Gtk.Image.new_from_icon_name("battery", Gtk.IconSize.DND) widget.pack_start(icon, False, False, 0) label = Gtk.Label(_("Secondary battery")) widget.pack_start(label, False, False, 0) label = Gtk.Label() label.set_markup(details) label.get_style_context().add_class("dim-label") widget.pack_end(label, False, False, 0) return widget else: return None def add_battery_device_secondary(self, device): device_id = device[UP_ID] kind = device[UP_TYPE] percentage = device[UP_PERCENTAGE] battery_level = device[UP_BATTERY_LEVEL] vendor = device[UP_VENDOR] model = device[UP_MODEL] if kind == UPowerGlib.DeviceKind.UPS: icon_name = "uninterruptible-power-supply" desc = _("Uninterruptible power supply") elif kind == UPowerGlib.DeviceKind.MOUSE: icon_name = "input-mouse" desc = _("Wireless mouse") elif kind == UPowerGlib.DeviceKind.KEYBOARD: icon_name = "input-keyboard" desc = _("Wireless Keyboard") elif kind == UPowerGlib.DeviceKind.TABLET: icon_name = "input-tablet" desc = _("Tablet") elif kind == UPowerGlib.DeviceKind.PDA: icon_name = "pda" desc = _("Personal digital assistant") elif kind == UPowerGlib.DeviceKind.PHONE: icon_name = "phone" desc = _("Cellphone") elif kind == UPowerGlib.DeviceKind.MEDIA_PLAYER: icon_name = "multimedia-player" desc = _("Media player") elif kind == UPowerGlib.DeviceKind.COMPUTER: icon_name = "computer" desc = _("Computer") else: icon_name = "battery" desc = (_("Battery")) if (model != "" or vendor != ""): desc = "%s %s" % (vendor, model) widget = self.create_battery_row(device_id, icon_name, desc, percentage, battery_level) return widget def create_battery_row(self, device_id, icon_name, desc, percentage, battery_level, details=None): if device_id in self.aliases: desc = self.aliases[device_id] widget = SettingsWidget() vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) label_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15) image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DND) entry = Gtk.Entry() entry.set_text(desc) entry.connect('focus-out-event', self.on_alias_changed, device_id) label_box.pack_start(image, False, False, 0) label_box.pack_start(entry, False, False, 0) self.battery_label_size_group.add_widget(label_box) hbox.pack_start(label_box, False, False, 0) if battery_level == UPowerGlib.DeviceLevel.NONE: label = Gtk.Label() label.set_markup("%d%%" % int(percentage)) label.set_size_request(30, -1) hbox.pack_start(label, False, False, 15) level_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) level_bar = Gtk.LevelBar() level_bar.set_mode(Gtk.LevelBarMode.DISCRETE) level_bar.set_min_value(0) level_bar.set_max_value(10) level_bar.add_offset_value("high", 5) level_bar.add_offset_value("low", 2) level_box.set_valign(Gtk.Align.CENTER) level_bar.set_value(round(percentage / 10)) level_box.pack_start(level_bar, True, True, 0) hbox.pack_start(level_box, True, True, 0) else: status_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) status_icon = Gtk.Image.new_from_icon_name( self.bat_level_to_icon(battery_level), Gtk.IconSize.BUTTON) status_icon.set_size_request(30, -1) status_box.pack_start(status_icon, False, False, 15) status_label = Gtk.Label(self.bat_level_to_label(battery_level)) status_box.pack_end(status_label, False, False, 0) hbox.pack_start(status_box, True, True, 0) vbox.pack_start(hbox, False, False, 0) if details: label = Gtk.Label() label.set_markup(details) label.get_style_context().add_class("dim-label") label.set_halign(Gtk.Align.END) vbox.pack_end(label, False, False, 0) widget.pack_start(vbox, True, True, 0) return widget def bat_level_to_icon(self, level): if level in (UPowerGlib.DeviceLevel.FULL, UPowerGlib.DeviceLevel.HIGH): return "battery-full-symbolic" elif level == UPowerGlib.DeviceLevel.NORMAL: return "battery-good-symbolic" elif level == UPowerGlib.DeviceLevel.LOW: return "battery-low-symbolic" elif level == UPowerGlib.DeviceLevel.CRITICAL: return "battery-caution-symbolic" def bat_level_to_label(self, level): if level == UPowerGlib.DeviceLevel.FULL: return _("Battery full") elif level == UPowerGlib.DeviceLevel.HIGH: return _("Battery almost full") elif level == UPowerGlib.DeviceLevel.NORMAL: return _("Battery good") elif level == UPowerGlib.DeviceLevel.LOW: return _("Low battery") elif level == UPowerGlib.DeviceLevel.CRITICAL: return _("Critically low battery") def on_alias_changed(self, entry, event, device_id): self.aliases[device_id] = entry.get_text() aliases = [] for alias in self.aliases: aliases.append("%s:=%s" % (alias, self.aliases[alias])) self.settings.set_strv("device-aliases", aliases)
class Module: name = "display" comment = _("Manage display settings") category = "hardware" def __init__(self, content_box): keywords = _("display, screen, monitor, layout, resolution, dual, lcd") self.sidePage = SidePage(_("Display"), "cs-display", keywords, content_box, 650, module=self) self.display_c_widget = None def on_module_selected(self): if not self.loaded: print("Loading Display module") self.sidePage.stack = SettingsStack() self.sidePage.add_widget(self.sidePage.stack) page = SettingsPage() self.sidePage.stack.add_titled(page, "layout", _("Layout")) try: settings = page.add_section(_("Layout")) widget = SettingsWidget() widget.set_border_width(0) widget.set_margin_start(0) widget.set_margin_end(0) content = self.sidePage.content_box.c_manager.get_c_widget( "display") widget.pack_start(content, True, True, 0) self.display_c_widget = content settings.add_row(widget) widget.get_parent().set_activatable(False) except Exception as detail: print(detail) page = SettingsPage() self.sidePage.stack.add_titled(page, "settings", _("Settings")) settings = page.add_section(_("Settings")) switch = GSettingsSwitch( _("Disable automatic screen rotation"), "org.cinnamon.settings-daemon.peripherals.touchscreen", "orientation-lock") switch.set_tooltip_text( _("Select this option to disable automatic screen rotation on hardware equipped with supported accelerometers." )) settings.add_row(switch) switch = Switch( _("Enable fractional scaling controls (experimental)")) switch.set_tooltip_text( _("Select this option to display additional layout controls for per-monitor scaling." )) settings.add_row(switch) self.fractional_switch = switch.content_widget self.muffin_settings = Gio.Settings( schema_id="org.cinnamon.muffin") self.experimental_features_changed(self.muffin_settings, "x11-randr-fractional-scaling") self.muffin_settings.connect("changed::experimental-features", self.experimental_features_changed) self.fractional_switch.connect("notify::active", self.fractional_switch_toggled) def experimental_features_changed(self, settings, key): self.fractional_switch.freeze_notify() features = self.muffin_settings.get_strv("experimental-features") self.fractional_switch.set_active( "x11-randr-fractional-scaling" in features) self.fractional_switch.thaw_notify() def fractional_switch_toggled(self, switch, pspec): active = switch.get_active() features = self.muffin_settings.get_strv("experimental-features") if active: if "x11-randr-fractional-scaling" in features: return else: features.append("x11-randr-fractional-scaling") else: try: features.remove("x11-randr-fractional-scaling") except ValueError: pass self.muffin_settings.set_strv("experimental-features", features) def on_navigate_out_of_module(self): if self.display_c_widget: self.display_c_widget.hide()