#!/bin/ash
# Fatdog64 Startup Manager
# Copyright (C) James Budiono 2015
#
# License: GNU GPL Version 3 or later
#
# Note: the bits in Global Inits, which is part of GTK-server bash init code
#       is (C) Peter van Eerten. Everything else is (C) James Budiono
#
#
# L18L internationalisation

# 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 'Fatdog64 Startup Manager')"
RESERVED=":README.md:"
ALL_STARTUP_DIR=/etc/xdg/Startup
MY_STARTUP_DIR=$HOME/Startup

### runtime variables
GDK_TYPE_PIXBUF=
MAIN_WINDOW=
SELECTED_ITER=

# servicestore model
T_NAME=0 T_FILE=1 T_ENABLED=2 

#---------------------------------------------------------- Global Inits
### global variables for proper GTK-server
GTK= NULL="NULL"
PIPE=/tmp/gtk.bash.$$
trap 'stop-gtk-server -fifo $PIPE; exit' HUP TERM INT # stop GTK-server in case of an error

### start GTK-server
gtk-server -fifo=$PIPE &
while [ ! -p $PIPE ]; do sleep 1; continue; done

### GTK-server helper functions
# Assignment function
define() { $2 $3 $4 $5 $6 $7 $8 $9; eval $1=\'"$GTK"\'; }

# Communicate with GTK-server
gtk()
{
/bin/echo "$@" > $PIPE
read GTK < $PIPE
}

#---------------------------------------------------------- GUI realization

# add GTK+ API not defined in gtk-server.cfg
add_missing_gtk_api() {
	gtk gtk_server_define gdk_pixbuf_get_type NONE INT 0
	gtk gtk_server_define gtk_label_set_markup NONE NONE 2 WIDGET STRING
	
	# add GTK+ API not defined in gtk-server.cfg
	gtk gtk_server_define gtk_builder_new NONE WIDGET 0
	gtk gtk_server_define gtk_builder_add_from_file NONE INT 3 WIDGET STRING NULL
	gtk gtk_server_define gtk_builder_add_from_string NONE INT 4 WIDGET STRING INT NULL
	gtk gtk_server_define gtk_builder_get_object NONE WIDGET 2 WIDGET STRING
	gtk gtk_server_define gtk_builder_connect_signals NONE NONE 2 WIDGET NULL	
	
	gtk gtk_server_define gtk_tree_view_get_cursor NONE NONE 3 WIDGET PTR_WIDGET NULL
	
	# hack for getting unlimited string length and leak-free operation with gtk_tree_get_model
	# see below
	gtk gtk_server_define strcat NONE STRING 2 POINTER STRING
	gtk gtk_server_define memcpy NONE NONE 3 PTR_LONG POINTER INT			
}

### helper - leak-free, unlimited string length from gtk_tree_model_get with unpatched gtk-server
# $1-tree $2-iter $3-index $4-result name
# static variable to speed up stuff
STATIC_BUF=
STATIC_ADDRESS=
tree_model_get_string() {
	[ -z "$STATIC_BUF" ] && define STATIC_BUF gtk malloc 32 # gtk-server MAX_DIG is 32 bytes
	
	gtk gtk_server_define gtk_tree_model_get NONE NONE 5 WIDGET WIDGET INT POINTER INT # ensure correct definition
	gtk gtk_tree_model_get $1 $2 $3 $STATIC_BUF -1
	
	gtk memcpy NULL $STATIC_BUF 32	# print address stored in $BUF --> *buf
	STATIC_ADDRESS=$GTK
	if [ $STATIC_ADDRESS -ne 0 ]; then
		gtk strcat $STATIC_ADDRESS \"\" # get string point by address --> **buf	
		eval $4=\'"$GTK"\'				# this will be our return value	
		gtk free $STATIC_ADDRESS		# this was dynamically alloc-ed by gtk_tree_model_get, now free it
	else
		eval $4=\"\"
	fi
}

### helper - display progress
# $1-text
display_progress() {
	gtk gtk_label_set_markup $PROGRESS_LABEL \""$1"\"
	gtk gtk_widget_show_all $PROGRESS_WINDOW
	flush_gtk
}
hide_progress() {
	gtk gtk_widget_hide $PROGRESS_WINDOW
	flush_gtk
}
flush_gtk() {
	gtk gtk_events_pending
	while [ $GTK -eq 1 ]; do
		gtk gtk_main_iteration
		gtk gtk_events_pending
	done	
}

### check for reserved services which we don't want to show
# $1-startup filename
is_reserved() {
	local p=${1##*/}
	case "$RESERVED" in 
		*:${p}:*) return 0
	esac
	return 1
}

### helper
# $1-text $2-icon
msgbox() {
	local DLG
	define DLG gtk gtk_message_dialog_new NULL GTK_DIALOG_MODAL $2 GTK_BUTTONS_CLOSE "'$1'" "''"
	gtk gtk_dialog_run $DLG	
	gtk gtk_widget_destroy $DLG
}

### helper
root_warning() {
	if [ $(id -u) -ne 0 ]; then
		msgbox "$(gettext 'You must be root to change the setting.')" GTK_DIALOG_ERROR
		return 1
	fi
	return 0
}

### helper - reload content of view
# $1-startup dir $2-store 
load_view() {
	local ITER p sfile ENABLED
	define ITER gtk gtk_server_opaque
	
	gtk gtk_list_store_clear $2
	gtk gtk_server_define gtk_list_store_set NONE NONE 9 WIDGET WIDGET INT STRING INT STRING INT INT INT

	for p in $(ls $1/*); do
		sfile="${p#$1/}"
		is_reserved $sfile && continue 
		
		ENABLED=$(! test -x "$p"; echo $?)
		gtk gtk_list_store_append $2 $ITER NULL		
		gtk gtk_list_store_set $2 $ITER $T_NAME "$sfile" $T_FILE "$p" $T_ENABLED $ENABLED -1
	done

	gtk free $ITER
}

### helper - get the currently selected item
# returns SELECTED_STARTUP SELECTED_ITER (must be freed with 'gtk free')
# $1-view $2-store
get_selected_item() {
	local SELECTED_PATH
	define SELECTED_ITER gtk gtk_server_opaque
	
	define SELECTED_PATH gtk gtk_tree_view_get_cursor $1 NULL
	if [ $SELECTED_PATH -ne 0 ]; then
		gtk gtk_tree_model_get_iter $2 $SELECTED_ITER $SELECTED_PATH	
		gtk gtk_tree_path_free $SELECTED_PATH

		# $1-tree $2-iter $3-index $4-result name
		tree_model_get_string $2 $SELECTED_ITER $T_FILE SELECTED_STARTUP
	else
		gtk free $SELECTED_ITER
		SELECTED_ITER=""
	fi
}


### helper
# $1-path
open_rox() {
	msgbox "$(gettext 'This is not a regular file and thus cannot be modified.')
	$('You need to modify this manually.')
	$('I will open the window for you to do that after you close this note.')" GTK_MESSAGE_ERROR
	rox -d "$1"	
}

### signal handler
# $1-startup file
enable_item() {
	if [ -f "$1" ]; then
		chmod +x "$1"
	else
		open_rox "${1%/*}"
	fi
}

### signal handler
# $1-startup file
disable_item() {
	local DLG
	if [ -f "$1" ]; then
		chmod -x "$1"
	else
		open_rox "${1%/*}"
	fi	
}

### signal handler
# $1-startup item $2-store $3-iter
update_startup_status() {
	local ENABLED
	ENABLED=$(! test -x "$1"; echo $?)
	gtk gtk_server_define gtk_list_store_set NONE NONE 5 WIDGET WIDGET INT INT INT 
	gtk gtk_list_store_set $2 $3 $T_ENABLED $ENABLED -1	
}

################### main ###################
### gtk-server localisation stanza
gtk gtk_server_define setlocale NONE STRING 2 INT STRING
gtk gtk_server_define bindtextdomain NONE STRING 2 STRING STRING
gtk gtk_server_define textdomain NONE STRING 1 STRING
gtk setlocale 6 \"\" # 6 corresponds to LC_ALL
gtk bindtextdomain $TEXTDOMAIN $TEXTDOMAINDIR
gtk textdomain $TEXTDOMAIN

### init gui
add_missing_gtk_api
gtk gtk_init
define GDK_TYPE_PIXBUF gtk gdk_pixbuf_get_type

define BUILDER gtk gtk_builder_new
define RESULT gtk gtk_builder_add_from_file $BUILDER "${0}.xml"
gtk gtk_builder_connect_signals $BUILDER

define MAIN_WINDOW gtk gtk_builder_get_object $BUILDER main_window
define CLOSE_BUTTON gtk gtk_builder_get_object $BUILDER close-button

define MY_ENABLE_BUTTON gtk gtk_builder_get_object $BUILDER my-enable-button
define MY_DISABLE_BUTTON gtk gtk_builder_get_object $BUILDER my-disable-button
define MY_START_BUTTON gtk gtk_builder_get_object $BUILDER my-start-button
define MY_STARTUP_VIEW gtk gtk_builder_get_object $BUILDER mystartupview
define MY_STARTUP_STORE gtk gtk_builder_get_object $BUILDER mystartuplist

define ALL_ENABLE_BUTTON gtk gtk_builder_get_object $BUILDER all-enable-button
define ALL_DISABLE_BUTTON gtk gtk_builder_get_object $BUILDER all-disable-button
define ALL_START_BUTTON gtk gtk_builder_get_object $BUILDER all-start-button
define ALL_STARTUP_VIEW gtk gtk_builder_get_object $BUILDER allstartupview
define ALL_STARTUP_STORE gtk gtk_builder_get_object $BUILDER allstartuplist

define PROGRESS_WINDOW gtk gtk_builder_get_object $BUILDER progress_window
define PROGRESS_LABEL gtk gtk_builder_get_object $BUILDER progress-label

# connect signals
gtk gtk_server_connect $MAIN_WINDOW delete-event quit
gtk gtk_server_connect $CLOSE_BUTTON clicked quit

gtk gtk_server_connect $MY_STARTUP_VIEW cursor-changed my-cursor-changed
gtk gtk_server_connect $MY_ENABLE_BUTTON clicked my-enable-startup
gtk gtk_server_connect $MY_DISABLE_BUTTON clicked my-disable-startup
gtk gtk_server_connect $MY_START_BUTTON clicked my-start-startup

gtk gtk_server_connect $ALL_STARTUP_VIEW cursor-changed all-cursor-changed
gtk gtk_server_connect $ALL_ENABLE_BUTTON clicked all-enable-startup
gtk gtk_server_connect $ALL_DISABLE_BUTTON clicked all-disable-startup
gtk gtk_server_connect $ALL_START_BUTTON clicked all-start-startup


# preparation stuff
load_view $MY_STARTUP_DIR $MY_STARTUP_STORE
load_view $ALL_STARTUP_DIR $ALL_STARTUP_STORE

### gtk main loop
gtk gtk_widget_show_all $MAIN_WINDOW
while true; do 
	define EVENT gtk gtk_server_callback wait
	case $EVENT in
		quit)
			break ;;
								
		my-enable-startup)			
			get_selected_item $MY_STARTUP_VIEW $MY_STARTUP_STORE
			if [ "$SELECTED_ITER" ]; then
				enable_item "$SELECTED_STARTUP"
				update_startup_status $SELECTED_STARTUP $MY_STARTUP_STORE $SELECTED_ITER
				gtk free $SELECTED_ITER
				SELECTED_ITER=""
			else
				msgbox "$(gettext 'Nothing is selected.')" GTK_DIALOG_INFO				
			fi
			;;
			
		my-disable-startup)
			get_selected_item $MY_STARTUP_VIEW $MY_STARTUP_STORE			
			if [ "$SELECTED_ITER" ]; then
				disable_item "$SELECTED_STARTUP"
				update_startup_status $SELECTED_STARTUP $MY_STARTUP_STORE $SELECTED_ITER
				gtk free $SELECTED_ITER
				SELECTED_ITER=""
			else
				msgbox "$(gettext 'Nothing is selected.')" GTK_DIALOG_INFO
			fi
			;;

		my-start-startup)
			get_selected_item $MY_STARTUP_VIEW $MY_STARTUP_STORE			
			if [ "$SELECTED_ITER" ]; then
				sh $SELECTED_STARTUP &
				gtk free $SELECTED_ITER
				SELECTED_ITER=""
			else
				msgbox "$(gettext 'Nothing is selected.')" GTK_DIALOG_INFO
			fi
			;;

		all-enable-startup)
			root_warning || continue
			get_selected_item $ALL_STARTUP_VIEW $ALL_STARTUP_STORE
			if [ "$SELECTED_ITER" ]; then
				enable_item "$SELECTED_STARTUP"
				update_startup_status $SELECTED_STARTUP $ALL_STARTUP_STORE $SELECTED_ITER
				gtk free $SELECTED_ITER
				SELECTED_ITER=""
			else
				msgbox "$(gettext 'Nothing is selected.')" GTK_DIALOG_INFO
			fi
			;;
			
		all-disable-startup)
			root_warning || continue
			get_selected_item $ALL_STARTUP_VIEW $ALL_STARTUP_STORE			
			if [ "$SELECTED_ITER" ]; then
				disable_item "$SELECTED_STARTUP"
				update_startup_status $SELECTED_STARTUP $ALL_STARTUP_STORE $SELECTED_ITER
				gtk free $SELECTED_ITER
				SELECTED_ITER=""
			else
				msgbox "$(gettext 'Nothing is selected.')" GTK_DIALOG_INFO
			fi
			;;

		all-start-startup)
			get_selected_item $ALL_STARTUP_VIEW $ALL_STARTUP_STORE			
			if [ "$SELECTED_ITER" ]; then
				sh $SELECTED_STARTUP &
				gtk free $SELECTED_ITER
				SELECTED_ITER=""
			else
				msgbox "$(gettext 'Nothing is selected.')" GTK_DIALOG_INFO
			fi
			;;
				
	esac
	hide_progress
done
gtk gtk_server_exit 
