pika-gnome-layouts/data/scripts/papirus-folders

534 lines
11 KiB
Plaintext
Raw Permalink Normal View History

2024-08-28 22:45:39 +02:00
#!/usr/bin/env bash
# This script allows changing the color of folders in Papirus icon theme
#
# @author: Sergei Eremenko (https://github.com/SmartFinn)
# @license: MIT license (MIT)
# @link: https://github.com/PapirusDevelopmentTeam/papirus-folders
if test -z "$BASH_VERSION"; then
printf "Error: this script only works in bash.\n" >&2
exit 1
fi
if (( BASH_VERSINFO[0] * 10 + BASH_VERSINFO[1] < 40 )); then
printf "Error: this script requires bash version >= 4.0\n" >&2
exit 1
fi
# set -x # Uncomment to debug this shell script
set -o errexit \
-o noclobber \
-o pipefail
readonly THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
readonly PROGNAME="$(basename "${BASH_SOURCE[0]}")"
readonly VERSION="1.12.0"
readonly -a ARGS=("$@")
msg() {
printf "%s: %b\n" "$PROGNAME" "$*"
}
verbose() {
[ -t 4 ] || return 0
msg "$@" >&4
}
err() {
msg "Error:" "$*" >&2
}
_exit() {
msg "$*" "Exiting ..."
exit 0
}
fatal() {
err "$*"
exit 1
}
usage() {
cat <<- EOF
USAGE
$ $PROGNAME [options] -t <theme> {-C --color} <color>
$ $PROGNAME [options] -t <theme> {-D --default}
$ $PROGNAME [options] -t <theme> {-R --restore}
OPERATIONS
-C --color <color> change color of folders
-D --default back to the default color
-R --restore restore the last used color from the config file
OPTIONS
-l --list show available colors
-o --once do not save the changes to the config file
-t --theme <theme> make changes to the specified theme (Default: Papirus)
-u --update-caches update icon caches for Papirus and siblings
-V --version print $PROGNAME version and exit
-v --verbose be verbose
-h --help show this help
EOF
exit "${1:-0}"
}
_is_root_user() {
if [ "$(id -u)" -eq 0 ]; then
return 0
fi
return 1
}
_is_user_dir() {
[ -n "$USER_HOME" ] || return 1
# if $THEME_DIR is placed in home dir
if [ -z "${THEME_DIR##"$USER_HOME"/*}" ]; then
return 0
fi
return 1
}
_is_writable() {
if [ -w "$THEME_DIR/48x48/places/folder.svg" ]; then
return 0
fi
return 1
}
_is_valid_color() {
local color="$1"
eval "$(declare_colors)"
for i in "${colors[@]}"; do
[ "$i" == "$color" ] || continue
return 0
done
return 1
}
declare_colors() {
local color=''
local -a colors=()
local -a valid_colors=("adwaita" "black" "blue" "bluegrey" "breeze" "brown"
"carmine" "cyan" "darkcyan" "deeporange" "green" "grey"
"indigo" "magenta" "nordic" "orange" "palebrown" "paleorange"
"pink" "purple" "red" "teal" "violet" "white" "yaru" "yellow")
for color in "${valid_colors[@]}"; do
if [ -e "$THEME_DIR/48x48/places/folder-$color.svg" ]; then
colors=( "${colors[@]}" "$color" )
fi
done
# return array of colors
declare -p colors
}
declare_current_color() {
local icon_file icon_name current_color=''
icon_file=$(readlink -f "$THEME_DIR/48x48/places/folder.svg")
icon_name=$(basename "$icon_file" .svg)
current_color="${icon_name##*-}"
declare -p current_color
}
get_theme_dir() {
local data_dir icons_dir
local -a data_dirs=()
local -a icons_dirs=(
"$USER_HOME/.icons"
"${XDG_DATA_HOME:-$USER_HOME/.local/share}/icons"
)
# Get data directories from XDG_DATA_DIRS variable and
# convert colon-separated list into bash array
IFS=: read -ra data_dirs <<< "${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
for data_dir in "${data_dirs[@]}"; do
[ -d "$data_dir/icons" ] || continue
icons_dirs=( "${icons_dirs[@]}" "${data_dir%/}/icons" )
done
for icons_dir in "${icons_dirs[@]}"; do
[ -f "$icons_dir/$THEME_NAME/index.theme" ] || continue
printf '%s' "$icons_dir/$THEME_NAME"
verbose "'$THEME_NAME' is found in '$icons_dir'."
return 0
done
return 1
}
get_real_user() {
# return name of the user that runs the script
local user=''
if [ -n "$PKEXEC_UID" ]; then
user="$(id -nu "$PKEXEC_UID")"
elif [ -n "$SUDO_USER" ]; then
user="$SUDO_USER"
else
user="$(id -nu)"
fi
printf '%s' "$user"
}
get_user_home() {
local user="$1"
getent passwd "$user" | awk -F: '{print $(NF-1)}'
}
config() {
# usage: config [{-n --new}] {-s --set} key=value... | {-g --get} key...
local config_dir
local config_file
if _is_user_dir; then
config_dir="${XDG_CONFIG_HOME:-$USER_HOME/.config}/$PROGNAME"
else
config_dir="/var/lib/$PROGNAME"
fi
config_file="$config_dir/keep"
while (( "$#" )); do
case "$1" in
-g|--get) shift;
[ -f "$config_file" ] || return 1
for key; do
[ -n "$key" ] || continue
awk -F= -v key="$key" '
$1 == key {
print $2
exit
}
' "$config_file"
done
break
;;
-n|--new) shift;
rm -f "$config_file"
;;
-e|--exists) shift;
# return 1 if test config_file not exist or empty
if [ -f "$config_file" ] && [ -s "$config_file" ]; then
return 0
else
return 1
fi
;;
-s|--set) shift;
[ "$ONCE" -eq "1" ] && break
[ -d "$config_dir" ] || mkdir -p "$config_dir"
[ -f "$config_file" ] || touch "$config_file"
verbose "Saving params to '$config_file' ..."
cat >> "$config_file" <<- EOF
$(for key_value; do echo "$key_value"; done)
EOF
break
;;
*)
err "illegal option -- '$1'"
return 1
esac
done
return 0
}
change_color() {
local color="${1:?${FUNCNAME[-1]}: color is not set}"
local size prefix file_path file_name symlink_path
local -a sizes=(22x22 24x24 32x32 48x48 64x64)
local -a prefixes=("folder-$color" "user-$color")
for size in "${sizes[@]}"; do
for prefix in "${prefixes[@]}"; do
for file_path in "$THEME_DIR/$size/places/$prefix"{-*,}.svg; do
[ -f "$file_path" ] || continue # is a file
[ -L "$file_path" ] && continue # is not a symlink
file_name="${file_path##*/}"
symlink_path="${file_path/-$color/}" # remove color suffix
ln -sf "$file_name" "$symlink_path" || {
fatal "Fail to create '$symlink_path' symlink"
}
done
done
done
}
list_colors() {
local color='' prefix=''
eval "$(declare_colors)"
eval "$(declare_current_color)"
for color in "${colors[@]}"; do
if [ "$current_color" == "$color" ]; then
prefix='>'
else
prefix=''
fi
printf '%2s %s\n' "$prefix" "$color"
done
}
do_change_color() {
_is_valid_color "$SELECTED_COLOR" || {
fatal "Unable to find '$SELECTED_COLOR' color in '$THEME_NAME'"
}
verify_privileges
msg "Changing color of folders to '$SELECTED_COLOR' for '$THEME_NAME' ..."
change_color "$SELECTED_COLOR"
config --new --set "theme=$THEME_NAME" "color=$SELECTED_COLOR"
update_icon_cache
}
do_revert_default() {
verify_privileges
msg "Restoring default folder color for '$THEME_NAME' ..."
change_color "${DEFAULT_COLORS[$THEME_NAME]:-blue}"
config --new
update_icon_cache
}
do_restore_color() {
local saved_color=''
if config --exists; then
THEME_NAME="$(config --get theme)"
saved_color="$(config --get color)"
else
_exit "Unable to find config file."
fi
THEME_DIR="$(get_theme_dir)" || {
_exit "Unable to find '$THEME_NAME' icon theme."
}
_is_valid_color "$saved_color" || {
_exit "Unable to find '$saved_color' color in '$THEME_NAME'."
}
verify_privileges
change_color "$saved_color"
msg "'$saved_color' color of the folders has been restored."
}
delete_icon_caches() {
local icon_cache real_user='' real_home=''
real_user="$(get_real_user)"
real_home="$(get_user_home "$real_user")"
declare -a icon_caches=(
# KDE 5 icon caches
"$real_home/.cache/icon-cache.kcache"
"/var/tmp/kdecache-$real_user/icon-cache.kcache"
)
verbose "Deleting icon caches ..."
for icon_cache in "${icon_caches[@]}"; do
[ -e "$icon_cache" ] || continue
rm -f "$icon_cache"
done
}
update_icon_cache() {
[ -z "$DISABLE_UPDATE_ICON_CACHE" ] || return 0
delete_icon_caches
verbose "Rebuilding icon cache for '$THEME_NAME' ..."
gtk-update-icon-cache -qf "$THEME_DIR" || true
}
update_icon_caches() {
local theme=''
delete_icon_caches
for theme in "${!DEFAULT_COLORS[@]}"; do
[ -f "$THEME_DIR/../$theme/index.theme" ] || continue
verbose "Rebuilding icon cache for '$theme' ..."
gtk-update-icon-cache -qf "$THEME_DIR/../$theme" || true
done
}
verify_privileges() {
_is_root_user && return 0
_is_user_dir && return 0
_is_writable && return 0
verbose "This operation requires root privileges."
if command -v sudo > /dev/null; then
exec sudo USER_HOME="$USER_HOME" XDG_DATA_DIRS="$XDG_DATA_DIRS" \
"$THIS_SCRIPT" "${ARGS[@]}"
else
fatal "You need to be root to run this command."
fi
}
parse_args() {
local arg='' opt=''
local -a args=()
# Show help if no argument is passed
if [ -z "$1" ]; then
usage 2
fi
# Translate --gnu-long-options to -g (short options)
for arg; do
case "$arg" in
--help) args+=( -h ) ;;
--list) args+=( -l ) ;;
--once) args+=( -o ) ;;
--theme) args+=( -t ) ;;
--update-caches) args+=( -u ) ;;
--verbose) args+=( -v ) ;;
--color|--colour) args+=( -C ) ;;
--default) args+=( -D ) ;;
--restore) args+=( -R ) ;;
--version) args+=( -V ) ;;
--[0-9a-Z]*)
err "illegal option -- '$arg'"
usage 2
;;
*) args+=("$arg")
esac
done
# Reset the positional parameters to the short options
set -- "${args[@]}"
while getopts ":C:DRlot:uvVh" opt; do
case "$opt" in
C ) OPERATIONS+=("change-color")
SELECTED_COLOR="$OPTARG"
;;
D ) OPERATIONS+=("revert-default") ;;
R ) OPERATIONS+=("restore-color") ;;
l ) OPERATIONS+=("list-colors") ;;
o ) ONCE=1 ;;
t ) THEME_NAME="$OPTARG" ;;
u ) OPERATIONS+=("update-icon-caches") ;;
v ) VERBOSE=1 ;;
V ) printf "%s %s\n" "$PROGNAME" "$VERSION"
exit 0
;;
h ) usage 0 ;;
: ) err "option requires an argument -- '-$OPTARG'"
usage 2
;;
\?) err "illegal option -- '-$OPTARG'"
usage 2
;;
esac
done
shift $((OPTIND-1))
# Return an error if any positional parameters are found
if [ -n "$1" ]; then
err "illegal parameter -- '$1'"
usage 2
fi
}
main() {
# default values of options
declare THEME_NAME="${THEME_NAME:-Papirus}"
declare -i VERBOSE="${VERBOSE:-0}"
declare -i ONCE="${ONCE:-0}"
declare -A DEFAULT_COLORS=(
['ePapirus']='blue'
['Papirus']='blue'
['Papirus-Dark']='blue'
)
declare SELECTED_COLOR=''
declare -a OPERATIONS=()
parse_args "${ARGS[@]}"
if [ "$VERBOSE" -eq "1" ]; then
# open a file descriptor for verbose messages
exec 4>&1
# close the file descriptor before exiting
trap 'exec 4>&-' EXIT HUP INT TERM
fi
# set USER_HOME variable instead HOME to prevent changing user's icons
# when running with sudo
[ -n "$USER_HOME" ] || USER_HOME="$(get_user_home "$(id -nu)")"
if [ -f "$THEME_NAME/index.theme" ]; then
# THEME_NAME is a path to an icon theme
THEME_DIR="$(readlink -f "$THEME_NAME")"
THEME_NAME="$(basename "$THEME_DIR")"
verbose "The path to '$THEME_DIR' theme is specified."
else
THEME_DIR="$(get_theme_dir)" || {
fatal "Fail to find '$THEME_NAME' icon theme."
}
fi
for operation in "${OPERATIONS[@]}"; do
case "$operation" in
change-color)
do_change_color
;;
revert-default)
do_revert_default
;;
restore-color)
do_restore_color
;;
list-colors)
if [ -t 1 ]; then
cat <<- EOF
List of available colors:
$(list_colors)
EOF
else
list_colors
fi
;;
update-icon-caches)
verify_privileges
update_icon_caches
;;
esac
done
verbose "Done!"
exit 0
}
main
exit 1