Entirely rewrote 'inquirer.sh' script to utilise 'dialog'. Updated 'installer.sh' dependencies and function calls accordingly.

This commit is contained in:
Rohan Barar 2024-07-15 23:21:56 +10:00
parent 903af2d0cd
commit be5d0f4b26
2 changed files with 175 additions and 823 deletions

View File

@ -1,835 +1,182 @@
#!/usr/bin/env bash
#======== Based on (then updated) https://raw.githubusercontent.com/tanhauhau/Inquirer.sh/master/dist/inquirer.sh ========
### GLOBAL CONSTANTS ###
declare -r ANSI_LIGHT_BLUE="\033[1;94m" # Light blue text.
declare -r ANSI_LIGHT_GREEN="\033[92m" # Light green text.
declare -r ANSI_CLEAR_TEXT="\033[0m" # Default text.
declare -r DIALOG_HEIGHT=14 # Height of dialog window.
declare -r TEXT_WIDTH_OFFSET=4 # Offset for fitting title text.
declare -r CHK_OPTION_WIDTH_OFFSET=10 # Offset for fitting options.
declare -r MNU_OPTION_WIDTH_OFFSET=7 # Offset for fitting options.
# License from: https://github.com/kahkhang/Inquirer.sh/blob/master/LICENSE
### FUNCTIONS ###
function inqMenu() {
# DECLARE VARIABLES.
# Variables created from function arguments:
declare DIALOG_TEXT="$1" # Dialog heading.
declare INPUT_OPTIONS_VAR="$2" # Input variable name.
declare RETURN_STRING_VAR="$3" # Output variable name.
declare -n INPUT_OPTIONS="$INPUT_OPTIONS_VAR" # Input array nameref.
declare -n RETURN_STRING="$RETURN_STRING_VAR" # Output string nameref.
# Note: namerefs allow changes made through the nameref to affect the
# referenced variable, even across different scopes like function calls.
# The MIT License (MIT)
# Other variables:
declare TRIMMED_OPTIONS=() # Input array post-trimming.
declare PADDED_OPTIONS=() # Input array with extra white space.
declare DIALOG_OPTIONS=() # Input array for options dialog.
declare DIALOG_WIDTH=0 # Width of dialog window.
declare OPTION_NUMBER=0 # Number of options in dialog window.
declare SELECTED_OPTIONS_STRING="" # Output value from dialog window.
# Copyright (c) 2017
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# shellcheck disable=all
# store the current set options
OLD_SET=$-
set -e
arrow="$(echo -e '\xe2\x9d\xaf')"
checked="$(echo -e '\xe2\x97\x89')"
unchecked="$(echo -e '\xe2\x97\xaf')"
down_arrow=$(echo -e '\u23f7')
up_arrow=$(echo -e '\u23f6')
black="$(tput setaf 0)"
red="$(tput setaf 1)"
green="$(tput setaf 2)"
yellow="$(tput setaf 3)"
blue="$(tput setaf 4)"
magenta="$(tput setaf 5)"
cyan="$(tput setaf 6)"
white="$(tput setaf 7)"
bold="$(tput bold)"
normal="$(tput sgr0)"
dim=$'\e[2m'
print() {
echo "$1"
tput el
}
join() {
local IFS=$'\n'
local _join_list
eval _join_list=( '"${'${1}'[@]}"' )
local first=true
for item in ${_join_list[@]}; do
if [ "$first" = true ]; then
printf "%s" "$item"
first=false
else
printf "${2-, }%s" "$item"
fi
done
}
function gen_env_from_options() {
local IFS=$'\n'
local _indices
local _env_names
local _checkbox_selected
eval _indices=( '"${'${1}'[@]}"' )
eval _env_names=( '"${'${2}'[@]}"' )
for i in $(gen_index ${#_env_names[@]}); do
_checkbox_selected[$i]=false
# MAIN LOGIC.
# Trim leading and trailing white space for each option.
for OPTION in "${INPUT_OPTIONS[@]}"; do
TRIMMED_OPTIONS+=("$(echo "$OPTION" | sed 's/^[ \t]*//;s/[ \t]*$//')")
done
for i in ${_indices[@]}; do
_checkbox_selected[$i]=true
done
for i in $(gen_index ${#_env_names[@]}); do
printf "%s=%s\n" "${_env_names[$i]}" "${_checkbox_selected[$i]}"
done
}
on_default() {
true;
}
on_keypress() {
local OLD_IFS
local IFS
local key
OLD_IFS=$IFS
local on_up=${1:-on_default}
local on_down=${2:-on_default}
local on_space=${3:-on_default}
local on_enter=${4:-on_default}
local on_left=${5:-on_default}
local on_right=${6:-on_default}
local on_ascii=${7:-on_default}
local on_backspace=${8:-on_default}
_break_keypress=false
while IFS="" read -rsn1 key; do
case "$key" in
$'\x1b')
read -rsn1 key
if [[ "$key" == "[" ]]; then
read -rsn1 key
case "$key" in
'A') eval $on_up ;;
'B') eval $on_down ;;
'D') eval $on_left ;;
'C') eval $on_right ;;
esac
fi
;;
' ') eval $on_space ' ' ;;
[a-z0-9A-Z\!\#\$\&\+\,\-\.\/\;\=\?\@\[\]\^\_\{\}\~]) eval $on_ascii $key ;;
$'\x7f') eval $on_backspace $key ;;
'') eval $on_enter $key ;;
esac
if [ $_break_keypress = true ]; then
break
fi
done
IFS=$OLD_IFS
}
gen_index() {
local k=$1
local l=0
if [ $k -gt 0 ]; then
for l in $(seq $k)
do
echo "$l-1" | bc
done
fi
}
cleanup() {
# Reset character attributes, make cursor visible, and restore
# previous screen contents (if possible).
tput sgr0
tput cnorm
stty echo
# Restore `set e` option to its orignal value
if [[ $OLD_SET =~ e ]]
then set -e
else set +e
fi
}
control_c() {
cleanup
exit $?
}
select_indices() {
local _select_list
local _select_indices
local _select_selected=()
eval _select_list=( '"${'${1}'[@]}"' )
eval _select_indices=( '"${'${2}'[@]}"' )
local _select_var_name=$3
eval $_select_var_name\=\(\)
for i in $(gen_index ${#_select_indices[@]}); do
eval $_select_var_name\+\=\(\""${_select_list[${_select_indices[$i]}]}"\"\)
done
}
print_checkbox_line_arrow() {
if [ "${_checkbox_selected[$1]}" = true ]; then
printf "${cyan}${arrow}${green}${checked}${normal} ${_checkbox_list[$1]} ${normal}"
else
printf "${cyan}${arrow}${normal}${unchecked} ${_checkbox_list[$1]} ${normal}"
fi
}
print_checkbox_line() {
if [ "${_checkbox_selected[$1]}" = true ]; then
printf " ${green}${checked}${normal} ${_checkbox_list[$1]} ${normal}"
else
printf " ${normal}${unchecked} ${_checkbox_list[$1]} ${normal}"
fi
}
# https://www.gnu.org/software/termutils/manual/termutils-2.0/html_chapter/tput_1.html
# http://linuxcommand.org/lc3_adv_tput.php
on_checkbox_input_up2() {
#remove_checkbox_instructions
tput cub "$(tput cols)"
if [ "${_checkbox_selected[$_current_index]}" = true ]; then
printf " ${green}${checked}${normal} ${_checkbox_list[$_current_index]} ${normal}"
else
printf " ${unchecked} ${_checkbox_list[$_current_index]} ${normal}"
fi
tput el
if [ $_current_index = 0 ]; then
_current_index=$((${#_checkbox_list[@]}-1))
tput cud $((${#_checkbox_list[@]}-1))
tput cub "$(tput cols)"
else
_current_index=$((_current_index-1))
tput cuu1 # Up one line
tput cub "$(tput cols)" # Back to beginning (does this work?)
tput el # Clear to end of line
fi
if [ "${_checkbox_selected[$_current_index]}" = true ]; then
printf "${cyan}${arrow}${green}${checked}${normal} ${_checkbox_list[$_current_index]} ${normal}"
else
printf "${cyan}${arrow}${normal}${unchecked} ${_checkbox_list[$_current_index]} ${normal}"
fi
}
on_checkbox_input_up() {
#remove_checkbox_instructions
tput cub "$(tput cols)"
if (( ${_current_row} > 0 )) || (( ${#_checkbox_list[@]} <= 5 )); then
print_checkbox_line $_current_index
tput el
if [ $_current_index = 0 ]; then
_current_index=$((${#_checkbox_list[@]}-1))
_current_row=4
tput cud $((${#_checkbox_list[@]}-1))
tput cub "$(tput cols)"
else
_current_index=$((_current_index-1))
_current_row=$((_current_row-1))
tput cuu1
tput cub "$(tput cols)"
tput el
fi
print_checkbox_line_arrow $_current_index
else
if [ $_current_index = 0 ]; then
_current_index=$((${#_checkbox_list[@]}-1))
_current_row=4
tput cuu 1
tput cub "$(tput cols)"
tput el
printf " ${cyan}${up_arrow}${normal}"
for I in 4 3 2 1; do
tput cud1
tput cub "$(tput cols)"
tput el
print_checkbox_line $((_current_index-I))
done
tput cud1
tput cub "$(tput cols)"
tput el
print_checkbox_line_arrow $((_current_index))
tput cud1
tput cub "$(tput cols)"
tput el
printf " ${dim}${down_arrow}${normal}"
tput cuu 1
else
_current_index=$((_current_index-1))
tput cud 5
tput cub "$(tput cols)"
tput el
printf " ${cyan}${down_arrow}${normal}"
for I in 4 3 2 1; do
tput cuu1
tput cub "$(tput cols)"
tput el
print_checkbox_line $((_current_index+I))
done
tput cuu1
tput cub "$(tput cols)"
tput el
print_checkbox_line_arrow $((_current_index))
if [ $_current_index = 0 ]; then
tput cuu1
tput cub "$(tput cols)"
tput el
printf " ${dim}${up_arrow}${normal}"
tput cud1
fi
fi
fi
}
on_checkbox_input_down() {
#remove_checkbox_instructions
tput cub "$(tput cols)"
if (( ${_current_row} < 4 )) || (( ${#_checkbox_list[@]} <= 5 )); then
print_checkbox_line $_current_index
tput el
if [ $_current_index = $((${#_checkbox_list[@]}-1)) ]; then
_current_index=0
_current_row=0
tput cuu $((${#_checkbox_list[@]}-1))
tput cub "$(tput cols)"
else
_current_index=$((_current_index+1))
_current_row=$((_current_row+1))
tput cud1
tput cub "$(tput cols)"
tput el
fi
print_checkbox_line_arrow $_current_index
else
if [ $_current_index = $((${#_checkbox_list[@]}-1)) ]; then
_current_index=0
_current_row=0
tput cuu 5
tput cub "$(tput cols)"
tput el
printf " ${dim}${up_arrow}${normal}"
tput cud1
tput cub "$(tput cols)"
tput el
print_checkbox_line_arrow $((_current_index))
for I in 1 2 3 4; do
tput cud1
tput cub "$(tput cols)"
tput el
print_checkbox_line $((_current_index+I))
done
tput cud1
tput cub "$(tput cols)"
tput el
printf " ${cyan}${down_arrow}${normal}"
tput cuu 5
else
_current_index=$((_current_index+1))
tput cuu 5
tput cub "$(tput cols)"
tput el
printf " ${cyan}${up_arrow}${normal}"
for I in 4 3 2 1; do
tput cud1
tput cub "$(tput cols)"
tput el
print_checkbox_line $((_current_index-I))
done
tput cud1
tput cub "$(tput cols)"
tput el
print_checkbox_line_arrow $((_current_index))
if [ $_current_index = $((${#_checkbox_list[@]}-1)) ]; then
tput cud1
tput cub "$(tput cols)"
tput el
printf " ${dim}${down_arrow}${normal}"
tput cuu1
fi
fi
fi
}
on_checkbox_input_enter() {
remove_checkbox_instructions
local OLD_IFS
OLD_IFS=$IFS
_checkbox_selected_indices=()
_checkbox_selected_options=()
IFS=$'\n'
for i in $(gen_index ${#_checkbox_list[@]}); do
if [ "${_checkbox_selected[$i]}" = true ]; then
_checkbox_selected_indices+=($i)
_checkbox_selected_options+=("${_checkbox_list[$i]}")
# Find the length of the longest option to set the dialog width.
for OPTION in "${TRIMMED_OPTIONS[@]}"; do
if [ "${#OPTION}" -gt $DIALOG_WIDTH ]; then
DIALOG_WIDTH=${#OPTION}
fi
done
if (( ${#_checkbox_list[@]} <= 5 )); then
tput cud $((${#_checkbox_list[@]}-${_current_index}))
tput cub "$(tput cols)"
for i in $(seq $((${#_checkbox_list[@]}+1))); do
tput el1
tput el
tput cuu1
done
else
tput cud $((6-${_current_row}))
tput cub "$(tput cols)"
for i in $(seq 8); do
tput el1
tput el
tput cuu1
done
fi
tput cub "$(tput cols)"
# Apply the offset value to the dialog width.
DIALOG_WIDTH=$((DIALOG_WIDTH + MNU_OPTION_WIDTH_OFFSET))
tput cuf $((${#prompt}+3))
printf "${cyan}$(join _checkbox_selected_options)${normal}"
tput el
tput cud1
tput cub "$(tput cols)"
tput el
_break_keypress=true
IFS=$OLD_IFS
}
on_checkbox_input_space() {
#remove_checkbox_instructions
tput cub "$(tput cols)"
tput el
if [ "${_checkbox_selected[$_current_index]}" = true ]; then
_checkbox_selected[$_current_index]=false
else
_checkbox_selected[$_current_index]=true
# Adjust the dialog width again if the dialog text is longer.
if [ "$DIALOG_WIDTH" -lt $((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET)) ]; then
DIALOG_WIDTH="$((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET))"
fi
print_checkbox_line_arrow $_current_index
}
remove_checkbox_instructions() {
if [ $_first_keystroke = true ]; then
tput cuu $((${_current_index}+1))
tput cub "$(tput cols)"
tput cuf $((${#prompt}+3))
tput el
tput cud $((${_current_index}+1))
_first_keystroke=false
fi
}
# for vim movements
on_checkbox_input_ascii() {
local key=$1
case $key in
"j" ) on_checkbox_input_down ;;
"k" ) on_checkbox_input_up ;;
esac
}
_checkbox_input() {
local i
local j
prompt=$1
eval _checkbox_list=( '"${'${2}'[@]}"' )
_current_index=0
_current_row=0
_first_keystroke=true
trap control_c SIGINT EXIT
stty -echo
tput civis
print "${normal}${green}?${normal} ${bold}${prompt}${normal} ${dim}(Press <space> to select, <enter> to finalize)${normal}"
for i in $(gen_index ${#_checkbox_list[@]}); do
_checkbox_selected[$i]=false
# Pad option text with trailing white space to left-align all options.
for OPTION in "${TRIMMED_OPTIONS[@]}"; do
local PAD_LENGTH=$((DIALOG_WIDTH - MNU_OPTION_WIDTH_OFFSET - ${#OPTION}))
local PADDED_OPTION="${OPTION}$(printf '%*s' $PAD_LENGTH)"
PADDED_OPTIONS+=("$PADDED_OPTION")
done
if [ -n "$3" ]; then
eval _selected_indices=( '"${'${3}'[@]}"' )
for i in ${_selected_indices[@]}; do
_checkbox_selected[$i]=true
done
fi
# Convert options into the appropriate format for a 'dialog' menu.
for PADDED_OPTION in "${PADDED_OPTIONS[@]}"; do
DIALOG_OPTIONS+=("$PADDED_OPTION" "")
done
if (( ${#_checkbox_list[@]} > 5 )); then
tput cub "$(tput cols)"
print " ${dim}${up_arrow}${normal}"
fi
# Store the number of options.
OPTION_NUMBER="${#INPUT_OPTIONS[@]}"
for i in $(gen_index ${#_checkbox_list[@]}); do
tput cub "$(tput cols)"
if [ $i = 0 ]; then
print_checkbox_line_arrow $i
else
print_checkbox_line $i
fi
print ""
tput el
if (( $i > 3 )) && (( ${#_checkbox_list[@]} > 5 )); then
print " ${cyan}${down_arrow}${normal}"
break
# Produce checkbox.
# The output string contains options delimited by spaces.
# Each option is enclosed in double quotes within the output string.
# For example: '"Option 1 " "The Second Option " " Option Number 3 "'
SELECTED_OPTIONS_STRING=$(dialog \
--keep-tite \
--clear \
--no-shadow \
--menu \
"$DIALOG_TEXT" \
"$DIALOG_HEIGHT" \
"$DIALOG_WIDTH" \
"$OPTION_NUMBER" \
"${DIALOG_OPTIONS[@]}" \
2>&1 >/dev/tty) || exit 0
# Remove white space added previously.
RETURN_STRING=$(echo "$SELECTED_OPTIONS_STRING" | \
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
# Remove escapes (introduced by 'dialog' if options have parentheses).
RETURN_STRING=$(echo "$RETURN_STRING" | sed 's/\\//g')
# Display question and response.
echo -e "${ANSI_LIGHT_GREEN}Q) ${ANSI_CLEAR_TEXT}${ANSI_LIGHT_BLUE}${DIALOG_TEXT}${ANSI_CLEAR_TEXT} --> ${ANSI_LIGHT_GREEN}${RETURN_STRING}${ANSI_CLEAR_TEXT}"
}
function inqChkBx() {
# DECLARE VARIABLES.
# Variables created from function arguments:
declare DIALOG_TEXT="$1" # Dialog heading.
declare INPUT_OPTIONS_VAR="$2" # Input variable name.
declare RETURN_ARRAY_VAR="$3" # Output variable name.
declare -n INPUT_OPTIONS="$INPUT_OPTIONS_VAR" # Input array nameref.
declare -n RETURN_ARRAY="$RETURN_ARRAY_VAR" # Output array nameref.
# Note: namerefs allow changes made through the nameref to affect the
# referenced variable, even across different scopes like function calls.
# Other variables:
declare TRIMMED_OPTIONS=() # Input array post-trimming.
declare PADDED_OPTIONS=() # Input array with extra white space.
declare DIALOG_OPTIONS=() # Input array for options dialog.
declare DIALOG_WIDTH=0 # Width of dialog window.
declare OPTION_NUMBER=0 # Number of options in dialog window.
declare SELECTED_OPTIONS_STRING="" # Output value from dialog window.
# MAIN LOGIC.
# Trim leading and trailing white space for each option.
for OPTION in "${INPUT_OPTIONS[@]}"; do
TRIMMED_OPTIONS+=("$(echo "$OPTION" | sed 's/^[ \t]*//;s/[ \t]*$//')")
done
# Find the length of the longest option to set the dialog width.
for OPTION in "${TRIMMED_OPTIONS[@]}"; do
if [ "${#OPTION}" -gt $DIALOG_WIDTH ]; then
DIALOG_WIDTH=${#OPTION}
fi
done
for j in $(gen_index ${#_checkbox_list[@]}); do
tput cuu1
if (( $j > 4 )); then
break
fi
# Apply the offset value to the dialog width.
DIALOG_WIDTH=$((DIALOG_WIDTH + CHK_OPTION_WIDTH_OFFSET))
# Adjust the dialog width again if the dialog text is longer.
if [ "$DIALOG_WIDTH" -lt $((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET)) ]; then
DIALOG_WIDTH="$((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET))"
fi
# Pad option text with trailing white space to left-align all options.
for OPTION in "${TRIMMED_OPTIONS[@]}"; do
local PAD_LENGTH=$((DIALOG_WIDTH - CHK_OPTION_WIDTH_OFFSET - ${#OPTION}))
local PADDED_OPTION="${OPTION}$(printf '%*s' $PAD_LENGTH)"
PADDED_OPTIONS+=("$PADDED_OPTION")
done
on_keypress on_checkbox_input_up on_checkbox_input_down on_checkbox_input_space on_checkbox_input_enter on_default on_default on_checkbox_input_ascii
}
checkbox_input() {
_checkbox_input "$1" "$2"
_checkbox_input_output_var_name=$3
select_indices _checkbox_list _checkbox_selected_indices $_checkbox_input_output_var_name
unset _checkbox_list
unset _break_keypress
unset _first_keystroke
unset _current_index
unset _checkbox_input_output_var_name
unset _checkbox_selected_indices
unset _checkbox_selected_options
cleanup
}
checkbox_input_indices() {
_checkbox_input "$1" "$2" "$3"
_checkbox_input_output_var_name=$3
eval $_checkbox_input_output_var_name\=\(\)
for i in $(gen_index ${#_checkbox_selected_indices[@]}); do
eval $_checkbox_input_output_var_name\+\=\(${_checkbox_selected_indices[$i]}\)
# Convert options into the appropriate format for a 'dialog' checkbox.
for PADDED_OPTION in "${PADDED_OPTIONS[@]}"; do
DIALOG_OPTIONS+=("$PADDED_OPTION" "" off)
done
unset _checkbox_list
unset _break_keypress
unset _first_keystroke
unset _current_index
unset _checkbox_input_output_var_name
unset _checkbox_selected_indices
unset _checkbox_selected_options
# Store the number of options.
OPTION_NUMBER="${#INPUT_OPTIONS[@]}"
cleanup
}
# Produce checkbox.
# The output string contains options delimited by spaces.
# Each option is enclosed in double quotes within the output string.
# For example: '"Option 1 " "The Second Option " " Option Number 3 "'
SELECTED_OPTIONS_STRING=$(dialog \
--keep-tite \
--clear \
--no-shadow \
--checklist \
"$DIALOG_TEXT" \
"$DIALOG_HEIGHT" \
"$DIALOG_WIDTH" \
"$OPTION_NUMBER" \
"${DIALOG_OPTIONS[@]}" \
2>&1 >/dev/tty) || exit 0
# Convert the output string into an array.
while IFS= read -r LINE; do
LINE="${LINE/#\"}" # Remove leading double quote.
LINE="${LINE/%\"}" # Remove trailing double quote.
RETURN_ARRAY+=("$LINE") # Add to array.
done < <(echo "$SELECTED_OPTIONS_STRING" | sed 's/\" \"/\"\n\"/g')
# Final modifications.
for (( i=0; i<${#RETURN_ARRAY[@]}; i++ )); do
# Remove white space added previously.
RETURN_ARRAY[i]=$(echo "${RETURN_ARRAY[i]}" | \
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
on_list_input_up() {
remove_list_instructions
tput cub "$(tput cols)"
printf " ${_list_options[$_list_selected_index]}"
tput el
if [ $_list_selected_index = 0 ]; then
_list_selected_index=$((${#_list_options[@]}-1))
tput cud $((${#_list_options[@]}-1))
tput cub "$(tput cols)"
else
_list_selected_index=$((_list_selected_index-1))
tput cuu1
tput cub "$(tput cols)"
tput el
fi
printf "${cyan}${arrow} %s ${normal}" "${_list_options[$_list_selected_index]}"
}
on_list_input_down() {
remove_list_instructions
tput cub "$(tput cols)"
printf " ${_list_options[$_list_selected_index]}"
tput el
if [ $_list_selected_index = $((${#_list_options[@]}-1)) ]; then
_list_selected_index=0
tput cuu $((${#_list_options[@]}-1))
tput cub "$(tput cols)"
else
_list_selected_index=$((_list_selected_index+1))
tput cud1
tput cub "$(tput cols)"
tput el
fi
printf "${cyan}${arrow} %s ${normal}" "${_list_options[$_list_selected_index]}"
}
on_list_input_enter_space() {
local OLD_IFS
OLD_IFS=$IFS
IFS=$'\n'
tput cud $((${#_list_options[@]}-${_list_selected_index}))
tput cub "$(tput cols)"
for i in $(seq $((${#_list_options[@]}+1))); do
tput el1
tput el
tput cuu1
# Remove escapes (introduced by 'dialog' if options have parentheses).
RETURN_ARRAY[i]=$(echo "${RETURN_ARRAY[i]}" | sed 's/\\//g')
done
tput cub "$(tput cols)"
tput cuf $((${#prompt}+3))
printf "${cyan}${_list_options[$_list_selected_index]}${normal}"
tput el
tput cud1
tput cub "$(tput cols)"
tput el
_break_keypress=true
IFS=$OLD_IFS
}
remove_list_instructions() {
if [ $_first_keystroke = true ]; then
tput cuu $((${_list_selected_index}+1))
tput cub "$(tput cols)"
tput cuf $((${#prompt}+3))
tput el
tput cud $((${_list_selected_index}+1))
_first_keystroke=false
fi
}
_list_input() {
local i
local j
prompt=$1
eval _list_options=( '"${'${2}'[@]}"' )
_list_selected_index=0
_first_keystroke=true
trap control_c SIGINT EXIT
stty -echo
tput civis
print "${normal}${green}?${normal} ${bold}${prompt}${normal} ${dim}(Use arrow keys)${normal}"
for i in $(gen_index ${#_list_options[@]}); do
tput cub "$(tput cols)"
if [ $i = 0 ]; then
print "${cyan}${arrow} ${_list_options[$i]} ${normal}"
else
print " ${_list_options[$i]}"
fi
tput el
done
for j in $(gen_index ${#_list_options[@]}); do
tput cuu1
done
on_keypress on_list_input_up on_list_input_down on_list_input_enter_space on_list_input_enter_space
}
list_input() {
_list_input "$1" "$2"
local var_name=$3
eval $var_name=\'"${_list_options[$_list_selected_index]}"\'
unset _list_selected_index
unset _list_options
unset _break_keypress
unset _first_keystroke
cleanup
}
list_input_index() {
_list_input "$1" "$2"
local var_name=$3
eval $var_name=\'"$_list_selected_index"\'
unset _list_selected_index
unset _list_options
unset _break_keypress
unset _first_keystroke
cleanup
}
on_text_input_left() {
remove_regex_failed
if [ $_current_pos -gt 0 ]; then
tput cub1
_current_pos=$(($_current_pos-1))
fi
}
on_text_input_right() {
remove_regex_failed
if [ $_current_pos -lt ${#_text_input} ]; then
tput cuf1
_current_pos=$(($_current_pos+1))
fi
}
on_text_input_enter() {
remove_regex_failed
if [[ "$_text_input" =~ $_text_input_regex && "$(eval $_text_input_validator "$_text_input")" = true ]]; then
tput cub "$(tput cols)"
tput cuf $((${#_read_prompt}-19))
printf "${cyan}${_text_input}${normal}"
tput el
tput cud1
tput cub "$(tput cols)"
tput el
eval $var_name=\'"${_text_input}"\'
_break_keypress=true
else
_text_input_regex_failed=true
tput civis
tput cud1
tput cub "$(tput cols)"
tput el
printf "${red}>>${normal} $_text_input_regex_failed_msg"
tput cuu1
tput cub "$(tput cols)"
tput cuf $((${#_read_prompt}-19))
tput el
_text_input=""
_current_pos=0
tput cnorm
fi
}
on_text_input_ascii() {
remove_regex_failed
local c=$1
if [ "$c" = '' ]; then
c=' '
fi
local rest="${_text_input:$_current_pos}"
_text_input="${_text_input:0:$_current_pos}$c$rest"
_current_pos=$(($_current_pos+1))
tput civis
printf "$c$rest"
tput el
if [ ${#rest} -gt 0 ]; then
tput cub ${#rest}
fi
tput cnorm
}
on_text_input_backspace() {
remove_regex_failed
if [ $_current_pos -gt 0 ]; then
local start="${_text_input:0:$(($_current_pos-1))}"
local rest="${_text_input:$_current_pos}"
_current_pos=$(($_current_pos-1))
tput cub 1
tput el
tput sc
printf "$rest"
tput rc
_text_input="$start$rest"
fi
}
remove_regex_failed() {
if [ $_text_input_regex_failed = true ]; then
_text_input_regex_failed=false
tput sc
tput cud1
tput el1
tput el
tput rc
fi
}
text_input_default_validator() {
echo true;
}
text_input() {
local prompt=$1
local var_name=$2
local _text_input_regex="${3:-"\.+"}"
local _text_input_regex_failed_msg=${4:-"Input validation failed"}
local _text_input_validator=${5:-text_input_default_validator}
local _read_prompt_start=$'\e[32m?\e[39m\e[1m'
local _read_prompt_end=$'\e[22m'
local _read_prompt="$( echo "$_read_prompt_start ${prompt} $_read_prompt_end")"
local _current_pos=0
local _text_input_regex_failed=false
local _text_input=""
printf "$_read_prompt"
trap control_c SIGINT EXIT
stty -echo
tput cnorm
on_keypress on_default on_default on_text_input_ascii on_text_input_enter on_text_input_left on_text_input_right on_text_input_ascii on_text_input_backspace
eval $var_name=\'"${_text_input}"\'
cleanup
}
# =============================================================
function menuFromCmd() {
local mLOCALRESULT=$1
local mRESULT=''
read -r -a ARRAY <<< $3
list_input "$2" ARRAY mRESULT
eval $mLOCALRESULT="'$mRESULT'"
}
function menuFromArr() {
local mLOCALRESULT=$1
shift
local PROMPT=$1
shift
local ARRAY=("$@")
list_input "$PROMPT" ARRAY mRESULT
eval $mLOCALRESULT="'$mRESULT'"
}
function multiFromArr() {
local mLOCALRESULT=$1
shift
local PROMPT=$1
shift
local ARRAY=("$@")
checkbox_input "$PROMPT" ARRAY mRESULT
eval $mLOCALRESULT="'$mRESULT'"
}

View File

@ -216,7 +216,7 @@ function waCheckInput() {
else
# Install vs. uninstall?
OPTIONS=("Install" "Uninstall")
menuFromArr SELECTED_OPTION "Install or uninstall WinApps?" "${OPTIONS[@]}"
inqMenu "Install or uninstall WinApps?" OPTIONS SELECTED_OPTION
# Set flags.
if [[ "$SELECTED_OPTION" == "Uninstall" ]]; then
@ -225,7 +225,7 @@ function waCheckInput() {
# User vs. system?
OPTIONS=("Current User" "System")
menuFromArr SELECTED_OPTION "Configure WinApps for the current user '$(whoami)' or the whole system?" "${OPTIONS[@]}"
inqMenu "Configure WinApps for the current user '$(whoami)' or the whole system?" OPTIONS SELECTED_OPTION
# Set flags.
if [[ "$SELECTED_OPTION" == "Current User" ]]; then
@ -236,8 +236,8 @@ function waCheckInput() {
# Automatic vs. manual?
if [ "$OPT_UNINSTALL" -eq 0 ]; then
OPTIONS=("Automatic" "Manual")
menuFromArr SELECTED_OPTION "Automatically install supported applications or choose manually?" "${OPTIONS[@]}"
OPTIONS=("Manual (Default)" "Automatic")
inqMenu "Automatically install supported applications or choose manually?" OPTIONS SELECTED_OPTION
# Set flags.
if [[ "$SELECTED_OPTION" == "Automatic" ]]; then
@ -428,8 +428,8 @@ function waCheckDependencies() {
# Print feedback.
echo -n "Checking whether all dependencies are installed... "
# 'Basic Calculator'.
if ! command -v bc &>/dev/null; then
# 'Dialog'.
if ! command -v dialog &>/dev/null; then
# Complete the previous line.
echo -e "${FAIL_TEXT}Failed!${CLEAR_TEXT}\n"
@ -437,18 +437,18 @@ function waCheckDependencies() {
echo -e "${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}"
# Display the error details.
echo -e "${INFO_TEXT}Please install 'Basic Calculator' to proceed.${CLEAR_TEXT}"
echo -e "${INFO_TEXT}Please install 'dialog' to proceed.${CLEAR_TEXT}"
# Display the suggested action(s).
echo "--------------------------------------------------------------------------------"
echo "Debian/Ubuntu-based systems:"
echo -e " ${COMMAND_TEXT}sudo apt install bc${CLEAR_TEXT}"
echo -e " ${COMMAND_TEXT}sudo apt install dialog${CLEAR_TEXT}"
echo "Red Hat/Fedora-based systems:"
echo -e " ${COMMAND_TEXT}sudo dnf install bc${CLEAR_TEXT}"
echo -e " ${COMMAND_TEXT}sudo dnf install dialog${CLEAR_TEXT}"
echo "Arch Linux systems:"
echo -e " ${COMMAND_TEXT}sudo pacman -S bc${CLEAR_TEXT}"
echo -e " ${COMMAND_TEXT}sudo pacman -S dialog${CLEAR_TEXT}"
echo "Gentoo Linux systems:"
echo -e " ${COMMAND_TEXT}sudo emerge --ask sys-apps/bc${CLEAR_TEXT}"
echo -e " ${COMMAND_TEXT}sudo emerge --ask dialog${CLEAR_TEXT}"
echo "--------------------------------------------------------------------------------"
# Terminate the script.
@ -1033,6 +1033,7 @@ function waConfigureApps() {
local OPTIONS=() # Stores a list of options presented to the user.
local APP_INSTALL="" # Stores the option selected by the user.
local SELECTED_APPS=() # Stores the officially supported applications selected by the user.
local TEMP_ARRAY=() # Temporary array used for sorting elements of an array.
# Read the list of officially supported applications that are installed on the Windows VM into an array, returning an empty array if no such files exist.
# This will remove leading and trailing whitespace characters as well as ignore empty lines.
@ -1065,8 +1066,9 @@ function waConfigureApps() {
# Sort the 'APPS' array in alphabetical order.
IFS=$'\n'
readarray -t APPS < <(sort <<<"${APPS[*]}")
TEMP_ARRAY=($(sort <<<"${APPS[*]}"))
unset IFS
APPS=("${TEMP_ARRAY[@]}")
# Prompt user to select which officially supported applications to configure.
OPTIONS=(
@ -1074,11 +1076,11 @@ function waConfigureApps() {
"Choose specific officially supported applications to set up"
"Skip setting up any officially supported applications"
)
menuFromArr APP_INSTALL "How would you like to handle officially supported applications?" "${OPTIONS[@]}"
inqMenu "How would you like to handle officially supported applications?" OPTIONS APP_INSTALL
# Remove unselected officially supported applications from the 'install' file.
if [[ "$APP_INSTALL" == "Choose specific officially supported applications to set up" ]]; then
checkbox_input "Which officially supported applications would you like to set up?" APPS SELECTED_APPS
inqChkBx "Which officially supported applications would you like to set up?" APPS SELECTED_APPS
# Clear/create the 'install' file.
echo "" >"$INST_FILE_PATH"
@ -1113,6 +1115,7 @@ function waConfigureDetectedApps() {
local APP_INSTALL="" # Stores the option selected by the user.
local SELECTED_APPS=() # Detected applications selected by the user.
local APP_DESKTOP_FILE="" # Stores the '.desktop' file used to launch the application.
local TEMP_ARRAY=() # Temporary array used for sorting elements of an array.
if [ -f "$DETECTED_FILE_PATH" ]; then
# On UNIX systems, lines are terminated with a newline character (\n).
@ -1146,8 +1149,10 @@ function waConfigureDetectedApps() {
done
# Sort the 'APPS' array in alphabetical order.
IFS=$'\n' APPS=("$(sort <<<"${APPS[*]}")")
IFS=$'\n'
TEMP_ARRAY=($(sort <<<"${APPS[*]}"))
unset IFS
APPS=("${TEMP_ARRAY[@]}")
# Prompt user to select which other detected applications to configure.
OPTIONS=(
@ -1155,11 +1160,11 @@ function waConfigureDetectedApps() {
"Select which applications to set up"
"Do not set up any applications"
)
menuFromArr APP_INSTALL "How would you like to handle other detected applications?" "${OPTIONS[@]}"
inqMenu "How would you like to handle other detected applications?" OPTIONS APP_INSTALL
# Store selected detected applictions.
# Store selected detected applications.
if [[ "$APP_INSTALL" == "Select which applications to set up" ]]; then
checkbox_input "Which other applications would you like to set up?" APPS SELECTED_APPS
inqChkBx "Which other applications would you like to set up?" APPS SELECTED_APPS
elif [[ "$APP_INSTALL" == "Set up all detected applications" ]]; then
readarray -t SELECTED_APPS <<<"${APPS[@]}"
fi