/*
** 1998-05-29 -	A command to change the access flags of a file or directory.
**		Made significantly simpler by the new cmd_generic module.
** 1999-03-06 -	Adapted for new selection/generic handling.
*/

#include "gentoo.h"
#include "errors.h"
#include "dirpane.h"

#include "cmd_generic.h"
#include "cmd_chmod.h"

#define	CMD_ID	"chmod"

/* ----------------------------------------------------------------------------------------- */

typedef struct {
	GtkWidget	*frame;
	GtkWidget	*vbox;
	GtkWidget	*check[3];
} PFrame;

typedef struct {
	GtkWidget	*vbox;
	GtkWidget	*label;
	GtkWidget	*fbox;
	PFrame		frame[4];
	GtkWidget	*bbox;
	GtkWidget	*all, *none, *toggle, *revert;
	mode_t		last_mode;
} ChmInfo;

static mode_t mask[] = {S_ISUID, S_ISGID, S_ISVTX,  S_IRUSR, S_IWUSR, S_IXUSR,
			 S_IRGRP, S_IWGRP, S_IXGRP,  S_IROTH, S_IWOTH, S_IXOTH};

/* ----------------------------------------------------------------------------------------- */

static mode_t get_checks(ChmInfo *chm)
{
	mode_t	mode = 0;
	guint	i;

	for(i = 0; i < sizeof mask / sizeof mask[0]; i++)
	{
		if(GTK_TOGGLE_BUTTON(chm->frame[i / 3].check[i % 3])->active)
			mode |= mask[i];
	}
	return mode;
}

static void set_checks(ChmInfo *chm, mode_t mode)
{
	guint	i;

	for(i = 0; i < sizeof mask / sizeof mask[0]; i++)
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chm->frame[i / 3].check[i % 3]), mode & mask[i]);
}

static void chm_body(MainInfo *min, DirPane *src, DirRow *row, GtkWindow *win, gpointer user)
{
	ChmInfo	*chm = (ChmInfo *) user;
	char	*name = DP_ROW_NAME(row);
	char	temp[FILENAME_MAX + 32];
	mode_t	mode = DP_ROW_LSTAT(row).st_mode;

	gtk_window_set_title(win, "Change mode");

	g_snprintf(temp, sizeof temp, "Set protection bits for \"%s\":", name);
	gtk_label_set_text(GTK_LABEL(chm->label), temp);
	set_checks(chm, chm->last_mode = mode);
}

/* ----------------------------------------------------------------------------------------- */

static int chm_action(MainInfo *min, DirPane *src, DirPane *dst, DirRow *row, gpointer user)
{
	ChmInfo	*chm = (ChmInfo *) user;
	mode_t	mode;

	mode = get_checks(chm);
	if(chmod(DP_ROW_NAME(row), mode))
		err_set(min, errno, CMD_ID, DP_ROW_NAME(row));
	else
		dp_unselect(src, DP_ROW_INDEX(src, row));

	return errno == 0;
}

/* ----------------------------------------------------------------------------------------- */

static gint evt_clicked(GtkWidget *wid, gpointer user)
{
	ChmInfo	*chm = (ChmInfo *) user;

	if(wid == chm->all)
		set_checks(chm, S_ISUID | S_ISGID | S_ISVTX | S_IRUSR | S_IWUSR | S_IXUSR |
				S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
	else if(wid == chm->none)
		set_checks(chm, 0);
	else if(wid == chm->toggle)
		set_checks(chm, ~get_checks(chm));
	else if(wid == chm->revert)
		set_checks(chm, chm->last_mode);

	return TRUE;
}

/* 1998-05-29 -	Build a protection frame, with three checkboxes. If type is 0, we build a
**		special one (with setuid/setgid/sticky), otherwise a standard (read/write/exec).
*/
static int build_frame(ChmInfo *ci, int pos, int type)
{
	char	*label[]  = { "Special", "Owner", "Group", "Other" };
	char	*check[] = { "Set UID", "Set GID", "Sticky", "Read", "Write", "Execute" };
	PFrame	*fr = &ci->frame[pos];
	int	i;

	if((fr->frame = gtk_frame_new(label[pos])) != NULL)
	{
		if((fr->vbox = gtk_vbox_new(FALSE, 0)) != NULL)
		{
			for(i = 0; i < 3; i++)
			{
				fr->check[i] = gtk_check_button_new_with_label(check[type * 3 + i]);
				gtk_box_pack_start(GTK_BOX(fr->vbox), fr->check[i], TRUE, TRUE, 0);
				gtk_widget_show(fr->check[i]);
			}
			gtk_widget_show(fr->vbox);
			gtk_container_add(GTK_CONTAINER(fr->frame), fr->vbox);
			gtk_widget_show(fr->frame);
			return 1;
		}
		gtk_widget_destroy(fr->frame);
	}
	return 0;
}

int cmd_chmod(MainInfo *min, DirPane *src, DirPane *dst, CmdArg *ca)
{
	static ChmInfo	ci;
	guint		i, got;

	if((ci.vbox = gtk_vbox_new(FALSE, 5)) != NULL)
	{
		if((ci.label = gtk_label_new("Protection bits")) != NULL)
		{
			if((ci.fbox = gtk_hbox_new(FALSE, 0)) != NULL)
			{
				for(i = got = 0; i < sizeof ci.frame / sizeof ci.frame[0]; i++)
				{
					if(build_frame(&ci, i, (i == 0) ? 0 : 1))
					{
						gtk_box_pack_start(GTK_BOX(ci.fbox), ci.frame[i].frame, TRUE, TRUE, 5);
						got++;
					}
				}
				if(got == sizeof ci.frame / sizeof ci.frame[0])
				{
					gtk_box_pack_start(GTK_BOX(ci.vbox), ci.label, FALSE, FALSE, 0);
					gtk_box_pack_start(GTK_BOX(ci.vbox), ci.fbox,  TRUE,  TRUE, 0);
					gtk_widget_show(ci.label);
					gtk_widget_show(ci.fbox);
					if((ci.bbox = gtk_hbox_new(FALSE, 0)) != NULL)
					{
						ci.all	  = gtk_button_new_with_label("All");
						ci.none	  = gtk_button_new_with_label("None");
						ci.toggle = gtk_button_new_with_label("Toggle");
						ci.revert = gtk_button_new_with_label("Revert");
						if(ci.all && ci.none && ci.toggle && ci.revert)
						{
							gtk_signal_connect(GTK_OBJECT(ci.all), "clicked", GTK_SIGNAL_FUNC(evt_clicked), &ci);
							gtk_signal_connect(GTK_OBJECT(ci.none), "clicked", GTK_SIGNAL_FUNC(evt_clicked), &ci);
							gtk_signal_connect(GTK_OBJECT(ci.toggle), "clicked", GTK_SIGNAL_FUNC(evt_clicked), &ci);
							gtk_signal_connect(GTK_OBJECT(ci.revert), "clicked", GTK_SIGNAL_FUNC(evt_clicked), &ci);
							gtk_box_pack_start(GTK_BOX(ci.bbox), ci.all, TRUE, TRUE, 5);
							gtk_box_pack_start(GTK_BOX(ci.bbox), ci.none, TRUE, TRUE, 5);
							gtk_box_pack_start(GTK_BOX(ci.bbox), ci.toggle, TRUE, TRUE, 5);
							gtk_box_pack_start(GTK_BOX(ci.bbox), ci.revert, TRUE, TRUE, 5);
							gtk_box_pack_start(GTK_BOX(ci.vbox), ci.bbox, TRUE, TRUE, 0);
							gtk_widget_show(ci.all);
							gtk_widget_show(ci.none);
							gtk_widget_show(ci.toggle);
							gtk_widget_show(ci.revert);
							gtk_widget_show(ci.bbox);
							return cmd_generic(min, CGF_SRC, chm_body, chm_action, NULL, &ci);
						}
					}
				}
				gtk_widget_destroy(ci.fbox);
			}
			gtk_widget_destroy(ci.label);
		}
		gtk_widget_destroy(ci.vbox);
	}
	return 0;
}
