#!/bin/dash
# GTK+3 theme viewer and changer
# jamesbond3142 (License: GPL Version 3)

### configuration
APPTITLE="GTK+3 Change Theme"
USER_THEME_DIRS=${HOME}/.:${HOME}/local
BUILTIN_THEMES="Adwaita HighContrast win32" # this is from gtk source code
USER_GTK3_CONF=$HOME/.config/gtk-3.0/settings.ini
XSETTINGSD_CONF=$HOME/.xsettingsd
THEME_TESTER=${THEME_TESTER:-awf-gtk3} # alternative: gtk3-widget-factory
CURRENT_THEME=

### helpers

# $1-theme name, out: true/false
is_builtin() {
	local p
	for p in $BUILTIN_THEMES; do
		[ $p = "$1" ] && return 0
	done
	return 1
}

# $1-extra dirs, stdout: all gtk3 theme dirs
get_theme_dirs() {
	local ifs="$IFS" dirs="$XDG_DATA_DIRS:$1"
	IFS=:
	set -- $dirs
	while [ $1 ]; do
		case $1 in
			*.) echo ${1}themes ;;
			*) echo $1/themes ;;
		esac
		shift;
	done
	IFS="$ifs"
}

# stdout: all available gtk3 themes
get_all_gtk3_themes() {
	local p
	for p in $BUILTIN_THEMES; do echo \"$p\"; done
	while read -r p; do
		if [ -e "$p/gtk-3.0/gtk.css" ]; then
			! is_builtin "${p##*/}" && echo \""${p##*/}"\"
		fi
	done << EOF
$(find $(get_theme_dirs $USER_THEME_DIRS) -maxdepth 1 -type d 2> /dev/null)
EOF
}

# stdout: selected theme
select_theme() {
	local p choices= selected=
	while read -r p; do
		choices="$choices $p $p"
	done << EOF
$(get_all_gtk3_themes)
EOF

	selected=$(eval Xdialog --stdout --title \"$APPTITLE\" \
		--backtitle \"GTK+ 3 theme currently in-use: $CURRENT_THEME\" \
		--ok-label \"Preview theme\" --no-tags \
		--menubox \"Select theme to preview.\" 0 0 10 $choices)
	echo $selected
}

#$1-theme name
test_theme() {
	local selected="$1"
	if type $THEME_TESTER > /dev/null 2>&1; then
		Xdialog --title "$APPTITLE" --backtitle "Testing: $selected" \
			--infobox "A test app will be shown in the selected theme.
Close the test app to continue." 0 0 10000
		GSETTINGS_BACKEND=memory GTK_THEME="$selected" $THEME_TESTER
	fi
}

#$1-theme name
set_theme() {
	local selected="$1"

	# do nothing if the selected theme is the current theme
	[ "$selected" = "$CURRENT_THEME" ] && return
	
	if [ -e $USER_GTK3_CONF ]; then
		sed -i -e "s/^gtk-theme-name[ \t]*=.*/gtk-theme-name = $selected/" $USER_GTK3_CONF
	fi
	if type xsettingsd > /dev/null 2>&1; then
		if [ -e $XSETTINGSD_CONF ]; then
			sed -i -e 's|^Net/ThemeName.*|Net/ThemeName "'"$selected"'"|' $XSETTINGSD_CONF
		else
			echo 'Net/ThemeName "'"$selected"'"' > $XSETTINGSD_CONF
		fi
		apply_theme
	else
		Xdialog --title "$APPTITLE" --infobox "To change GTK+ 3 theme of running applications, you need to have 'xsettingsd' installed.
Otherwise, the new theme will only be visible after you restart the applications." 0 0 10000
	fi
}

apply_theme() {
	local pid
	if type xsettingsd > /dev/null 2>&1; then
		if pidof xsettingsd > /dev/null; then
			# tell xsettingsd to reload its configuration
			killall -HUP xsettingsd
		else
			# run shortly only to trigger changes, then force it to exit
			xsettingsd &
			pid=$!
			sleep 2
			kill $pid
		fi	
	fi
}

# stdout: current theme
get_current_theme() {
	CURRENT_THEME="$(sed -n '/^gtk-theme-name[ \t]*=/ {s/.*=//; s/^[ \t]*//; p}' $USER_GTK3_CONF)"
}

#
interactive() {
	local selected
	while true; do
		selected="$(select_theme)"
		
		if [ "$selected" ]; then
			test_theme "$selected"
			Xdialog --title "$APPTITLE" --ok-label "Apply and exit" --cancel-label "Cancel and exit" \
				--extra-button --extra-label "Select and test another" --yesno "Do you like this theme ($selected)?" 0 0
			case $? in
				0) break ;;
				3) continue ;;
				*) exit
			esac
		else
			exit
		fi
	done
	set_theme "$selected"
}

### main
get_current_theme
case "$1" in
	"") interactive ;; # no parameters
	--help|-h) cat << EOF
${0##*/} [themename]
If a theme is specified on the command line, that theme is set and applied.
No selected is performed.
EOF
		exit ;;
	*) set_theme "$1"; exit ;; # everything else: assume set theme
esac
