#!/usr/local/bin/bash #################################################################### # # The common routines for display a text-based UI using bash # #################################################################### # # global data # typeset -a _UI_RES_ # the active UI template typeset _KEY_ # the key input typeset _FOCUS_ # the current focus typeset _DATA_ # the field data typeset -i _DATA_ATTR_ # the display attribute of the field data (0/1/2) typeset _UI_UPDATED_ # whether the UI need to be updated (true/false) typeset _CLEAR_SCREEN_ # whether clear screen before display the UI (true/false) # # routine for tty setting # typeset old_tty_settings tty_init() { old_tty_settings=$(stty -g) stty -icanon -echo min 1 time 0 } tty_restore() { stty "$old_tty_settings" } # # read the UI template into the global variable # load_ui_res() { unset _UI_RES_[*] local -i i=0 local -i j=0 local -i k local -a patterns local -a replacements while read line; do if [ "${line:0:1}" != "~" ]; then patterns[$j]="${line%%=*}" replacements[$j]="${line#*=}" let j++ else line="${line#[~]}" k=0 while [ $k -lt ${#patterns[*]} ]; do if [ ${#line} -gt 0 ] && [ "${line%%*${patterns[$k]}*}" == "" ]; then left="${line%%${patterns[$k]}*}" right="${line#*${patterns[$k]}}" line="$left${replacements[$k]}$right" fi let k++ done while [ ${#line} -gt 0 ] && [ "${line%%*&[(]*[)]*}" == "" ]; do left="${line%%&[(]*}" line="${line#*&[(]}" hl_txt="${line%%[)]*}" line="${line#*[)]}" line="$left"'\e[1m'"$hl_txt"'\e[0m'"$line" done _UI_RES_[$i]="$line" let i++ fi done _UI_UPDATED_="false" _CLEAR_SCREEN_="true" } # # Create the text-based UI using the active UI template and the get_data call back function # # $1 -- *get_data func_name; # create_ui() { shopt -s extglob local -i i=0 while [ $i -lt ${#_UI_RES_[*]} ]; do line="${_UI_RES_[$i]}" while [ ${#line} -gt 0 ] && [ "${line%%*<[?]*[?]>*}" == "" ]; do echo -n "${line%%<[?]*}" line="${line#*<[?]}" var_info="${line%%[?]>*}" line="${line#*[?]>}" space_holder="${line%%[^#]*}" line="${line##*('#')}" name="${var_info%%[[,]*}" width="${var_info#*,}" if [ "$width" == "#" ]; then width=$((${#var_info}+4+${#space_holder})) fi if [ "$width" == "-#" ]; then width='-'$((${#var_info}+4+${#space_holder})) fi if [ "${var_info%%*[[]*[]]*}" == "" ]; then index="${var_info#*[[]}" index="${index%[]]*}" else index="-1" fi _DATA_ATTR_=0 $1 $name $index typeset -i w=$width if [ $w -lt 0 ]; then w=0-$w fi if [ ${#_DATA_} -gt $w ]; then _DATA_="${_DATA_:0:$(($w-3))}"'...' fi if [ $_DATA_ATTR_ -eq 2 ]; then printf '%s%'$width's%s' '\e[1;34;47m' "$_DATA_" '\e[0m' else if [ $_DATA_ATTR_ -eq 1 ]; then printf '%s%'$width's%s' '\e[1m' "$_DATA_" '\e[0m' else printf '%'$width's' "$_DATA_" fi fi done echo "$line" let i++ done } # # create the UI text and show it on the screen # update_ui() { if [ "$_UI_UPDATED_" == "false" ]; then ui_text="`create_ui $*`" if [ "$_CLEAR_SCREEN_" == "true" ]; then clear else tput cup 0 0 fi echo -e "$ui_text" _UI_UPDATED_="true" _CLEAR_SCREEN_="false" fi } # read the input key and put into the buffer # read_key () { ch=$(dd bs=1 count=1 <&0 2> /dev/null) case $ch in $'\x1b') read -s -n1 -t1 ch if [ "$?" == "0" ]; then case $ch in $'\x5b') read -s -n1 ch case $ch in $'\x41') _KEY_="up" ;; $'\x42') _KEY_="down" ;; H) _KEY_="home" ;; F) _KEY_="end" ;; 1) _KEY_="home" read -s -n1 ch ;; 4) _KEY_="end" read -s -n1 ch ;; 5) _KEY_="pgup" read -s -n1 ch ;; 6) _KEY_="pgdown" read -s -n1 ch ;; esac ;; esac else _KEY_="esc" fi ;; '') _KEY_="enter" ;; $'\x09') _KEY_="tab" ;; *) _KEY_="$ch" ;; esac } # # routines for list UI # # naming rules for list data # # {LIST_NAME}_array # {LIST_NAME}_height # {LIST_NAME}_top_index # {LIST_NAME}_sel_index # # # get list data function # # $1 -- list_name # $2 -- n # get_list_data() { local list_name=$1 n=$2 eval 'top_index=$'$list_name'_top_index' eval 'sel_index=$'$list_name'_sel_index' eval 'list_height=$'$list_name'_height' local -i index=$(($top_index+$n)) eval list_size='${#'$list_name'_array[*]}' _DATA_ATTR_=0 if [ $index -lt $list_size ]; then eval _DATA_='${'$list_name'_array['$index']}' if [ "$_FOCUS_" == "$list_name" ] && [ $index -eq $sel_index ]; then _DATA_ATTR_=2 fi else _DATA_="" fi } # # get list range info string # # $1 -- list name # $2 -- list hight # $3 -- display length # get_list_range() { local list_name=$1 local disp_len=$2 eval 'list_size=${#'$list_name'_array[*]}' eval 'top_index=$'$list_name'_top_index' eval 'list_height=$'$list_name'_height' hz_line="-----------------------------------------------------------------------------------" if [ $list_height -lt $list_size ]; then _DATA_="($(($top_index+1))-$(($top_index+$list_height))/$list_size)" left_len=$(($(($disp_len-${#_DATA_}))/2)) right_len=$(($disp_len-${#_DATA_}-$left_len)) _DATA_="${hz_line:0:$left_len}$_DATA_${hz_line:0:$right_len}" else _DATA_="${hz_line:0:$disp_len}" fi } # # scroll list operation # # $1 -- list_name # $2 -- operation (up/down) # scroll_list() { list_name=$1 operation=$2 eval 'list_size=${#'$list_name'_array[*]}' top_index=$list_name'_top_index' sel_index=$list_name'_sel_index' eval 'list_height=$'$list_name'_height' eval 'sel_index_v=$'"$sel_index" eval 'top_index_v=$'"$top_index" local old_sel_index=$sel_index_v case $operation in "up") if [ $sel_index_v -gt 0 ]; then eval "$sel_index="'$(($'"$sel_index"'-1))' fi ;; "down") if [ $sel_index_v -lt $(($list_size-1)) ]; then eval "$sel_index="'$(($'"$sel_index"'+1))' fi ;; "pgup") if [ $sel_index_v -ge $list_height ]; then eval 'let '"$sel_index-=$list_height" else eval "$sel_index=0" fi if [ $top_index_v -ge $list_height ]; then eval 'let '"$top_index-=$list_height" else eval "$top_index=0" fi ;; "pgdown") if [ $sel_index_v -lt $(($list_size-$list_height-1)) ]; then eval 'let '"$sel_index+=$list_height" else eval "$sel_index=$(($list_size-1))" fi if [ $top_index_v -lt $(($list_size-2*$list_height)) ]; then eval 'let '"$top_index+=$list_height" else eval "$top_index=$(($list_size-$list_height))" fi ;; "home") eval "$sel_index=0" ;; "end") eval "$sel_index=$(($list_size-1))" ;; "valid") if [ $list_size -gt 0 ]; then if [ $sel_index_v -ge $list_size ]; then eval "$sel_index=$(($list_size-1))" fi else eval "$sel_index=0" fi ;; esac eval 'sel_index_v=$'"$sel_index" eval 'top_index_v=$'"$top_index" if [ $sel_index_v -lt $top_index_v ]; then eval "$top_index=$sel_index_v" fi if [ $sel_index_v -ge $(($top_index_v+$list_height)) ]; then eval "$top_index="'$(('"$sel_index_v-$list_height+1"'))' fi if [ "$old_sel_index" != "$sel_index_v" ]; then _UI_UPDATED_="false" fi }