#!/bin/ash
# GUI for Xorg xkb map selector
# Copyright (C) James Budiono 2012, 2015
# License: GNU GPL Version 3 or later
# note: requires xml2 package
#131026 internationalisation by L18L
#140106 keyboard test added

# std localisation stanza
export TEXTDOMAIN=fatdog
. gettext.sh
# performance tweak - use "C" if there is no localisation
! [ -e $TEXTDOMAINDIR/${LANG%.*}/LC_MESSAGES/$TEXTDOMAIN.mo ] &&
! [ -e $TEXTDOMAINDIR/${LANG%_*}/LC_MESSAGES/$TEXTDOMAIN.mo ] && LANG=C

### configuration
APPTITLE="$(gettext 'Keyboard Wizard')"
XKB_RULES_PATH=/usr/share/X11/xkb/rules
XORG_CONF_DIR=/etc/X11/xorg.conf.d
XKBLANG_CONF=xkb-lang.conf
KEYMAP_CONFIG=$FATDOG_STATE_DIR/xkeymap	# used xinitrc

##### helpers #####

dlg() {
	Xdialog --stdout --title "$APPTITLE" "$@"
}

############ main ##########
# get the name of rule currently in-use
active_rule=$(setxkbmap -query | awk '/rules:/ { print $2} ')
[ $active_rule ] || active_rule=evdev

# get the corresponding xkb rule file
rule_file=$XKB_RULES_PATH/$active_rule.xml

# xml2 converts xml to flat file, awk script converts into Xdialog treeview selection
items=$(xml2 < $rule_file | awk '
	function qsort(A, left, right,   i, last) {
		if (left >= right)
			return
		swap(A, left, left+int((right-left+1)*rand()))
		last = left
		for (i = left+1; i <= right; i++)
			if (A[i] < A[left])
				swap(A, ++last, i)
		swap(A, left, last)
		qsort(A, left, last-1)
		qsort(A, last+1, right)
	}
	function swap(A, i, j,   t) {
		t = A[i]; A[i] = A[j]; A[j] = t
	}
	/layout/ {
		# start new layout
		if (match($0,/layout\/configItem\/name=/)) {
			sub(/.*=/,"")
			current_layout = $0
			layouts = layouts " " $0
		}
		
		# get layout description
		if (match($0,/layout\/configItem\/description=/)) {
			sub(/.*=/,"")
			desc[current_layout] = $0
		}
		
		# start new variant (for a layout)
		if (match($0,/variant\/configItem\/name=/)) {
			sub(/.*=/,"")
			current_variant=$0
			variants[current_layout] = variants[current_layout] " " $0
		}
		
		# get description for the variant
		if (match($0,/variant\/configItem\/description=/)) {
			sub(/.*=/,"")
			variant_desc[current_layout ":" current_variant] = $0
		}
		
	}
	END {
		# print the layout & variants in Xdialog treeview format
		n = split(layouts, zz, " ")
		qsort(zz,1,n)
		for (i=1; i<n; i++) {
			printf zz[i] " \"" zz[i] " - " desc[zz[i]] "\" off 0 "
			printf zz[i] " \"" "" zz[i] "  -  Standard " desc[zz[i]] "\" off 1 "
			
			nn = split (variants[zz[i]], yy, " ")
			for (k=1; k<nn; k++) {
				printf zz[i] ":" yy[k] " \"" "" zz[i] ":" yy[k] "  -  " variant_desc[zz[i] ":" yy[k]] "\" off 1 "
			}
		}
	}
')
#echo "$items"

### load currently active xkeymap (=-in-use), and current xkeymap (=in settings file)
active_xkeymap=$(setxkbmap -query | awk '
/layout:/ { layout=$2 }
/variant:/ { variant=$2 }
END {
	if (variant) print layout " " variant
	else print layout
}')
cur_xkeymap="$active_xkeymap"
[ -e $KEYMAP_CONFIG ] && read cur_xkeymap < $KEYMAP_CONFIG
[ -z "$cur_xkeymap" ] && cur_xkeymap="us" # should never happen, but just in case

### choose & test loop
while : ; do
    selection=$(eval "dlg --backtitle '$(eval_gettext 'Current keyboard mapping: [$cur_xkeymap]')' \
                          --treeview '$(gettext "Select your keyboard layout")' 30 70 0 $items")
    if [ $selection ]; then
		selection="$(echo $selection | tr ':' ' ')"
        setxkbmap $selection
        
        # let user test the selected keymap
        if dlg --inputbox "$(eval_gettext 'Test [$selection] keyboard now')" 10 50 \
           "$(gettext 'qwertz/&> @ | %;:_?<')" > /dev/null; then
           
	        # if root, do additional setup - create persistent xkb-lang.conf
	        if [ $(id -u) -eq 0 ]; then
	        layout=${selection% *} variant=${selection#* }
	        [ $layout = $variant ] && variant=""	        
				cat << EOF > $XORG_CONF_DIR/$XKBLANG_CONF
Section "InputClass"
    Identifier "XKB Lang"
    MatchIsKeyboard "yes"
    Option "XkbLayout" "$layout"
    $([ "$variant" ] && echo Option \"XkbVariant\" \"$variant\")
EndSection    
EOF
				EXTRACONF=" and $XORG_CONF_DIR/$XKBLANG_CONF"
	        fi
            dlg --infobox "$(eval_gettext 'Keyboard layout now set to [$selection] and remembered in ${KEYMAP_CONFIG}${EXTRACONF}.')" 0 0 10000
	        echo $selection > $KEYMAP_CONFIG
	        
	        break
	    else
            # revert old keymap
            setxkbmap $active_xkeymap
	        continue
	    fi
    else
	    dlg --infobox "$(gettext 'Cancelled, nothing is changed.')" 0 0 5000
	    break 
    fi
done
