mirror of
https://github.com/GradienceTeam/Gradience.git
synced 2024-07-05 18:47:53 +00:00
e412a2c537
Signed-off-by: tfuxu <73042332+tfuxu@users.noreply.github.com> Co-authored-by: 0xMRTT <0xMRTT@evta.fr> Co-authored-by: 0xMRTT <0xMRTT@proton.me> Co-authored-by: daudix-UFO <ddaudix@gmail.com>
274 lines
9.9 KiB
Python
274 lines
9.9 KiB
Python
# gsettings.py
|
|
#
|
|
# Change the look of Adwaita, with ease
|
|
# Copyright (c) 2011, John Stowers
|
|
# Copyright (C) 2023, Gradience Team
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
# NOTICE:
|
|
# This code is from the GNOME Tweaks application, which is licensed under the GPL-3.0 license.
|
|
# https://gitlab.gnome.org/GNOME/gnome-tweaks/-/blob/master/gtweak/gsettings.py
|
|
|
|
import os
|
|
import re
|
|
import shutil
|
|
import os.path
|
|
import xml.dom.minidom
|
|
import gettext
|
|
|
|
from subprocess import SubprocessError, CompletedProcess
|
|
|
|
from gi.repository import Gio, GLib
|
|
|
|
from gradience.backend.utils.subprocess import GradienceSubprocess
|
|
from gradience.backend.constants import localedir, app_id
|
|
|
|
from gradience.backend.logger import Logger
|
|
|
|
logging = Logger(logger_name="GSettings")
|
|
|
|
|
|
_SCHEMA_CACHE = {}
|
|
_GSETTINGS_SCHEMAS = set(Gio.Settings.list_schemas())
|
|
_GSETTINGS_RELOCATABLE_SCHEMAS = set(Gio.Settings.list_relocatable_schemas())
|
|
|
|
|
|
class GSettingsMissingError(Exception):
|
|
pass
|
|
|
|
|
|
class _GSettingsSchema:
|
|
def __init__(self, schema_name, schema_dir=None, schema_filename=None, **options):
|
|
if not schema_filename:
|
|
schema_filename = schema_name + ".gschema.xml"
|
|
if not schema_dir:
|
|
schema_dir = app_id
|
|
for xdg_dir in GLib.get_system_data_dirs():
|
|
dir = os.path.join(xdg_dir, "glib-2.0", "schemas")
|
|
if os.path.exists(os.path.join(dir, schema_filename)):
|
|
schema_dir = dir
|
|
break
|
|
|
|
schema_path = os.path.join(schema_dir, schema_filename)
|
|
if not os.path.exists(schema_path):
|
|
logging.critical("Could not find schema %s" % schema_path)
|
|
assert (False)
|
|
|
|
self._schema_name = schema_name
|
|
self._schema = {}
|
|
|
|
try:
|
|
dom = xml.dom.minidom.parse(schema_path)
|
|
global_gettext_domain = dom.documentElement.getAttribute(
|
|
'gettext-domain')
|
|
try:
|
|
if global_gettext_domain:
|
|
# We can't know where the schema owner was installed, let's assume it's
|
|
# the same prefix as ours
|
|
global_translation = gettext.translation(
|
|
global_gettext_domain, localedir)
|
|
else:
|
|
global_translation = gettext.NullTranslations()
|
|
except IOError:
|
|
global_translation = None
|
|
logging.debug("No translated schema for %s (domain: %s)" % (
|
|
schema_name, global_gettext_domain))
|
|
for schema in dom.getElementsByTagName("schema"):
|
|
gettext_domain = schema.getAttribute('gettext-domain')
|
|
try:
|
|
if gettext_domain:
|
|
translation = gettext.translation(
|
|
gettext_domain, localedir)
|
|
else:
|
|
translation = global_translation
|
|
except IOError:
|
|
translation = None
|
|
logging.debug("Schema not translated %s (domain: %s)" % (
|
|
schema_name, gettext_domain))
|
|
if schema_name == schema.getAttribute("id"):
|
|
for key in schema.getElementsByTagName("key"):
|
|
name = key.getAttribute("name")
|
|
# summary is 'compulsory', description is optional
|
|
# …in theory, but we should not barf on bad schemas ever
|
|
try:
|
|
summary = key.getElementsByTagName(
|
|
"summary")[0].childNodes[0].data
|
|
except:
|
|
summary = ""
|
|
logging.info("Schema missing summary %s (key %s)" %
|
|
(os.path.basename(schema_path), name))
|
|
try:
|
|
description = key.getElementsByTagName(
|
|
"description")[0].childNodes[0].data
|
|
except:
|
|
description = ""
|
|
|
|
# if missing translations, use the untranslated values
|
|
self._schema[name] = dict(
|
|
summary=translation.gettext(
|
|
summary) if translation else summary,
|
|
description=translation.gettext(
|
|
description) if translation else description
|
|
)
|
|
|
|
except:
|
|
logging.critical("Error parsing schema %s (%s)" %
|
|
(schema_name, schema_path), exc_info=True)
|
|
|
|
def __repr__(self):
|
|
return "<gradience._GSettingsSchema: %s>" % self._schema_name
|
|
|
|
|
|
class GSettingsSetting(Gio.Settings):
|
|
def __init__(self, schema_name, schema_dir=None, schema_path=None, **options):
|
|
|
|
if schema_dir is None:
|
|
if schema_path is None and schema_name not in _GSETTINGS_SCHEMAS:
|
|
raise GSettingsMissingError(schema_name)
|
|
|
|
if schema_path is not None and schema_name not in _GSETTINGS_RELOCATABLE_SCHEMAS:
|
|
raise GSettingsMissingError(schema_name)
|
|
|
|
if schema_path is None:
|
|
Gio.Settings.__init__(self, schema=schema_name)
|
|
else:
|
|
Gio.Settings.__init__(
|
|
self, schema=schema_name, path=schema_path)
|
|
else:
|
|
GioSSS = Gio.SettingsSchemaSource
|
|
schema_source = GioSSS.new_from_directory(schema_dir,
|
|
GioSSS.get_default(),
|
|
False)
|
|
schema_obj = schema_source.lookup(schema_name, True)
|
|
if not schema_obj:
|
|
raise GSettingsMissingError(schema_name)
|
|
|
|
Gio.Settings.__init__(self, None, settings_schema=schema_obj)
|
|
|
|
if schema_name not in _SCHEMA_CACHE:
|
|
_SCHEMA_CACHE[schema_name] = _GSettingsSchema(
|
|
schema_name, schema_dir=schema_dir, **options)
|
|
logging.debug("Caching gsettings: %s" % _SCHEMA_CACHE[schema_name])
|
|
|
|
self._schema = _SCHEMA_CACHE[schema_name]
|
|
|
|
def _on_changed(self, settings, key_name):
|
|
logging.debug("Change: %s %s -> %s" %
|
|
(self.props.schema, key_name, self[key_name]))
|
|
|
|
def _setting_check_is_list(self, key):
|
|
variant = Gio.Settings.get_value(self, key)
|
|
return variant.get_type_string() == "as"
|
|
|
|
def schema_get_summary(self, key):
|
|
return self._schema._schema[key]["summary"]
|
|
|
|
def schema_get_description(self, key):
|
|
return self._schema._schema[key]["description"]
|
|
|
|
def schema_get_all(self, key):
|
|
return self._schema._schema[key]
|
|
|
|
def setting_add_to_list(self, key, value):
|
|
""" helper function, ensures value is present in the GSettingsList at key """
|
|
assert self._setting_check_is_list(key)
|
|
|
|
vals = self[key]
|
|
if value not in vals:
|
|
vals.append(value)
|
|
self[key] = vals
|
|
return True
|
|
|
|
def setting_remove_from_list(self, key, value):
|
|
""" helper function, removes value in the GSettingsList at key (if present)"""
|
|
assert self._setting_check_is_list(key)
|
|
|
|
vals = self[key]
|
|
try:
|
|
vals.remove(value)
|
|
self[key] = vals
|
|
return True
|
|
except ValueError:
|
|
# not present
|
|
pass
|
|
|
|
def setting_is_in_list(self, key, value):
|
|
assert self._setting_check_is_list(key)
|
|
return value in self[key]
|
|
|
|
|
|
class FlatpakGSettings:
|
|
def __init__(self, schema_name, schema_dir=None, **options):
|
|
self.schema_name = schema_name
|
|
self.schema_dir = schema_dir
|
|
|
|
def list_keys(self) -> str:
|
|
dconf_cmd = ["gsettings", "list-keys", self.schema_name]
|
|
process = GradienceSubprocess()
|
|
|
|
if self.schema_dir:
|
|
self._insert_schemadir(dconf_cmd)
|
|
|
|
try:
|
|
completed = process.run(dconf_cmd, allow_escaping=True)
|
|
stdout = process.get_stdout_data(completed, decode=True)
|
|
except SubprocessError:
|
|
raise
|
|
else:
|
|
return stdout
|
|
|
|
def get(self, key:str) -> CompletedProcess:
|
|
dconf_cmd = ["gsettings", "get", self.schema_name, key]
|
|
process = GradienceSubprocess()
|
|
|
|
if self.schema_dir:
|
|
self._insert_schemadir(dconf_cmd)
|
|
|
|
try:
|
|
completed = process.run(dconf_cmd, allow_escaping=True)
|
|
stdout = process.get_stdout_data(completed, decode=True)
|
|
except SubprocessError:
|
|
raise
|
|
else:
|
|
return stdout
|
|
|
|
def set(self, key:str, value:str) -> None:
|
|
dconf_cmd = ["gsettings", "set", self.schema_name, key, value]
|
|
process = GradienceSubprocess()
|
|
|
|
if self.schema_dir:
|
|
self._insert_schemadir(dconf_cmd)
|
|
|
|
try:
|
|
process.run(dconf_cmd, allow_escaping=True)
|
|
except SubprocessError:
|
|
raise
|
|
|
|
def reset(self, key:str = None) -> None:
|
|
dconf_cmd = ["gsettings", "reset", self.schema_name, key]
|
|
process = GradienceSubprocess()
|
|
|
|
if self.schema_dir:
|
|
self._insert_schemadir(dconf_cmd)
|
|
|
|
try:
|
|
process.run(dconf_cmd, allow_escaping=True)
|
|
except SubprocessError:
|
|
raise
|
|
|
|
def _insert_schemadir(self, dconf_cmd):
|
|
dconf_cmd.insert(1, "--schemadir")
|
|
dconf_cmd.insert(2, self.schema_dir)
|