#!/bin/ash
# vim: foldmarker=[[[,]]]:

# =============================================================================
# Find'N'Run - find and run apps very quickly
  Version=1.10.6
# authors: March-May 2015 by SFR and L18L; onwards gawk fork by step
# license: GNU GPL applies
# requirements: gtk-dialog >= 0.8.3, gawk, ash (or bash)
# source: https://github.com/step-/find-n-run
# forum: http://www.murga-linux.com/puppy/viewtopic.php?t=98330
# =============================================================================

# Localization settings. [[[1
# i18n findnrun.pot file generated with:
# i18n xgettext -ci18n -L Shell -o findnrun.pot --no-wrap --package-name=find-n-run --package-version=1.10.6 --msgid-bugs-address=https://github.com/step-/find-n-run/issues/ findnrun
export TEXTDOMAIN=findnrun
export OUTPUT_CHARSET=UTF-8

# Initialization variables that can be changed. [[[1
# i18n Main window title
APP_NAME=$(gettext "Find'N'Run")
APP_TITLE="${APP_NAME}"

XDG_DATA_DIRS=${XDG_DATA_DIRS:-/usr/share:/usr/local/share}
XDG_DATA_HOME=${XDG_DATA_HOME:-$HOME/.local}

{ read DESKTOP_FILE_DIRS; read ICON_DIRS; } << EOF
$(
  IFS=:
  set -- ${XDG_DATA_HOME} ${XDG_DATA_DIRS}
  for i; do echo -n " $i/applications"; done; echo
  for i; do echo -n " $i/icons"; done; echo
)
EOF
# Ref. http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout
# Note: Search by icon *theme* isn't implemented.
# Additional Fatdog64-specific locations start at .../midi-icons.
ICON_DIRS="$HOME/.icons ${ICON_DIRS} /usr/share/pixmaps /usr/share/midi-icons /usr/share/mini-icons"

[ -f "${CONFIG}" ] || CONFIG="${HOME}/.findnrunrc"
HELPFILE=/usr/share/doc/findnrun/help[en].tar.gz:/usr/share/doc/findnrun/index.html:/usr/share/doc/findnrun/index.md

# Initialization variables that should not be changed. [[[1
REMARK= # leave unset
DEBUG=${DEBUG:-} # unset to disable debug trace to stderr; set verbosity (integer) 1-3
trap 'rm -rf "${TMPF:-/tmp/dummy}"*' INT QUIT TERM HUP 0
TMPF=`mktemp -d -p "${TMPDIR:-/tmp}" ${0##*/}_XXXXXX` && chmod 755 "${TMPF}"
DATF="${TMPF}/.dat" # database of .desktop file entries
AWKF="${TMPF}/.build.awk" # builds (and queries) database
AWKQ="${TMPF}/.query.awk" # queries database (faster)
HSTF="${TMPF}/.sh"; >"${HSTF}" # saves combobox command line history
FCSF="${TMPF}/.fcs"; >"${FCSF}" # triggers varSEARCH focus grabber
SEP=`echo -e "\b"` # field separator of packed values
ICONCACHE="${HOME}/.icons" # location of icon cache files (in $CONFIG).

# -----------------------------------------------------------------------------
#LANG=de_DE.UTF-8 # just for demo
LR=${LANG%.*} #ex:pt_BR
LL=${LANG%_*} #ex:pt

# -----------------------------------------------------------------------------
# Unexport these variables before running the selected command.
UNEXPORT="unset GUI_ABOUT varSEARCH varLIST varFOCUSGRABBER varCMD varCOMMENT varOPEN varICON varFOCUSSEARCH TEXTDOMAIN OUTPUT_CHARSET"

# Prepare the configuration file. [[[1
# Defaults.
defOPEN=false
export varICONS=false
export varFOCUSSEARCH=false

# Read / supplement $CONFIG.
touch "${CONFIG}" && . "${CONFIG}" # must exist even if empty
# Add missing/hidden defaults.
awk '
  /^ICONCACHE=/{f1=1}
  /^SEARCHCOMMENTS=/{f2=1}
  /^SEARCHFROMLEFT=/{f3=1}
  /^SEARCHREGEX=/{f4=1}
  /^CASEDEPENDENT=/{f5=1}
  # GEOMETRY=WxH+X+Y has no default, environment is overridden
  # DESKTOP_FILE_DIRS initialized from system values, environment overrides
  # ICON_DIRS initialized from has system values, environment overrides
  /^SEARCHCATEGORIES=/{f6=1}
  END{
    if(!f1) print "ICONCACHE='"${ICONCACHE}"'">>ARGV[1]
    if(!f2) print "SEARCHCOMMENTS=false">>ARGV[1]
    if(!f3) print "SEARCHFROMLEFT=false">>ARGV[1]
    if(!f4) print "SEARCHREGEX=false">>ARGV[1]
    if(!f5) print "CASEDEPENDENT=false">>ARGV[1]
    if(!f6) print "SEARCHCATEGORIES=false">>ARGV[1]
  }
  ' "${CONFIG}"

# Values that depend on defaults.
ICONSTEM="${ICONCACHE}/findnrun-" # prefix to worked-around icons.

# Parse command line --options. [[[1
while ! [ "${1#--}" = "$1" ]; do
  case "$1" in
    --geometry=*) # Override GEOMETRY set in $CONFIG, if any.
      o=${1#--geometry=}; GEOMETRY=${o%--geometry}
      ;;
    --perm=*|--perm) # Tighten tempdir permissions from 755
      o=${1#--perm=}; o=${o%--perm}; chmod "${o:-700}" "${TMPF}"
      ;;
    --) shift; CMDLINEOPTS="$@"; break ;; # pass CMDLINEOPTS to gtkdialog
    --stdout) ENABLESTDOUT=1 ;; # Don't redirect gtkdialog's stdout to null
    --*) echo "${0##*/}: invalid option $1" >&2; exit 1
    ;;
  esac
  shift
done

# More environment variables: BROWSER, LANG [[[1
# =============================================================================

which gtkdialog4 >/dev/null 2>&1 && GTKDIALOG=gtkdialog4 || GTKDIALOG=gtkdialog

# -----------------------------------------------------------------------------
# Set LOCS to the glob that lists valid .desktop file locations.
# Do this in the main shell after having processed script option.
for i in ${DESKTOP_FILE_DIRS}; do
  set -- "$i"/*.desktop
  if [ "$1" != "${1%/*.desktop}/*.desktop" ]; then
    x=${1%% }; x=${x%/*}; LOCS="${LOCS}${LOCS+ }$x/*.desktop"
  else
    : # LOCi is empty or non-existent
  fi
done

# Prepare the database builder script. [[[1
# Usage: gawk -f "scriptpath" [-v GREP="string"] [-v ALL_ICONS=true] files
[ -x /bin/dash ] && SH=/bin/dash || SH=/bin/ash
> "${AWKF}" echo '#!/usr/bin/gawk -f
BEGIN {
  if("'${DEBUG}'") print "\nSTARTING AWK builder, ALL_ICONS="ALL_ICONS >"/dev/stderr"
  RS="^~cannot~match~me~" # enable slurp read mode.
  # Choose a shell for ongoing command execution - see icon_workaround().
  sh = "'${SH}'" # not used as a coprocess
  ICONSTEM = "'"${ICONSTEM}"'" # prefix to worked-around icons.
  if("'${DEBUG}'") print "ICONSTEM="ICONSTEM >"/dev/stderr"
}
{
  # Slurp NFILES .desktop files.
  file[++NFILES]=";"FILENAME"\n"$0
  FILENAME="-in-section-END-"
}
END {
  # Is the icon work-around enabled and up-to-date?
  if(ICONUPDATED = is_icon_workaround_uptodate()) {
    # Speed up icon_workaround() by reading the icon index file.
    read_icon_index() # creates ICONINDEX map
  }
  # Decode .desktop files.
  if("'${DEBUG}'") print "awk decoding",NFILES,"files..." > "/dev/stderr"
  for(i=1; i<=NFILES; i++) {
    fil=file[i]
    name=exec=icnpath=icnname=icnext=comment=category=""
    match(fil, /^;([^\n]+)/, m); filename=m[1]
    match(fil, /\nName=([^\n]+)/, m); name=m[1]
    if(match(fil, /\nName\[('"${LR:-@}|${LL:-@}"')\]=([^\n]+)/, m)) name=m[2]
    if(!name) continue # trap bogus .desktop files
    match(fil, /\nExec=([^\n]+)/, m); exec=m[1]
    if(!exec) continue # trap bogus .desktop files
    match(fil, /\nIcon=([^\n]*\/)?([^\n.]+)([.][^\n]*)?/, m)
    icnpath=m[1]; icnname=m[2]; icnext=substr(m[3],2)
    match(fil, /\nComment=([^\n]+)/, m); comment=m[1]
    if(match(fil, /\nComment\[('"${LR:-@}|${LL:-@}"')\]=([^\n]+)/, m)) comment=m[2]
    match(fil, /\nCategories=([^\n]+)/, m); category=";"m[1]
    # narrow matches by GREP pattern and store for sorting step
    if(GREP && index(tolower(name), GREP) || !GREP) {
      key[k] = k = tolower(name) # case-indepedent sort
      if(ALL_ICONS == "true") icon_workaround(sh)
      out[k] = format_item()
    }
  }
  # Sort by name and print.
  nkey = asort(key) # Note: asort is a GNU awk (gawk) extension.
  for(i=1; i<=nkey; i++) {
    print substr(out[key[i]], 1, 510)
    # 510 works around gtkdialog tree widget buffer overflow limit
  }
  # Remember if we performed a complete icon work-around.
  mark_icon_workaround_uptodate()
  # Close shell.
  print "exit" | sh
  close(sh)
  if("'${DEBUG}'") print "ENDING AWK" >"/dev/stderr"
}

# Format tree widget item - columns: icon, name, all-packed-values.
# Note: assert data does not include characters "|" and $SEP.
function format_item(   ic,cols) {
  ic = format_icon_cell()
  cols = sprintf("%s||%s|%s", \
    ic, name, \
    sprintf("%s'${SEP}'%s'${SEP}'%s'${SEP}'%s'${SEP}'%s", \
    filename,name,exec,comment,category))
    # tree widget exports all packed values as a single column
  return(cols)
}

# Format tree row icon cell.
# Use within <input icon-column="0"> or <stock-column="0">
# Note: tree widget does not support icons with paths anyway.
function format_icon_cell( ) {
  return(icnpath ? icnpath icnname "." icnext : icnname)
}

# Workaround for gtkdialog tree widget not displaying icons with path.
function icon_workaround(sh,    a,c,IFP,ifp,x,lnk,ext) {
  # Supposedly, IFP is the icon full path (it is used as the icon index key).
  IFP = icnpath icnname (icnext ?".":"") icnext
  if("" == IFP) return
  # If the work-around is not up-to-date run sh/find/ln to create icon links.
  if(!ICONUPDATED) {
    #printf "L" > "/dev/stderr"
    ifp = IFP
    c = ""
    if(-1 == getline < ifp) { # file ifp does not exist
      # This could happen because .desktop file sets Icon=name-only (valid), but
      # we cannot trust gtkdialog to show icons by name without extension, so
      # get the full pathname; ref. http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#icon_lookup
      c = "2>/dev/null find -L '${ICON_DIRS}' " \
        " -type f \\( -name \""icnname".png\" -o -name \""icnname".svg\" -o -name \""icnname".xpm\" \\) -print0 -quit"
        # ! -path "*/.*/*" excludes all matches within any hidden dir, neat!
        # -print0 ensures match string does not include \n character
        # -quit exits after first match, if any; GNU awk (gawk) extension.
      # Read found path into ifp, which stays == "" if nothing found.
      ifp=""; c | getline ifp; close(c)
      # Note that ifp is null-terminated. See \x00 below.
    } else { # file ifp exists
      close(ifp)
    }
    if("" != ifp) {
      sub(/\x00/,"",ifp)
    } else {
      print filename": Icon=" icnpath icnname "." icnext, "Not found." >"/dev/stderr"
      return
    }
    # gtkdialog tree can definitely display icon paths /usr/share/pixmaps/*
    # so try symlinking to target ifp
    x = split(ifp, a, /\//)
    lnk = a[x] # link name lnk <- icon-name[.ext] (no path)
    ext = (x = split(lnk, a, /\./)) ?"."a[x] :"" # ".ext" if any
    if(ext) x-- # forget ext from split link name
    # implode link name while replacing all dots with underscores
    lnk = ""; for(c=1; c<=x; c++) lnk = lnk "_" a[c]; lnk = substr(lnk, 2)
    # Symlink the icon.
    print "2>/dev/null ln -s \""ifp"\" \"" ICONSTEM lnk ext "\"" | sh
    # Progressively build the icon index file.
    printf "%s\x00%s\x00%s\x00%s\n", IFP, ifp, lnk, ext >> (ICONSTEM".index")
  } else {
    #printf "I" > "/dev/stderr"
    # The work-around is up-to-date so use ICONINDEX map to speed things up
    split(ICONINDEX[IFP], a, "\x00")
    lnk = a[3]
  }

  icnpath = "" # this tells format_item() to use icnname only
  icnname = "findnrun-" lnk # no ext
  # gtkdialog is very finicky: icon name must not include dots nor extension
  # but the link name must include an extension!
}

# Is the icon work-around enabled and up-to-date? - icon_workaround() helper.
function is_icon_workaround_uptodate(   uptodate,x) {
  if(uptodate = ("true" == ALL_ICONS)) {
    if(uptodate = (-1 != (getline < (x = ICONSTEM".uptodate"))))
      close(x)
  }
  if("'${DEBUG}'") print "is_icon_workaround_uptodate="uptodate > "/dev/stderr"
  return(uptodate)
}

# Read the index file - icon_workaround() helper
function read_icon_index(   a,b,data,i,x) {
  if("'${DEBUG}'") print "in read_icon_index" >"/dev/stderr"
  if(1 == (getline data < (x = ICONSTEM".index"))) {
    close(x)
    na = split(data, a, /\n/) # we slurped the index file
    for(i=1; i<=na; i++) {
      split(a[i], b, "\x00") # index record fields: IFP ifp lnk ext
      ICONINDEX[b[1]] = a[i]
    }
  }
}

# If the icon work-around is enabled, mark whether it is up-to-date.
function mark_icon_workaround_uptodate( ) {
  if("true" == ALL_ICONS) {
    print "" > (ICONSTEM".uptodate")
    if("'${DEBUG}'") print "\nmark_icon_workaround created", ICONSTEM".uptodate" > "/dev/stderr"
  }
}
'

# Prepare the database query gawk script. [[[1
# Usage: gawk -f "scriptpath" [-v GREP="string"] ${DATF}
unset case name commleft left categ REDIRECT2
[ true = "${CASEDEPENDENT}" ] || case=tolower
if [ true = "${SEARCHREGEX}" ]; then
  REDIRECT2=" 2>/dev/null" # Quiet awk's "fatal: invalid regex" message
  name="match($case(a[2]),GREP)"
  [ true = "${SEARCHFROMLEFT}" ] && left="&& RSTART==1"
  [ true = "${SEARCHCOMMENTS}" ] && commleft="|| match($case(a[4]),GREP)${left}"
  [ true = "${SEARCHCATEGORIES}" ] && categ="|| match($case(a[5]),GREP)"
  :
else
  name="index($case(a[2]),GREP)"
  [ true = "${SEARCHFROMLEFT}" ] && left="==1"
  [ true = "${SEARCHCOMMENTS}" ] && commleft="|| index($case(a[4]),GREP)${left}"
  [ true = "${SEARCHCATEGORIES}" ] && categ="|| index($case(a[5]),GREP)"
fi
> "${AWKQ}" echo '#!/usr/bin/gawk -f
BEGIN {
  if("'${DEBUG}'">2) {
    print "\nSTARTING AWK querier, GREP="GREP >"/dev/stderr"
    print "expression=('"${name}${left} ${commleft} ${categ}"')" >"/dev/stderr"
  }
  FS="|"
}
{
  # split out the last field of the record that format_item() printed ($AWKF)
  split($(NF), a, /'"${SEP}"'/) # a <- {filename,name,exec,comment,category}
  # narrow matches by GREP pattern
  if(GREP && ('"${name}${left} ${commleft} ${categ}"') || !GREP) {
    print substr($0, 1, 510)
    # 510 works around gtkdialog tree widget buffer overflow limit
  }
}
END {
  if("'${DEBUG}'">2) print "ENDING AWK" >"/dev/stderr"
} '

# Build database of .desktop file entries. [[[1
gawk -v ALL_ICONS=${varICONS} -f "${AWKF}" ${LOCS} > "${DATF}"
[ "${DEBUG}" ] && { >/tmp/varSEARCH; echo >&2 accepting input from /tmp/varSEARCH; }
NDATF=$(wc -l "${DATF}"); NDATF=${NDATF%% *}

# Prepare Help system and its GUI button (About). [[[1
# i18n About dialog.
find_help() #[[[
{
  # In:  ${HELPFILE} paths.
  # Out: ${helpviewer} command, ${helpbutton} gtk code.
  local IFS=:
  set ${HELPFILE}
  for i; do
    unset x p s helpviewer helpbutton; hf="$i"
    case "${hf}" in *\[*\]*) p="${hf%[*}["; s="]${hf##*]}" # search for translation
      [ -e "$p${LL}$s" ] && hf="$p${LL}$s"; [ -e "$p${LR}$s" ] && hf="$p${LR}$s"; [ -e "$p${LANG}$s" ] && hf="$p${LANG}$s" ;;
    esac
    if [ -e "${hf}" ]; then
      hd="${TMPF}/help" && mkdir -p "${hd}" &&
      case "${hf##*.}" in t[gx]z|[gx]z) tar -C "${hd}" -xaf "${hf}" && hf=$(set +f; echo "${hd}"/index.*) ;; esac &&
      case "${hf##*.}" in md) x=mdview ;; htm*) x=defaultbrowser;; esac
      read -t 1 helpviewer << EOF
$(which $x defaulttexteditor geany leafpad 2>&-)
EOF
      case ${helpviewer} in
        '') helpviewer="${BROWSER:-xdg-open} '${hf}'";; # catchall
        *mdview) helpviewer="'${helpviewer}' '${hf%/*}' '${hf##*/}' '${hf##*/}' \"${APP_TITLE}\"" ;;
        *defaultbrowser) helpviewer="'${helpviewer}' 'file://${hf}'" ;;
        *) # dup help files to protect sources from text editors
          if ! [ "${hf%/*}" = "${hd}" ]; then cp -fr "${hf%/*}/"* "${hd}/"; fi &&
          helpviewer="cd '${hd}' && '${helpviewer}' 'no-help.md'" ;;#${hf##*.}'";;
      esac &&
      helpbutton="
<button use-underline=\"true\">
  <label>$(gettext "_Help")</label>
  <input file stock=\"gtk-help\"></input>
  <action>${helpviewer} &</action>
  <action>closewindow:GUI_ABOUT</action>
</button>"
    break # on first valid help file path $hf found
  fi
  done
}
#]]]
find_help

# i18n About dialog widgets: window text; number of apps text (singular/plural)
export GUI_ABOUT='
<window title="'"${APP_TITLE}"'" icon-name="edit-find" window-position="2">
  <vbox>
    <frame>
      <text justify="0" selectable="true" can-focus="false">
        <label>"'"$(printf "$(gettext "%s %s
authors: SFR, L18L, step
Open source - GNU GPL license applies

%s
%s

configuration: %s
")" "${APP_NAME}" "${Version}" \
"http://www.murga-linux.com/puppy/viewtopic.php?t=98330" \
"https://github.com/step-/find-n-run" "${CONFIG}")
$(printf "$(ngettext "%s application found" "%s applications found" "${NDATF}")" "${NDATF}"
)"'"</label>
      </text>
    </frame>
    <hbox homogeneous="true">
      <text space-fill="true" space-expand="true"><label>""</label></text>
      <button use-underline="true">
        <label>'"$(gettext "_OK")"'</label>
        <input file stock="gtk-ok"></input>
        <action>closewindow:GUI_ABOUT</action>
      </button>
      '"${helpbutton}"'
      <text space-fill="true" space-expand="true"><label>""</label></text>
    </hbox>
  </vbox>
  <variable>GUI_ABOUT</variable>
  <action signal="key-press-event" condition="command_is_true([ $KEY_SYM = Escape ] && echo true )">closewindow:GUI_ABOUT</action>
</window>'

# Prepare and show the main window. [[[1
unset showcategories
[ true = "${SEARCHCATEGORIES}" ] && showcategories=true
# i18n Main window widgets: entry tooltip; entry default; icon tooltip; list tooltip; pull-down tooltip; pull-down default; pull-down icon tooltip; entry tooltip; {checkbox tooltip; checkbox label}repeat(3); {button icon tooltip}repeat(2)
# i18n "0" (invisible, disregard).
gettext 0 >/dev/null # work around an xgettext's limitation
if [ "${ENABLESTDOUT}" ]; then
  $GTKDIALOG ${GEOMETRY:+--geometry=}${GEOMETRY} ${CMDLINEOPTS} -s
else
  $GTKDIALOG ${GEOMETRY:+--geometry=}${GEOMETRY} ${CMDLINEOPTS} -s >/dev/null
fi << EOF
<window title="${APP_TITLE}" icon-name="edit-find" window-position="2">
  <vbox>
    <hbox spacing="0">
      <entry auto-refresh="${DEBUG:+true}" tooltip-text="$(gettext "Press ENTER to select")">
        <default>$(gettext "Type some letters to refine the list")</default>
        <variable>varSEARCH</variable>
        ${DEBUG:+<input file>/tmp/varSEARCH</input>}
        <action>refresh:varLIST</action>
        <action signal="activate">grabfocus:varLIST</action>
        <action signal="activate">echo false>"${FCSF}"</action>
      </entry>
      <button tooltip-text="$(gettext "Clear entry")" stock-icon-size="1">
        <input file stock="gtk-clear"></input>
        <action>grabfocus:varSEARCH</action>
        <action>clear:varSEARCH</action>
      </button>
    </hbox>

    <tree enable-search="false" exported-column="2" column-visible="1|1|0" headers-visible="false" icon-column-name="gtk-apply" hscrollbar-policy="1" vscrollbar-policy="1" tooltip-text="$(gettext "Press ENTER or double-click to run the selected item")">
      <label>Icon|Name|PackedValues</label>
      <variable>varLIST</variable>
      <input icon-column="0">gawk -f "${AWKQ}" -v GREP="\${varSEARCH}" '${DATF}'${REDIRECT2}</input>
      <action>IFS=${SEP}; set -- \${varLIST}; set -- \${3% \%*}; echo "\$@" >>"${HSTF}"; ${UNEXPORT}; eval "\$@" &</action>
      <action condition="active_is_false(varOPEN)">exit:EXIT</action>
      <action signal="changed">refresh:varCMD</action>
      <action signal="changed">clear:varCOMMENT</action>
      <action signal="changed">refresh:varCOMMENT</action>
      ${DEBUG:+<action>echo >&2 auto-refreshing varFOCUSGRABBER</action>}
      <action>( sleep 0.1 || sleep 1; echo "\${varFOCUSSEARCH}">"${FCSF}"; ) &</action>
    </tree>

    ${REMARK# handle varLIST on(EnterEnter|double-click)}
    ${REMARK# input file auto-refresh rate cannot be configured. http://code.google.com/p/gtkdialog/source/detail?r=453}
    ${REMARK# gtkdialog compiled w/o inotify refreshes about once a second. With inotify refreshing is instantaneous.}
    <checkbox auto-refresh="true" visible="false">
      <default>false</default>
      <variable>varFOCUSGRABBER</variable>
      <input file>${FCSF}</input>
      ${DEBUG:+<action>if true echo >&2 'grabfocus:varSEARCH'</action>}
      <action>if true grabfocus:varSEARCH</action>
      <action>if true echo false>"${FCSF}"</action>
      <action>if true clear:varFOCUSGRABBER</action>
    </checkbox>

    <hbox space-fill="false" space-expand="false">
      <comboboxentry space-expand="true" space-fill="true" tooltip-text="$(gettext "Press the up-arrow key to grab the current command and move through the command history. You can modify the command. You can enter any shell command. History persists while the window is kept open. History is cleared on exit.")">
        <variable>varCMD</variable>
        <default>$(gettext "Press up-arrow key to grab command")</default>
        <input file>${HSTF}</input>
        <input>IFS=${SEP}; set -- \${varLIST}; set -- \${3% \%*}; echo "\$@"</input>
        <output file>${HSTF}</output>

        <action signal="activate" condition="command_is_true(echo \${varCMD:-true})">break:</action>
        <action signal="activate">set -- \${varCMD}; echo "\$@" >> "${HSTF}"; ${UNEXPORT}; eval "\$@" &</action>
        <action signal="activate" condition="active_is_false(varOPEN)">exit:EXIT</action>
        <action signal="activate" condition="command_is_true(echo \${varFOCUSSEARCH})">grabfocus:varSEARCH</action>
        <action signal="activate" condition="command_is_false(echo \${varFOCUSSEARCH})">grabfocus:varLIST</action>
        <action signal="activate">refresh:varCMD</action>
      </comboboxentry>
      <button tooltip-text="$(gettext "Remove entry from command history")" stock-icon-size="1">
        <input file stock="gtk-remove"></input>
        <action>grabfocus:varCMD</action>
        <action>removeselected:varCMD</action>
        <action>save:varCMD</action>
        <action>refresh:varCMD</action>
      </button>
    </hbox>

    <entry sensitive="false" tooltip-text="$(gettext "Comment about current item")">
      <variable>varCOMMENT</variable>
      <input>IFS=${SEP}; set -- \${varLIST}; echo "\$4${showcategories:+ \$5}"</input>
    </entry>

    <hbox space-fill="false" space-expand="false">
      <checkbox use-underline="true" tooltip-text="$(gettext "Keep this window open after starting an item instead of closing this window each time a command starts. Keep the window open to use the command history feature, or to avoid startup delays.")">
        <label>$(gettext "_Keep window")</label>
        <default>${defOPEN}</default>
        <variable>varOPEN</variable>
        <action>awk -v s=defOPEN=\${varOPEN} '/^defOPEN=/{\$0=s;f=1}{a[++n]=\$0}END{if(!f)a[++n]=s;++n;for(i=1;i!=n;i++)print a[i]>ARGV[1]}' '${CONFIG}'</action>
      </checkbox>
      <checkbox use-underline="true" tooltip-text="$(gettext "Display all available icons instead of displaying just the icons that do not need to be cached. Caching all icons may take some time. Disabling this option clears the existing cache.")">
        <label>$(gettext "_Show all icons")</label>
        <default>${varICONS}</default>
        <variable>varICONS</variable>
        <action>awk -v s=varICONS=\${varICONS} '/^varICONS=/{\$0=s;f=1}{a[++n]=\$0}END{if(!f)a[++n]=s;++n;for(i=1;i!=n;i++)print a[i]>ARGV[1]}' '${CONFIG}'</action>
        <action>clear:varSEARCH</action>
        ${DEBUG:+<action>ls /usr/share/pixmaps/findnrun-.uptodate ~/.icons/findnrun-.uptodate >&2; cat ~/.findnrunrc >&2</action>}
        <action condition="command_is_true([ -f '${ICONSTEM}.uptodate' -a true = \${varICONS} ] && echo true)">break:</action>
        ${DEBUG:+<action>if true echo >&2 in true rebuilding database...</action>}
        <action>if true gawk -v ALL_ICONS=\${varICONS} -f '${AWKF}' ${LOCS} > '${DATF}'</action>
        ${DEBUG:+<action>if true echo >&2 in true clear:varLIST</action>}
        <action>if true clear:varLIST</action>
        ${DEBUG:+<action>if true echo >&2 in true refresh:varLIST</action>}
        <action>if true refresh:varLIST</action>
        ${DEBUG:+<action>if false echo >&2 in false rm -f \{~/.icons\|/usr/share/pixmaps\}/findnrun-\*</action>}
        <action>if false rm -f '${ICONSTEM:-/tmp/dummy}'*</action>
        ${DEBUG:+<action>if false echo >&2 in false rebuilding database...</action>}
        <action>if false gawk -v ALL_ICONS=\${varICONS} -f '${AWKF}' ${LOCS} > '${DATF}'</action>
        ${DEBUG:+<action>if false echo >&2 in false clear:varLIST</action>}
        <action>if false clear:varLIST</action>
        ${DEBUG:+<action>if false echo >&2 in false refresh:varLIST</action>}
        <action>if false refresh:varLIST</action>
      </checkbox>
      <checkbox use-underline="true" tooltip-text="$(gettext "Move the keyboard focus to the search input field after starting an item instead of keeping the keyboard focus on the started list item. This option also affects the command input field.")">
        <label>$(gettext "_Focus search")</label>
        <default>${varFOCUSSEARCH}</default>
        <variable>varFOCUSSEARCH</variable>
        <action>awk -v s=varFOCUSSEARCH=\${varFOCUSSEARCH} '/^varFOCUSSEARCH=/{\$0=s;f=1}{a[++n]=\$0}END{if(!f)a[++n]=s;++n;for(i=1;i!=n;i++)print a[i]>ARGV[1]}' '${CONFIG}'</action>
      </checkbox>
      <text space-fill="true" space-expand="true"><label>""</label></text>
      <button tooltip-text="$(gettext "About and help")" stock-icon-size="1">
        <input file stock="gtk-about"></input>
        <action>launch:GUI_ABOUT</action>
      </button>
      <button tooltip-text="$(gettext "Exit")" stock-icon-size="1">
        <input file stock="gtk-quit"></input>
        <action>exit:EXIT</action>
      </button>
    </hbox>
  </vbox>
  <action signal="key-press-event" condition="command_is_true([ \$KEY_SYM = Escape ] && echo true )">exit:EXIT</action>
  <action signal="key-press-event" condition="command_is_true([ \$KEY_SYM = F1 ] && echo true )">${helpviewer:-:} &</action>
  <action signal="delete-event">exit:abort</action>
</window>
EOF

###############################################################################

# i18n Optionally translate the Name[xx] field to be added to file findnrun.desktop
# i18n Do NOT include ending ' ' in your translation, assume that it isn't there.
Name=$(gettext "Find'N'Run ")
# i18n Optionally translate the Comment[xx] field to be added to file findnrun.desktop
Comment=$(gettext "Find and run applications very quickly")

