#!/bin/dash

# File comment file writer
# Author: step
# Copyright (c)2016-2019,2022,2026 step
# License: GNU GPLv3
Version=1.2.0

usage() {
  . gettext.sh
  ARG0="${0##*/}" COMMENT_OPTIONS='$COMMENT_OPTIONS' \
  eval_gettext 'Usage: [ENVIRONMENT] ${ARG0} [OPTIONS] FILE [FILE...]

Create/edit/delete ROX-Filer comment side-file GUI.
By default, `${ARG0} FILE` creates "FILE.txt"
and also symlinks ".FILE.comment" to "FILE.txt".
ROX-Filer reads FILE'\''s tooltip from either file.

Options:
  -h, --help      Show this message and exit
  --text-ext=EXT  Change the default side-file name extension ("txt") to EXT; note
                  --text-ext="ROX" just creates ".FILE.comment" as a regular file
  --no-dialog     Do not show a file open dialog
  --no-symlink    Do not create the ".FILE.comment" symlink
  --no-xattr      Do not append extended attributes
  --rm            Remove FILE'\''s comment file
  --rm-dry-run    Print the list of files that would be removed
  --rm-no-prompt  Do not prompt to confirm the removal
  --version       Print version string and exit
  --              Stop processing options

To disable a preceding --no-* option (including those from $COMMENT_OPTIONS),
use --* instead.  For example, --symlink disables --no-symlink.

*ENVIRONMENT*

  COMMENT_EDITOR="defaulttexteditor"  Change the default graphical text editor

  COMMENT_OPTIONS=""                  Default options; override on command line
'
}

### i18n
export TEXTDOMAIN=fatdog
export OUTPUT_CHARSET=UTF-8

# --- Translation table i18n ----------------------------------------------
# i18n_table.sh
# (C)2016-2025, step - https://github.com/step-/i18n-table
# Version: 2025.09.06 for POSIX shell.

# Notes for translators
# ---------------------
# A. Never use \n **inside** your MSGSTR. Swap \r for \n in yad/gtkdialog text.
# B. However, always **end** your MSGSTR with \n.
# C. Replace trailing spaces (U+0020) with no-break spaces (U+00A0).
#
# To create a pot file for this script use xgettext.sh[1] instead of xgettext.
# xgettext.sh augments xgettext with the ability to extract MSGIDs from calls
# to 'gettext -es'.
# [1] https://github.com/step-/i18n-table

i18n_table() {
# Cf. xgettext.sh usage:
# [c1] comment lines found inside this function are reproduced with prefix "#."
# [c2] variable names are reproduced with prefix "#." above their MSGIDs.
  {
read i18n_app_title
read i18n_app_title_create
read i18n_app_title_help
read i18n_app_title_remove
read i18n_err_invalid_option
# i18n_dlg_fopen_caption_fmt: "{Name}" adopts the GTK File Dialog widget's "Name" label.
read i18n_dlg_fopen_caption_fmt
read i18n_col_remove
read i18n_col_file
read i18n_win_help_size
read i18n_win_fopen_size
read i18n_win_rm_size
  } << EOF
$(gettext -es -- \
"Comment File\n" \
"Create\n" \
"Help\n" \
"Remove\n" \
"error: invalid option:\n" \
"Symbolic link creation is enabled, so a .<u>Name</u>.comment → <u>Name</u>.txt link will be created alongside the <u>Name</u>.txt file. Click Help for instructions on disabling symbolic links.\n" \
"Remove\n" \
"File\n" \
"s-write-comment:help: 700 600\n" \
"s-write-comment:fopen: 400 300\n" \
"s-write-comment:rm: 400 200\n" \
)
EOF
}

i18n_table

### Constants
COMMENT_EDITOR=${COMMENT_EDITOR:-defaulttexteditor}
# apps/128x128/posted.png
YAD_OPTIONS="--window-icon=posted --center --borders=10 --buttons-layout=spread --selectable-labels"
export YAD_OPTIONS
YAD=${YAD_BIN:-yad} # yad_gtk[23] ready; not yadu-ready due to no importing YAD_STOCK_BTN from yad-lib.sh

# Dialog size and HiDPI screen scaling
win_h_help=${i18n_win_help_size##* }
win_w_help=${i18n_win_help_size#* }
win_w_help=${win_w_help% *}
win_h_fopen=${i18n_win_fopen_size##* }
win_w_fopen=${i18n_win_fopen_size#* }
win_w_fopen=${win_w_fopen% *}
win_h_rm=${i18n_win_rm_size##* }
win_w_rm=${i18n_win_rm_size#* }
win_w_rm=${win_w_rm% *}

adapt_dims_to_hidpi_screen() {
  local YAD_VERSION="$($YAD --version)"
# IF GTK2 || GTK3 && GDK_SCALE < 2 or unset
if [ "${YAD_VERSION#*GTK+ 2}" != "$YAD_VERSION" -o "${GDK_SCALE:-1}" = 1 ]; then
  type fatdog-hidpi-lib > /dev/null || return 0
  . fatdog-hidpi-lib || return 1
  set -- $(hidpi_apply_scale $win_w_help $win_h_help $win_w_fopen $win_h_fopen $win_w_rm $win_h_rm)
  win_w_help=$1 win_h_help=$2 win_w_fopen=$3 win_h_fopen=$4 win_w_rm=$5 win_h_rm=$6
fi
}

adapt_dims_to_hidpi_screen

set_OPTIONS() { # $@-args | => $OPTIONS, $SHIFT, $opt_*
  unset OPTIONS
  SHIFT=$#
  unset opt_no_dialog opt_no_symlink opt_no_xattr opt_text_ext opt_rm
  while [ "${1#-}" != "$1" ]; do
    OPTIONS="$OPTIONS $1"
    case $1 in
      --) shift; break ;;
      --help|-h) usage; exit ;;
      --help=all-gui) usage --help-all | env YAD_OPTIONS="$YAD_OPTIONS
          --width=$win_w_help --height=$win_h_help
          --title \"$i18n_app_title - $i18n_app_title_help\"
          --fontname=Monospace
          " $YAD --text-info &
          exit 0 ;;
      --no-dialog) opt_no_dialog=1; shift ;;
         --dialog) unset opt_no_dialog; shift ;;
      --no-symlink) opt_no_symlink=1; shift ;;
         --symlink) unset opt_no_symlink; shift ;;
      --no-xattr) opt_no_xattr=1; shift ;;
         --xattr) unset opt_no_xattr; shift ;;
      --rm) opt_rm=prompt; shift ;;
      --rm-dry-run) opt_rm=dry-run; shift ;;
      --rm-no-prompt) opt_rm=no-prompt; shift ;;
      --text-ext=*) opt_text_ext="${1#*=}"; shift ;;
      --version) echo $Version; exit ;;
      -*) usage
          printf "\n%s: %s %s\n" "$0" "$i18n_err_invalid_option" "$1" >&2
          exit 1
          ;;
      *) break ;;
    esac
  done
  SHIFT=$(($SHIFT -$#))
}

dialog_fopen() { # $1-comment-filepathname | <= $opt_no_symlink
  local p="$1" caption
  [ -z "$opt_no_symlink" ] && caption="$i18n_dlg_fopen_caption_fmt\n"
  env YAD_OPTIONS="$YAD_OPTIONS
    --filename=\"$p\"
    --title=\"$i18n_app_title - $i18n_app_title_create\"
    --width=$win_w_fopen --height=$win_h_fopen
    --button=gtk-ok:0 --button=gtk-cancel:1
    --button=\"gtk-help:$0 --help=all-gui\"
    " $YAD --file --save ${caption:+"--text=$caption"}
  local exit_val=$? # 0|1|252
  [ 0 != $exit_val ] && echo "$p"
  echo $exit_val
  return $exit_val
}

### Parse options.
[ "$COMMENT_OPTIONS" ] && set_OPTIONS $COMMENT_OPTIONS
shift0=${SHIFT:-0}
set_OPTIONS $OPTIONS "$@"
shift $(($SHIFT -$shift0))
if [ 0 = $# ]; then usage >&2; exit 1; fi

### Default values.
opt_text_ext="${opt_text_ext:-txt}" # FILE.txt
opt_text_ext="${opt_text_ext#.}"

# Set traps and create temporary folder.
TMPD=$(mktemp -d -p "${TMPDIR:-/tmp}" "${0##*/}_XXXXXX")
chmod 700 "$TMPD" || exit 1
trap "rm -rf \"$TMPD\"/* \"$TMPD\"" HUP INT QUIT TERM ABRT 0
DATF="${TMPD}/.dat"; >"${DATF}" # list original and comment files
CFTF="${TMPD}/.cft"; >"${CFTF}" # list comment files

if [ -z "$opt_rm" ]; then
  ## Write comment file(s).

  # Press [Cancel] to exit the script.
  # Press [Esc] to accept the current default file name and go to the next FILE.
  for FILE; do
    ## Prompt to select comment file path/name.

    # Make full pathname, which saves some mouse clicks in yad.
    case $FILE in /*) f=$FILE ;; *) f="`pwd`/$FILE" ;; esac
    cf="$f.$opt_text_ext" exit_val=0
    if [ ROX = $opt_text_ext ]; then
      cf="${f%/*}/.${f##*/}.comment" opt_no_dialog=1
    fi
    # Allow renaming text comment file.
    if [ -z "$opt_no_dialog" ]; then
      { read cf; read exit_val; } << EOF
$(dialog_fopen "$cf")
EOF
    fi
    : "cf($cf)"
    : "exit_val($exit_val)"
    case $exit_val in
      1 ) # 1=Cancel
        exit
        ;;
      0) # 0=OK && 252=ESC->0 (--escape-ok)
        # Queue FILE and comment file full pathnames.
        printf "%s\n%s\n" "$f" "$cf" >> "$DATF"
        ;;
    esac
  done

  xargs -0 -L 1 < "$DATF"
  while read f; do
    read cf &&
    ## Create/edit comment file.

    # ROX-Filer reads the tooltip from ".FILE.comment" (rox jun7-2016.12 fd710)
    # and also from "FILE.txt" (rox jun7-2017.11 fd720). The ".FILE.comment"
    # symlink is colocated with "FILE" while "FILE.EXT" may not be.

    # Link ROX-Filer comment file to txt file.
    if [ -z "$opt_no_symlink" ] && [ ROX != $opt_text_ext ]; then
      # Relative link iff comment file co-resides with FILE.
      [ "${cf%/*}" = "${f%/*}" ] && tf=${cf##*/} || tf=$cf
      ln -sfT "$tf" "${f%/*}/.${f##*/}.comment"
    fi

    # APPEND optional content items.

    # Dump extended attributes.
    if [ -z "$opt_no_xattr" -a -e "$f" -a ! -e "$cf" ]
    then
      echo '```xattr' >> "$cf"

      # -e text avoids mixing text-encoded with hex- / base64-encoded fields for 2-byte chars cf. man getfattr -m
      getfattr -d -e text "$f" >> "$cf"
      # more complicated alternative using attr
      #attr -ql "$f" | xargs --no-run-if-empty -I{} attr -g {} "$f" >> "$cf"

      echo '```' >> "$cf"
    fi

    # Queue2 comment file fullpath.
    printf "%s\000" "$cf" >> "$CFTF"
  done  < "$DATF"

  ## Edit comment text in default/preferred program.

  # All files in a single editor call. For multiple calls with a single
  # file add -L 1 to xargs.
  # Don't quote $COMMENT_EDITOR so the user can specify options.
  xargs --no-run-if-empty -0 $COMMENT_EDITOR < "$CFTF"

else
  ## Remove comment file(s).

  # Press [OK] to remove comment and link (if any) files.
  # [Cancel] and [Esc] don't remove.
  for FILE; do
    ## Determine rm target(s) cf and lf.
    case $FILE in /*) f=$FILE ;; *) f="`pwd`/$FILE" ;; esac
    cf= lf="${f%/*}/.${f##*/}.comment" # regular file or link
    if [ -L "$lf" ]; then
      cf="$(readlink -f "$lf")"
    elif [ -f "$lf" ]; then
      cf="$lf" lf=
    elif [ -e "$f.$opt_text_ext" ]; then
      cf="$f.$opt_text_ext" lf=
    fi

    ## Queue link file and comment file full pathnames.
    # -L -o -f ensures detection of dangling links.
    [ -L "$lf" -o -f "$lf" ] && printf "%s\000" "$lf" >> "$DATF"
    [ -e "$cf" ] && printf "%s\000" "$cf" >> "$DATF"
  done

  ## Remove files.
  case $opt_rm in
    dry-run )
      tr '\0' '\n' < "$DATF"
      ;;
    prompt )
      awk 'BEGIN{RS="\x00"} {print "true\n"$0}' "$DATF" |
      env YAD_OPTIONS="$YAD_OPTIONS
        --title=\"$i18n_app_title - $i18n_app_title_remove\"
        --width=$win_w_rm --height=$win_h_rm
        --column=\"$i18n_col_remove\":CHK
        --column=\"$i18n_col_file\" --tooltip-column=2
        --print-all --ellipsize=START
        " $YAD_BIN --list |
        awk 'BEGIN{ORS="\x00"} /^TRUE/{print substr($0, 6, length($0)-6)}' |
        xargs --no-run-if-empty -0 rm
      ;;
    no-prompt )
      xargs --no-run-if-empty -0 --arg-file "$DATF" rm
      ;;
  esac

fi
