/* iconmanager.c - runs stuff when icons are clicked on etc.
   Copyright (C) 1997 Paul Sheer

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.
 */

#include "coolwidget.h"
#include "icon.h"
#include "mad.h"

extern int option_mouse_double_click;
extern int cursor_revert_timeout;
extern Window cursor_revert_window;

extern void destroy_icon (CWidget * w);
CWidget *CDrawPowerIcon (CPowerIcon * p);
int edit_dialog (CPowerIcon * icon, char *heading);
char *substitute_strings (char *text, char *cmdline_options, char *editor_file, char *dnd_major_type, char *dnd_minor_type, char *dnd_data_file);

void new_dialog (CWidget * w);
int save_icons (void);
void change_config_file (CWidget * icon);
void edit_change_directory (void);
void raise_lower_all (int r);
void arrange_icons (void);
void close_all (CWidget * w);
void icon_help (void);
void edit_tri_cursor (Window win);


void free_icon_elements (CPowerIcon * icon)
{
    if (icon->title)
	free (icon->title);
    if (icon->xpm_filename)
	free (icon->xpm_filename);
    if (icon->prompt)
	free (icon->prompt);
    if (icon->confirm)
	free (icon->confirm);
    if (icon->drop_script)
	free (icon->drop_script);
    if (icon->double_click_script)
	free (icon->double_click_script);
    if (icon->tool_hint)
	free (icon->tool_hint);
    if (icon->mime_majors) {
	int i;
	for (i = 0; icon->mime_majors[i]; i++)
	    free (icon->mime_majors[i]);
	free (icon->mime_majors);
    }
    memset (icon, 0, sizeof (CPowerIcon));
}

void copy_icon_elements (CPowerIcon * icon, CPowerIcon * icon2)
{
    int i;
    memcpy (icon, icon2, sizeof (CPowerIcon));
    icon->title = (char *) strdup (icon->title);
    icon->xpm_filename = (char *) strdup (icon->xpm_filename);
    icon->prompt = (char *) strdup (icon->prompt);
    icon->confirm = (char *) strdup (icon->confirm);
    icon->drop_script = (char *) strdup (icon->drop_script);
    icon->double_click_script = (char *) strdup (icon->double_click_script);
    icon->tool_hint = (char *) strdup (icon->tool_hint);
    if (icon2->mime_majors) {
	for (i = 0; icon2->mime_majors[i]; i++);
	icon->mime_majors = CMalloc ((i + 1) * (sizeof (char *)));
	for (i = 0; icon2->mime_majors[i]; i++)
	    icon->mime_majors[i] = (char *) strdup (icon2->mime_majors[i]);
	icon->mime_majors[i] = 0;
    }
}

void destroy_power_icon (CWidget * w)
{
    CPowerIcon *icon;
    destroy_icon (w);
    icon = (CPowerIcon *) w->user;
    if (!icon)
	return;
    free_icon_elements (icon);
    free (icon);
    w->user = 0;
}

void CDestroyIconWidget (char *ident)
{
    CWidget *w;
    w = CIdent (ident);
    if (w->droppedmenu) {
	CPullUp (w->droppedmenu);
	CDestroyWidget (w->droppedmenu->ident);
	w->droppedmenu = 0;
    }
    CDestroyWidget (ident);
}

int savefile (const char *filename, const char *data, int len, int permissions);

void icon_system (char *script, char *temp_data_file)
{
    pid_t p;
    p = fork ();
    if (p == -1) {
	CErrorDialog (CRoot, 20, 20, _ (" Execute Script "), _ (" Fork Failed "));
    } else {
	if (!p) {
	    char *temp_script_file;
	    temp_script_file = (char *) tempnam ("/tmp", "cicon");
	    savefile (temp_script_file, script, strlen (script), 0700);
#if 0
	    usleep ((unsigned long) option_mouse_double_click * 1000 * 2);
#endif
	    CSystem (temp_script_file);
	    remove (temp_script_file);
	    if (*temp_data_file)
		remove (temp_data_file);
	    exit (0);
	}
#if 0
	usleep ((unsigned long) option_mouse_double_click * 1000 * 2);
#endif
    }
}

char *filename_from_url (char *data, int size, int i);

void run_script (char *script, char *arg, char *drop_data, int dnd_len, char *dnd_mime_type)
{
    char *f = 0, *temp_data_file = 0;
    char *dnd_major_type, *dnd_minor_type;
    char *data_str;
    data_str = CMalloc (dnd_len + 1);
    memcpy (data_str, drop_data, dnd_len);
    data_str[dnd_len] = '\0';
    drop_data = data_str;

    dnd_major_type = (char *) strdup (dnd_mime_type);
    dnd_minor_type = strchr (dnd_major_type, '/');
    if (dnd_minor_type)
	*dnd_minor_type++ = '\0';
    else
	dnd_minor_type = "";

    if (!strcmp (dnd_mime_type, "url/url") || !strcmp (dnd_mime_type, "text/uri-list"))
	if (!strncmp (data_str, "file:/", 6))
	    f = filename_from_url (data_str, dnd_len, strlen ("file:"));

    if (!f)
	f = (char *) strdup ("");

    temp_data_file = (char *) tempnam ("/tmp", "cicon");
    savefile (temp_data_file, drop_data, dnd_len, 0600);
    script = substitute_strings (script, arg, f, dnd_major_type, dnd_minor_type, temp_data_file);

    icon_system (script, temp_data_file);

    free (script);
    free (temp_data_file);
    free (data_str);
    free (f);
    free (dnd_major_type);
}

void execute_icon_double_click (CEvent * cevent, CPowerIcon * icon)
{
    char *a = 0;
    if (!*icon->double_click_script)
	return;
    CHourGlass (cevent->window);
    CSetOverrideRedirect ();
    if (icon->options & ICON_OPTION_CONFIRM_DOUBLE_CLICK) {
	switch (CQueryDialog (CRoot, icon->x, icon->y, _ (" Execute "), catstrs (" ", icon->confirm, " ", 0), _ (" Ok "), _ (" Cancel "), 0)) {
	case -1:
	case 1:
	    CClearOverrideRedirect ();
	    goto done;
	case 0:
	    break;
	}
    }
    CClearOverrideRedirect ();

    if (icon->options & ICON_OPTION_PROMPT_DOUBLE_CLICK) {
	a = CInputDialog ("iconinput", CRoot, icon->x, icon->y, 300 | INPUT_DIALOG_BROWSE_SAVE, CLastInput ("iconinput"), _ (" Execute icon "), icon->prompt);
	if (!a)
	    goto done;
    }
    run_script (icon->double_click_script, a ? a : "", "", 0, "none/none");

  done:
    cursor_revert_timeout = 4;
    cursor_revert_window = cevent->window;
    if (a)
	free (a);
}

void execute_icon (CWidget * w)
{
    CEvent cevent;
    memset (&cevent, 0, sizeof (CEvent));
    cevent.window = w->winid;
    execute_icon_double_click (&cevent, (CPowerIcon *) w->user);
}

void execute_icon_drop (CWidget * w, CPowerIcon * icon, char *drop_data, int dnd_len, char *dnd_mime_type)
{
    char *a = 0;
    if (!*icon->drop_script)
	return;
    CHourGlass (w->winid);
    CSetOverrideRedirect ();
    if (icon->options & ICON_OPTION_CONFIRM_DROP) {
	switch (CQueryDialog (CRoot, icon->x, icon->y, _ (" Execute "), catstrs (" ", icon->confirm, " ", 0), _ (" Ok "), _ (" Cancel "), 0)) {
	case -1:
	case 1:
	    CClearOverrideRedirect ();
	    goto done;
	case 0:
	    break;
	}
    }
    CClearOverrideRedirect ();
    if (icon->options & ICON_OPTION_PROMPT_DROP) {
	a = CInputDialog ("iconinput", CRoot, icon->x, icon->y, 300 | INPUT_DIALOG_BROWSE_SAVE, CLastInput ("iconinput"), _ (" Execute icon "), icon->prompt);
	if (!a)
	    goto done;
    }
    run_script (icon->drop_script, a ? a : "", drop_data, dnd_len, dnd_mime_type);

  done:
    cursor_revert_timeout = 4;
    cursor_revert_window = w->winid;
    if (a)
	free (a);
}

void copy_icon (CWidget * w)
{
    CPowerIcon icon;
    copy_icon_elements (&icon, (CPowerIcon *) w->user);
    icon.x = w->x + 10;
    icon.y = w->y + 10;
    CDrawPowerIcon (&icon);
}

void edit_icon (CWidget *w)
{
    CPowerIcon icon;
    XUngrabPointer (CDisplay, CurrentTime);
    copy_icon_elements (&icon, (CPowerIcon *) w->user);
    CDisable (w->ident);
    if (edit_dialog (&icon, _(" Edit Icon "))) {
	CDestroyIconWidget (w->ident);
	CDrawPowerIcon (&icon);
	save_icons ();
    } else {
	CEnable (w->ident);
	free_icon_elements (&icon);
    }
}

void delete_icon (CWidget * w)
{
    XUngrabPointer (CDisplay, CurrentTime);
    CSetOverrideRedirect ();
    switch (CQueryDialog (CRoot, w->x, w->y, _(" Delete "), _(" Remove icon from desktop : "), _(" Remove "), _(" Cancel "), 0)) {
    case -1:
    case 1:
	CClearOverrideRedirect ();
	return;
    case 0:
	break;
    }
    CClearOverrideRedirect ();
    CDestroyIconWidget (w->ident);
}

void icon_menu (char *ident)
{
    CEvent cwevent;
    XEvent xevent;
    CWidget *w;
    w = CIdent (ident);
    if (!w)
	return;
    if (!w->droppedmenu)
	return;

    CPullDown (w->droppedmenu);
    XGrabPointer (CDisplay, w->droppedmenu->droppedmenu->winid, False,
		  ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
		  GrabModeSync, GrabModeAsync, CRoot,
		  CGetCursorID (2), CurrentTime);
    for (;;) {
	XAllowEvents (CDisplay, SyncPointer, CurrentTime);
	CNextEvent (&xevent, &cwevent);
	if (xevent.type == ButtonRelease)
	    break;
    }
    XUngrabPointer (CDisplay, CurrentTime);
    w = CIdent (ident);
    if (!w)
	return;
    if (!w->droppedmenu)
	return;
    CPullUp (w->droppedmenu);
}

#ifndef HAVE_DND
/*
   If a filename is dropped onto the main window, open an edit
   window with that file.
 */
struct drop drop;

int handle_drop (CWidget * w, Window from, unsigned char *data, int size, int xs, int ys, Atom type, Atom action)
{
    drop.data = CMalloc (size);
    memcpy (drop.data, data, size);
    drop.size = size;
    drop.atom_name = (char *) strdup (XGetAtomName (CDisplay, type));
    drop.ident = (char *) strdup (w->ident);
    return 0;
}

#endif

int icon_callback (CWidget * w, XEvent * xevent, CEvent * cwevent)
{
    CPowerIcon *ic;
    char ident[33];
    ic = (CPowerIcon *) w->user;
    ic->x = w->x;
    ic->y = w->y;
    switch (cwevent->type) {
    case ButtonPress:
	if (cwevent->button == Button2) {
	    Window win;
	    win = w->winid;
	    strcpy (ident, cwevent->ident);
	    icon_menu (ident);
	    memset (cwevent, 0, sizeof (CEvent));
	    xevent->type = MotionNotify;
	    xevent->xmotion.state = Button1;
	    CSendEvent (xevent);
	    XLowerWindow (CDisplay, win);
	} else if (cwevent->double_click) {
	    execute_icon_double_click (cwevent, ic);
	}
	break;
#ifdef HAVE_DND
    case ClientMessage:{
	    int xs, ys, data_type;
	    unsigned char *data;
	    unsigned long size;
	    data_type = CGetDrop (xevent, &data, &size, &xs, &ys);
	    if (data_type == DndNotDnd)
		return 0;
	    execute_icon_drop (cwevent, ic, (char *) data, size, data_type);
	    if (data)
		free (data);
	    return 1;
	}
	return 0;
#else
#if 0
    case ClientMessage:{
	    int xs, ys, data_type;
	    Atom action, type;
	    unsigned char *data;
	    unsigned long size;
	    action = xdnd_get_drop (CDisplay, xevent, 0, 0, &data, &size, &type, &x, &y);

	    data_type = CGetDrop (xevent, &data, &size, &xs, &ys);
	    if (data_type == DndNotDnd)
		return 0;
	    execute_icon_drop (cwevent, ic, (char *) data, size, data_type);
	    if (data)
		free (data);
	    return 1;
	}
	return 0;
#endif
#endif
    }
    return 0;
}

/* main window only recieves drops, so... */
static struct mouse_funcs icon_mouse_funcs =
{
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    (int (*)(void *, Window, unsigned char *, int, int, int, Atom, Atom)) handle_drop,
    0,
    DndUnknown,		/* no preference, rather use mime_major below */
    0
};

CWidget *CDrawPowerIcon (CPowerIcon * p)
{
    static int n = 0;
    char *f;
    CWidget *w;
    n++;
    if (p->y + FONT_PIX_PER_LINE > HeightOfScreen (DefaultScreenOfDisplay (CDisplay)))
	p->y = HeightOfScreen (DefaultScreenOfDisplay (CDisplay)) - FONT_PIX_PER_LINE;
    if (p->x + 20 > WidthOfScreen (DefaultScreenOfDisplay (CDisplay)))
	p->x = WidthOfScreen (DefaultScreenOfDisplay (CDisplay)) - 20;
    if (*p->xpm_filename != '/')
	f = catstrs (LIBDIR, "/", p->xpm_filename, 0);
    else
	f = p->xpm_filename;
    w = CDrawIcon (catstrs ("_icon", itoa (n), 0), CRoot, p->x, p->y, f, p->title);
    w->funcs = mouse_funcs_new (w, &icon_mouse_funcs);
    w->funcs->mime_majors = p->mime_majors;
    xdnd_set_dnd_aware (CDndClass, w->winid, 0);
    if (!w)
	return 0;
    if (*p->tool_hint)
/* Toolhint */
	CSetToolHint (catstrs ("_icon", itoa (n), 0), p->tool_hint);
    w->user = CMalloc (sizeof (CPowerIcon));
    memcpy (w->user, p, sizeof (CPowerIcon));
    w->destroy = destroy_power_icon;
    w->callback = icon_callback;
    if (!strcmp (p->double_click_script, "<icon manager>")) {
	w->droppedmenu = CDrawMenuButton (catstrs ("_icon", itoa (n), ".menu", 0), w->winid, CRoot, 0, w->height, AUTO_WIDTH, 1, 9,
					_ (" Dummy "),
					_ ("Help..."), 0, (void (*)(unsigned long)) icon_help, 0,
					_ ("New icon..."), 0, new_dialog, (unsigned long) w,
					_ ("Auto arrange"), 0, arrange_icons, 0,
					_ ("Change directory..."), 0, edit_change_directory, 0,
					_ ("Save icons"), 0, save_icons, 0,
					_ ("Config file..."), 0, change_config_file, (unsigned long) w,
					_ ("Raise icons"), 0, raise_lower_all, 1,
					_ ("Lower icons"), 0, raise_lower_all, 0,
					_ ("Kill Coolicons..."), 0, close_all, (unsigned long) w
	    );
    } else {
	w->droppedmenu = CDrawMenuButton (catstrs ("_icon", itoa (n), ".menu", 0), w->winid, CRoot, 0, w->height, AUTO_WIDTH, 1, 7,
					_ (" Dummy "),
					_ ("Edit icon..."), 0, (void (*)(unsigned long)) edit_icon, (unsigned long) w,
					_ ("Execute icon"), 0, execute_icon, (unsigned long) w,
					_ ("Copy icon"), 0, copy_icon, (unsigned long) w,
					_ ("Delete icon"), 0, delete_icon, (unsigned long) w,
					_ ("New icon..."), 0, new_dialog, (unsigned long) w,
					_ ("Auto arrange"), 0, arrange_icons, 0,
					_ ("Save icons"), 0, save_icons, 0
	    );
    }
    return w;
}

void strnlcpy (unsigned char *s, unsigned char *t)
{
    for (;;) {
	*s = (*t == '\n') ? 129 : *t;
	if (!*t)
	    break;
	s++;
	t++;
    }
}

void memlncpy (unsigned char *s, unsigned char *t, int l)
{
    while (l--) {
	*s = (*t == 129) ? '\n' : *t;
	s++;
	t++;
    }
}

void convert_commalist_to_array (char *mime_majors, char ***a)
{
    char **h, *p, *q;
    int i, done = 0;
    for (i = 0, p = q = mime_majors; (p = strchr (q, ',')); q = ++p, i++);
    h = *a = CMalloc ((i + 2) * sizeof (char *));
    for (p = q = mime_majors; !done; q = p) {
	while (!strchr (", \t", *p) && *p)
	    p++;
	if (*p)
	    *p++ = '\0';
	else
	    done = 1;
	if (*q) {
	    *h = (char *) strdup (q);
	    h++;
	}
	while (strchr (", \t", *p) && *p)
	    p++;
    }
    *h = 0;
}

void convert_array_to_commalist (char **a, char **mime_majors)
{
    if (!a) {
	*mime_majors = (char *) strdup ("");
    } else {
	int i, l;
	for (l = i = 0; a[i]; i++)
	    l += strlen (a[i]) + 2;
	*mime_majors = CMalloc (l + 1);
	**mime_majors = '\0';
	for (i = 0; a[i]; i++) {
	    strcat (*mime_majors, a[i]);
	    if (a[i + 1])
		strcat (*mime_majors, ", ");
	}
    }
}

char *gather_icon_options_for_config (void)
{
    char *t, *p, *q;
    int i = 0, size = 0, n;
    while (last_widget > i++)
	if (CIndex (i) != NULL)
	    if ((CIndex (i))->kind == C_ICON_WIDGET && (CIndex (i))->user) {
		CPowerIcon *icon;
		icon = (CPowerIcon *) (CIndex (i))->user;
		size += strlen (icon->title);
		size += strlen (icon->xpm_filename);
		size += strlen (icon->prompt);
		size += strlen (icon->confirm);
		size += strlen (icon->drop_script);
		size += strlen (icon->double_click_script);
		size += strlen (icon->tool_hint);
		if (icon->mime_majors)
		    for (n = 0; icon->mime_majors[n]; n++)
			size += strlen (icon->mime_majors[n]) + 2;
		size += 256;
	    }
    p = t = CMalloc (size);
    i = 0;
    while (last_widget > i++)
	if (CIndex (i) != NULL)
	    if ((CIndex (i))->kind == C_ICON_WIDGET && (CIndex (i))->user) {
		CPowerIcon *icon;
		icon = (CPowerIcon *) (CIndex (i))->user;
		strcpy (p, "title = ");
		p += strlen (p);
		strnlcpy ((unsigned char *) p, (unsigned char *) icon->title);
		strcat (p, "\nxpm_filename = ");
		strcat (p, icon->xpm_filename);
		strcat (p, "\nx = ");
		strcat (p, itoa ((CIndex (i))->x));
		strcat (p, "\ny = ");
		strcat (p, itoa ((CIndex (i))->y));
		strcat (p, "\nprompt = ");
		p += strlen (p);
		strnlcpy ((unsigned char *) p, (unsigned char *) icon->prompt);
		strcat (p, "\nconfirm = ");
		p += strlen (p);
		strnlcpy ((unsigned char *) p, (unsigned char *) icon->confirm);
		strcat (p, "\noptions = ");
		strcat (p, itoa (icon->options));
		strcat (p, "\ndrop_script = ");
		p += strlen (p);
		strnlcpy ((unsigned char *) p, (unsigned char *) icon->drop_script);
		strcat (p, "\ndouble_click_script = ");
		p += strlen (p);
		strnlcpy ((unsigned char *) p, (unsigned char *) icon->double_click_script);
		strcat (p, "\ntool_hint = ");
		strcat (p, icon->tool_hint);
		strcat (p, "\nmime_majors = ");
		convert_array_to_commalist (icon->mime_majors, &q);
		strcat (p, q);
		free (q);
		strcat (p, "\n\n");
		p += strlen (p);
	    }
    *p = '\0';
    return t;
}

static char *advance_equate (char *p, char *g, int *l)
{
    char *q;
    while (strchr ("\n\t ", *p))
	p++;
    if (strncmp (p, g, strlen (g)))
	return 0;
    p += strlen (g);
    q = strchr (p, '\n');
    if (!q)
	return 0;
    *l = (unsigned long) q - (unsigned long) p;
    return p;
}

/* returns 1 on error */
static int read_string (char **p, char *g, char **s)
{
    char *q;
    int l;
    q = advance_equate (*p, g, &l);
    if (!q)
	return 1;
    *s = CMalloc (l + 1);
    memlncpy ((unsigned char *) *s, (unsigned char *) q, l);
    (*s)[l] = '\0';
    *p = q + l + 1;
    return 0;
}

/* returns 1 on error */
static int read_int (char **p, char *g, int *i)
{
    char *q;
    int l;
    q = advance_equate (*p, g, &l);
    if (!q)
	return 1;
    *i = atoi (q);
    *p = q + l + 1;
    return 0;
}

/* returns 1 on error */
int draw_icons_from_config (char *t)
{
    char *p, *mime_majors;
    int n = 0;
    p = t;
    for (;;) {
	CPowerIcon icon;
	memset (&icon, 0, sizeof (CPowerIcon));
	n++;
	p = strstr (p, "title = ");
	if (!p)
	    break;
	if (read_string (&p, "title = ", &icon.title))
	    goto icon_error;
	if (read_string (&p, "xpm_filename = ", &icon.xpm_filename))
	    goto icon_error;
	if (read_int (&p, "x = ", &icon.x))
	    goto icon_error;
	if (read_int (&p, "y = ", &icon.y))
	    goto icon_error;
	if (read_string (&p, "prompt = ", &icon.prompt))
	    goto icon_error;
	if (read_string (&p, "confirm = ", &icon.confirm))
	    goto icon_error;
	if (read_int (&p, "options = ", &icon.options))
	    goto icon_error;
	if (read_string (&p, "drop_script = ", &icon.drop_script))
	    goto icon_error;
	if (read_string (&p, "double_click_script = ", &icon.double_click_script))
	    goto icon_error;
	if (read_string (&p, "tool_hint = ", &icon.tool_hint))
	    goto icon_error;
	if (read_string (&p, "mime_majors = ", &mime_majors)) {
	    icon.mime_majors = 0;
	} else {
	    convert_commalist_to_array (mime_majors, &icon.mime_majors);
	    free (mime_majors);
	}
	if (*p != '\n')
	    break;
	CDrawPowerIcon (&icon);
	p++;
    }
    return 0;
  icon_error:
    CErrorDialog (CRoot, 20, 20, _ (" Reading Config "), _ (" Error interpreting icon %d from config, \n not bothering to read further "), n);
    return 1;
}

