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 __init__(self, content_box): keywords = _("panel, height, bottom, top, autohide, size, layout") self.sidePage = SidePage(_("Panel"), "cs-panel", keywords, content_box, module=self)
def __init__(self, content_box): keywords = _("background, picture, slideshow") self.sidePage = SidePage(_("Backgrounds"), "cs-backgrounds", keywords, content_box, module=self)
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 __init__(self, content_box): keywords = _("sound, media, music, speakers, audio") self.sidePage = SidePage(_("Sound"), "cs-sound", keywords, content_box, module=self) self.sound_settings = Gio.Settings(CINNAMON_DESKTOP_SOUNDS)
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()
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)
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)
def __init__(self, content_box): keywords = _("notifications") sidePage = SidePage(_("Notifications"), "cs-notifications", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("screensaver, lock, away, message") sidePage = SidePage(_("Screensaver"), "cs-screensaver", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("window, tile, flip, tiling, snap, snapping") sidePage = SidePage(_("Window Tiling"), "cs-tiling", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("effects, window") sidePage = SidePage(_("Effects"), "cs-desktop-effects", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("startup, programs, boot, init, session, autostart, apps") sidePage = SidePage(_("Startup Applications"), "cs-startup-programs", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("mouse, touchpad, synaptic, double-click") sidePage = SidePage(_("Mouse and Touchpad"), "cs-mouse", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("desktop, home, button, trash") sidePage = SidePage(_("Desktop"), "cs-desktop", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("logging, click") sidePage = SidePage(_("General"), "cs-general", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("keyboard, shortcut, hotkey") sidePage = SidePage(_("Keyboard"), "cs-keyboard", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("workspace, osd, expo, monitor") sidePage = SidePage(_("Workspaces"), "cs-workspaces", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("magnifier, talk, access, zoom, keys, contrast") sidePage = SidePage(_("Accessibility"), "cs-universal-access", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _( "system, information, details, graphic, sound, kernel, version") sidePage = SidePage(_("System Info"), "cs-details", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("user, account, information, details, password") sidePage = SidePage(_("Account details"), "cs-user", keywords, content_box, module=self) self.sidePage = sidePage self.window = None
def __init__(self, content_box): keywords = _("privacy, recent, gtk, private") sidePage = SidePage(_("Privacy"), "cs-privacy", keywords, content_box, module=self) self.sidePage = sidePage self.settings = Gio.Settings(schema=PRIVACY_SCHEMA)
def __init__(self, content_box): keywords = _( "windows, titlebar, edge, switcher, window list, attention, focus") sidePage = SidePage(_("Windows"), "cs-windows", keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): keywords = _("hotcorner, overview, scale, expo") sidePage = SidePage(_("Hot Corners"), "cs-overview", keywords, content_box, -1, module=self) self.sidePage = sidePage
def __init__(self, content_box): self.keywords = _("themes, style") self.icon = "cs-themes" self.window = None sidePage = SidePage(_("Themes"), self.icon, self.keywords, content_box, module=self) self.sidePage = sidePage
def __init__(self, content_box): self.keywords = _("layout, cinnamon") self.icon = "cinnamon-layout" self.window = None sidePage = SidePage(_("Desktop Layout"), self.icon, self.keywords, content_box, module=self) self.sidePage = sidePage
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()
def __init__(self, content_box): self.keywords = _("themes, style") self.icon = "cs-themes" self.window = None sidePage = SidePage(_("Themes"), self.icon, self.keywords, content_box, module=self) self.sidePage = sidePage self.refreshing = False # flag to ensure we only refresh once at any given moment
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")