#!/usr/bin/env python3 # cli.py # # Change the look of Adwaita, with ease # Copyright (C) 2022 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 . import os import sys import json import shutil import signal import argparse import warnings warnings.filterwarnings("ignore") # suppress GTK warnings from gi.repository import GLib, Gio from gradience.backend.utils.common import to_slug_case from gradience.backend.globals import preset_repos, presets_dir from gradience.backend.theming.monet import Monet from gradience.backend.models.preset import Preset from gradience.backend.theming.preset_utils import PresetUtils from gradience.backend.preset_downloader import PresetDownloader from gradience.backend.flatpak_overrides import create_gtk_user_override, remove_gtk_user_override version = "@VERSION@" signal.signal(signal.SIGINT, signal.SIG_DFL) class CLI: settings = Gio.Settings.new("@APP_ID@") def __init__(self): self.parser = argparse.ArgumentParser(description="Gradience - change the look of Adwaita, with ease") self.parser.add_argument("-V", "--version", action="version", version=f"Gradience, version {version}") #self.parser.add_argument('-J', '--pretty-json', dest='pretty_json', action='store_true', help='pretty-print JSON output') subparsers = self.parser.add_subparsers(dest="command") #info_parser = subparsers.add_parser("info", help="show information about Gradience") presets_parser = subparsers.add_parser("presets", help="list installed presets") presets_parser.add_argument("-r", "--remove-preset", metavar="PRESET_NAME", help="remove a preset from the list") favorites_parser = subparsers.add_parser("favorites", help="list favorite presets") favorites_parser.add_argument("-a", "--add-preset", metavar="PRESET_NAME", help="add a preset to favorites") favorites_parser.add_argument("-r", "--remove-preset", metavar="PRESET_NAME", help="remove a preset from favorites") import_parser = subparsers.add_parser("import", help="import a preset") import_parser.add_argument("-p", "--preset-path", help="absolute path to a preset file", required=True) apply_parser = subparsers.add_parser("apply", help="apply a preset") apply_group = apply_parser.add_mutually_exclusive_group(required=True) apply_group.add_argument("-n", "--preset-name", help="display name for a preset") apply_group.add_argument("-p", "--preset-path", help="absolute path to a preset file") apply_parser.add_argument("--gtk", choices=["gtk4", "gtk3", "both"], default="gtk4", help="types of applications you want to theme (default: gtk4)") #apply_parser.add_argument("--flatpak", choices=["gtk4", "gtk3", "both"], help="types of Flatpak applications you want to theme (for GTK3 option, make sure you have adw-gtk3 installed as Flatpak)") new_parser = subparsers.add_parser("new", help="create a new preset") #new_parser.add_argument("-i", "--interactive", action="store_true", help="") new_parser.add_argument("-n", "--name", help="display name for a preset", required=True) new_parser.add_argument("--colors", help="", required=True) new_parser.add_argument("--palette", help="") new_parser.add_argument("--custom-css", help="") new_parser.add_argument("--preset-stdout", action="store_true", help="print out preset in JSON format directly to stdout") download_parser = subparsers.add_parser("download", help="download preset from preset repository") #new_parser.add_argument("-i", "--interactive", action="store_true", help="") download_parser.add_argument("-n", "--preset-name", help="name of a preset you want to get", required=True) #download_parser.add_argument("--custom-url", help="use custom repository's presets.json to download other presets") monet_parser = subparsers.add_parser("monet", help="generate Material You preset from image") monet_parser.add_argument("-n", "--preset-name", help="name for a generated preset", required=True) monet_parser.add_argument("-p", "--image-path", help="abosulte path to image", required=True) monet_parser.add_argument("--tone", default=20, help="a tone for colors (default: 20)") monet_parser.add_argument("--theme", choices=["light", "dark"], default="light", help="choose whatever it should be a light or dark theme (default: light)") monet_parser.add_argument("--preset-stdout", action="store_true", help="print out preset in JSON format directly to stdout") overrides_parser = subparsers.add_parser("flatpak-overrides", help="enable or disable Flatpak theming") overrides_group = overrides_parser.add_mutually_exclusive_group(required=True) overrides_group.add_argument("-e", "--enable-theming", choices=["gtk4", "gtk3", "both"], help="enable overrides for Flatpak theming") overrides_group.add_argument("-d", "--disable-theming", choices=["gtk4", "gtk3", "both"], help="disable overrides for Flatpak theming") self.__parse_args() def __print_json(self, data, pretty=False): if pretty: print(json.dumps(data, indent=4)) else: print(json.dumps(data)) def __parse_args(self): args = self.parser.parse_args() if not args.command: print(self.parser.format_help()) if args.command == "presets": self.list_presets(args) elif args.command == "favorites": self.favorite_presets(args) elif args.command == "import": self.import_preset(args) elif args.command == "apply": self.apply_preset(args) elif args.command == "new": self.new_preset(args) elif args.command == "download": self.download_preset(args) elif args.command == "monet": self.generate_monet(args) elif args.command == "flatpak-overrides": self.flatpak_theming(args) def list_presets(self, args): pass def favorite_presets(self, args): pass def import_preset(self, args): _preset_path = args.preset_path preset_file = GLib.path_get_basename(_preset_path) sys.stdout.write(f"Importing preset: {preset_file.strip()}\n") # TODO: Check if preset is already imported if _preset_path.endswith(".json"): shutil.copy( _preset_path, os.path.join( presets_dir, "user", preset_file.strip() ) ) else: sys.stdout.write("Error: Unsupported file format, must be .json\n") exit(1) def apply_preset(self, args): _preset_name = args.preset_name _preset_path = args.preset_path _gtk = args.gtk #_flatpak = args.flatpak if _preset_name: sys.stdout.write("Error: Preset name option not implemented yet\n") exit(1) elif _preset_path: preset = Preset().new_from_path(_preset_path) if _gtk == "gtk4": PresetUtils().apply_preset("gtk4", preset) elif _gtk == "gtk3": PresetUtils().apply_preset("gtk3", preset) sys.stdout.write("Note: In order for changes to take full effect, you need to log out.\n") def new_preset(self, args): #_interactive = args.interactive _name = args.name _colors = args.colors _palette = args.palette _custom_css = args.custom_css _preset_stdout = args.preset_stdout def download_preset(self, args): #_interactive = args.interactive _preset_name = args.preset_name #_custom_url = args.custom_url for repo_name, repo in preset_repos.items(): explore_presets, urls = PresetDownloader().fetch_presets(repo) if explore_presets: for (preset, preset_name), preset_url in zip(explore_presets.items(), urls): if _preset_name.lower() in preset_name.lower(): sys.stdout.write(f"Downloading preset: {preset_name}\n") try: PresetDownloader().download_preset(preset_name, to_slug_case(repo_name), preset_url) except (GLib.GError, json.JSONDecodeError, OSError) as e: sys.stdout.write(f"Error: An error occurred while downloading a preset. Exc: {e}\n") exit(1) else: sys.stdout.write(f"Error: An error occurred while trying to fetch presets from repository.\n") exit(1) def generate_monet(self, args): _preset_name = args.preset_name _image_path = args.image_path _tone = args.tone _theme = args.theme _preset_stdout = args.preset_stdout palette = Monet().generate_from_image(_image_path) props = [_tone, _theme] if _preset_stdout: preset = PresetUtils().new_preset_from_monet(name=_preset_name, monet_palette=palette, props=props, obj_only=True) preset_json = preset.get_preset_json() sys.stdout.write(f"{preset_json}\n") exit(0) PresetUtils().new_preset_from_monet(_preset_name, palette, props) sys.stdout.write("Note: In order for changes to take full effect, you need to log out.\n") # FIXME: Doesn't work in local builds (settings variable doesn't have access to local settings schema) def flatpak_theming(self, args): _enable_theming = args.enable_theming _disable_theming = args.disable_theming if _enable_theming == "gtk4": create_gtk_user_override(self.settings, "gtk4") elif _enable_theming == "gtk3": create_gtk_user_override(self.settings, "gtk3") elif _enable_theming == "both": create_gtk_user_override(self.settings, "gtk4") create_gtk_user_override(self.settings, "gtk3") if _disable_theming == "gtk4": remove_gtk_user_override(self.settings, "gtk4") elif _disable_theming == "gtk3": remove_gtk_user_override(self.settings, "gtk3") elif _disable_theming == "both": remove_gtk_user_override(self.settings, "gtk4") remove_gtk_user_override(self.settings, "gtk3") if __name__ == "__main__": cli = CLI()