/* coolicons.c - example usage of the coolwidget X API
   Copyright (C) 1997, 1998 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 "3dtext.h"
#include "cmdlineopt.h"
#include "icon.h"
#include <sys/types.h>
#include <my_string.h>
#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#include <sys/stat.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "mad.h"

#if (RETSIGTYPE==void)
#define handler_return return
#else
#define handler_return return 1
#endif

char *loadfile (const char *filename, long *filelen);
void draw_icons_from_config (char *t);
CWidget *CDrawPowerIcon (CPowerIcon * p);
int savefile (const char *filename, const char *data, int len, int permissions);
char *gather_icon_options_for_config (void);
void edit_tri_cursor (Window win);
void free_icon_elements (CPowerIcon * icon);
void free_all_lists (void);

#define SYSTEM_ICONS LIBDIR "/coolicon.config"
#define E_DATA LIBDIR "/e.data"
#define START_WIDTH size_of_e
#define START_HEIGHT size_of_e


/* {{{ command-line options */

#ifdef HAVE_DND
extern int option_dnd_version;
#endif

int option_wait_for_display = 0;
int option_shape = 0;
int size_of_e = 150;
int option_daemon = 0;

/* usually in /var/spool/mail/$USERNAME or similar */
char *mail_box_file_name = 0;
char *e_data_file_name = E_DATA;

/* interval to stat the above file */
int mail_check_seconds = 30;

/* shell command to run */
extern char *option_man_cmdline;

/* argv[0] */
char *argv_nought = 0;

/* font from the library */
extern char *init_font;

/* server from command line */
char *option_display = 0;

/* font from the command line */
char *option_font = "-*-helvetica-bold-r-*--12-*-*-*-p-*-iso8859-1";

/* config file */
char *current_config_file = 0;

static int get_help = 0;
static int get_version = 0;

int cursor_revert_timeout = 0;
Window cursor_revert_window = 0;

/* other things on the command line */
static char *command_line_args[] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

void usage (void)
{
    printf (\
	    _ ("Coolicon version %s\nUsage:\n" \
	       "Coolicon [options]\n" \
	       "-d, -display <display>                   the X server to display on\n" \
	       "-w, --wait-for-display                   if the server is not available\n" \
	       "                                         then keep trying to connect\n" \
	       "-s, --shape                              floating email `e' image using\n" \
	       "                                         shape extension\n" \
	       "-X, --size <pixels>                      size of `e' window in pixels\n" \
	       "-e, --e-data <file-name>                 3d data to read from\n" \
	       "                                         default %s\n" \
	       "-M, --mail-name <file-name>              specify the mail box file-name\n" \
	       "-S, --mail-seconds <seconds>             interval to check above file\n" \
	       "-f, -fn, -font <font-name>               default: 8x13bold\n" \
	       "-C, -config, --config-file <file-name>   use <file-name> as config file\n" \
	       "-h, -H, -?, --help                       print this message to stdout\n" \
	       "-V, -v, --version                        print versiom info\n" \
	       "\n"), \
	    VERSION, E_DATA);
}

void version (void)
{
    printf (_ ("Coolicon version %s\n"), VERSION);
}

struct prog_options coolicon_options[] =
{
    {' ', "", "", ARG_STRINGS, 0, command_line_args, 0},
#ifdef HAVE_DND
    {0, "-dnd-old", "--dnd-old", ARG_CLEAR, 0, 0, &option_dnd_version},
#endif
    {'f', "-fn", "-font", ARG_STRING, &option_font, 0, 0},
    {'C', "-config", "--config-file", ARG_STRING, &current_config_file, 0, 0},
    {'M', "", "--mail-name", ARG_STRING, &mail_box_file_name, 0, 0},
    {'X', "", "--size", ARG_INT, 0, 0, &size_of_e},
    {'S', "", "--mail-seconds", ARG_INT, 0, 0, &mail_check_seconds},
    {'e', "", "--e-data", ARG_STRING, &e_data_file_name, 0, 0},
    {'h', "-?", "--help", ARG_SET, 0, 0, &get_help},
    {'w', "", "--wait-for-display", ARG_SET, 0, 0, &option_wait_for_display},
    {'s', "-shape", "--shape", ARG_SET, 0, 0, &option_shape},
    {'H', "-help", "--help", ARG_SET, 0, 0, &get_help},
    {'V', "-v", "--version", ARG_SET, 0, 0, &get_version},
    {'d', "", "-display", ARG_STRING, &option_display, 0, 0},
/*    {'D', "", "--deamon", ARG_SET, 0, 0, &option_daemon}, */
    {0, 0, 0, 0, 0, 0, 0}
};

/* here we use our own function (which is better than get_opt() or get_opt_long()) */
static void process_command_line (int argc, char **argv)
{
    int error;
    error = get_cmdline_options (argc, argv, coolicon_options);

    if (error) {
	fprintf (stderr, _ ("%s: error processing commandline argument %d\n"), argv[0], error);
	usage ();
	exit (1);
    }
    if (get_help)
	usage ();
    if (get_version)
	version ();
    if (get_help || get_version)
	exit (0);
}

/* }}} command-line options */


static RETSIGTYPE child_handler (int x)
{
    waitpid (0, 0, WNOHANG | WUNTRACED);

    waitpid (0, 0, WNOHANG | WUNTRACED);

    signal (SIGCHLD, child_handler);
    handler_return;
}

static void set_signals (void)
{
    signal (SIGPIPE, SIG_IGN);
    signal (SIGCHLD, child_handler);
}

static int icon_width;

int sort_icons (CWidget ** a, CWidget ** b)
{
    int p, q;
    p = (((*a)->x - 5 - icon_width / 2 + icon_width * 2) / icon_width) * 65536 + (*a)->y;
    q = (((*b)->x - 5 - icon_width / 2 + icon_width * 2) / icon_width) * 65536 + (*b)->y;
    return p > q ? 1 : (p < q ? -1 : 0);
}

#define ICON_SPACING 1

void arrange_icons (void)
{
    int i, n, width, height;
    int h, x, y;
    CWidget *icons[1024];

    i = n = 0;
    width = height = 0;
    while (last_widget > i++)
	if (CIndex (i) != 0)
	    if ((CIndex (i))->kind == C_ICON_WIDGET) {
		if ((CIndex (i))->user)
		    icons[n++] = CIndex (i);
		if (width < (CIndex (i))->width)
		    width = (CIndex (i))->width;
		if (height < (CIndex (i))->height)
		    height = (CIndex (i))->height;
	    }
    width += ICON_SPACING;
    height += ICON_SPACING;
    icon_width = width;

    h = HeightOfScreen (DefaultScreenOfDisplay (CDisplay)) - 30 - height;

    qsort (icons, n, sizeof (CWidget *),
	   (int (*)(const void *, const void *)) sort_icons);
    x = 5;
    y = 5;
    for (i = 0; i < n; i++) {
	if (y > h) {
	    y = 5;
	    x += width;
	}
	CSetWidgetPosition (icons[i]->ident, x + (width - icons[i]->width) / 2, y + (height - icons[i]->height) / 2);
	y += height;
    }
}

void raise_lower_all (int r)
{
    int i = 0;
    XUngrabPointer (CDisplay, CurrentTime);
    while (last_widget > i++)
	if (CIndex (i) != 0)
	    if ((CIndex (i))->kind == C_ICON_WIDGET) {
		if (r)
		    XRaiseWindow (CDisplay, (CIndex (i))->winid);
		else
		    XLowerWindow (CDisplay, (CIndex (i))->winid);
	    }
}

void edit_change_directory (void)
{
    char *new_dir;
    XUngrabPointer (CDisplay, CurrentTime);
    new_dir = CGetDirectory (CRoot, 20, 20, current_dir, "", 
/* heads the 'Change Directory' dialog box */
    _(" Change Current Directory "));
    if (new_dir) {
	if (change_directory (new_dir) < 0) { /* which should never happen */

/* heads the 'Change Directory' dialog box */
	    CErrorDialog(CRoot, 20, 20, _(" Change directory "), get_sys_error (_(" Error return from chdir. ")));
	}
    }
}

void icon_help (void)
{
    switch (fork ()) {
    case 0:
	set_signal_handlers_to_default ();
	execlp ("coolman", "coolman", "coolicon", 0);
	exit (0);
    case -1:
	CErrorDialog (0, 0, 0, _ (" Run 'coolman' "), get_sys_error (_ (" Error trying to fork process ")));
	return;
    default:
	return;
    }
}

int edit_dialog (CPowerIcon * icon, char *heading)
{
    CEvent cwevent;
    Window win;
    CWidget *edit0, *edit1, *edit2;
    int x, y, yu, x2, y2;
    int w, h, result = 0, half;
    char *mime_majors = 0;

    if (CIdent ("edit"))
	return 0;

    XUngrabPointer (CDisplay, CurrentTime);

    win = CDrawHeadedDialog ("edit", CRoot, 20, 20, heading);

    CGetHintPos (&x, &y);
    yu = y;

/* this must be the longest of the messages in this dialog */
    CTextSize (&w, &h, _(" Prompt before executing : "));
    w += WIDGET_SPACING * 2;
    CDrawText ("edit.ttitle", win, x, y, _(" Icon title : "));
    edit0 = CDrawEditor ("edit.title", win, x + w, y,
			 FONT_MEAN_WIDTH * 41, 2 * FONT_PIX_PER_LINE, icon->title ? icon->title : "", 0, 0, EDITOR_NO_TEXT | EDITOR_NO_FILE | EDITOR_NO_SCROLL, strlen (icon->title ? icon->title : ""));
    CGetHintPos (0, &y);

    CDrawText ("edit.tfilename", win, x, y, _(" Icon XPM file-name : "));
    CDrawTextInput ("edit.filename", win, x + w, y, FONT_MEAN_WIDTH * 41, AUTO_HEIGHT, 256, icon->xpm_filename ? icon->xpm_filename : "");
    CGetHintPos (0, &y);

    CDrawText ("edit.tprompt", win, x, y, _(" Prompt before executing : "));
    CDrawTextInput ("edit.prompt", win, x + w, y, FONT_MEAN_WIDTH * 41, AUTO_HEIGHT, 256, icon->prompt ? icon->prompt : "");
    CGetHintPos (0, &y);

    CDrawText ("edit.tconfirm", win, x, y, _(" Confirmation prompt : "));
    CDrawTextInput ("edit.confirm", win, x + w, y, FONT_MEAN_WIDTH * 41, AUTO_HEIGHT, 256, icon->confirm ? icon->confirm : "");
    CGetHintPos (0, &y);

    CDrawText ("edit.tpos", win, x, y, _(" Starting position "));
    CDrawText ("edit.tx", win, x + w, y, _(" X : "));
    CGetHintPos (&x2, 0);
    x2 -= WIDGET_SPACING;
    CDrawTextInput ("edit.x", win, x2, y, FONT_MEAN_WIDTH * 14, AUTO_HEIGHT, 256, itoa (icon->x));
    CGetHintPos (&x2, 0);
    CDrawText ("edit.tx", win, x2, y, _(" Y : "));
    CGetHintPos (&x2, 0);
    x2 -= WIDGET_SPACING;
    CDrawTextInput ("edit.y", win, x2, y, FONT_MEAN_WIDTH * 14, AUTO_HEIGHT, 256, itoa (icon->y));
    CGetHintPos (0, &y);

    CDrawText ("edit.ttoolhint", win, x, y, _(" Toolhint : "));
    (CDrawTextInput ("edit.toolhint", win, x + w, y, FONT_MEAN_WIDTH * 40, AUTO_HEIGHT, 256, icon->tool_hint ? icon->tool_hint : ""))->position |= POSITION_FILL;
    CGetHintPos (0, &y);

    CDrawText ("edit.tconfdclick", win, x, y, _(" Double click : "));
    CDrawSwitch ("edit.confdclick", win, x + w, y, icon->options & ICON_OPTION_CONFIRM_DOUBLE_CLICK, _ (" Ask confirm "), 1);
    CGetHintPos (&x2, 0);
    CDrawSwitch ("edit.promptdclick", win, x2, y, icon->options & ICON_OPTION_PROMPT_DOUBLE_CLICK, _ (" Show prompt "), 1);
    CGetHintPos (0, &y);

    CDrawText ("edit.tconfdrop", win, x, y, _(" Recieve drop : "));
    CDrawSwitch ("edit.confdrop", win, x + w, y, icon->options & ICON_OPTION_CONFIRM_DROP, _ (" Ask confirm "), 2);
    CGetHintPos (&x2, 0);
    CDrawSwitch ("edit.promptdrop", win, x2, y, icon->options & ICON_OPTION_PROMPT_DROP, _ (" Show prompt "), 2);
    CGetHintPos (0, &y);

    CDrawText ("edit.tmimetypes", win, x, y, _(" Comma separated list of major Top-Level MIME types : "));
    CGetHintPos (0, &y);

    mime_majors = 0;
    if (icon->mime_majors)
	convert_array_to_commalist (icon->mime_majors, &mime_majors);
    (CDrawTextInput ("edit.mimetypes", win, x, y, FONT_MEAN_WIDTH * 40, AUTO_HEIGHT, 256, mime_majors ? mime_majors : ""))->position |= POSITION_FILL;
    if (mime_majors)
	free (mime_majors);
    CGetHintPos (0, &y);

    CDrawText ("edit.tdropscript", win, x, y, _(" Script for receiving a drop : "));
    CGetHintPos (0, &y2);
    edit1 = CDrawEditor ("edit.dropscript", win, x, y2,
			 40 * FONT_MEAN_WIDTH, 10 * FONT_PIX_PER_LINE, icon->drop_script ? icon->drop_script : "#!/bin/sh\n\n", 0, 0, EDITOR_NO_TEXT | EDITOR_NO_FILE, strlen (icon->drop_script ? icon->drop_script : "#!/bin/sh\n\n"));
    CGetHintPos (&half, 0);
    CDrawText ("edit.tdclick", win, x + half, y, _(" Script for double click : "));
    edit2 = CDrawEditor ("edit.dclick", win, x + half, y2,
			 40 * FONT_MEAN_WIDTH, 10 * FONT_PIX_PER_LINE, icon->double_click_script ? icon->double_click_script : "#!/bin/sh\n\n", 0, 0, EDITOR_NO_TEXT | EDITOR_NO_FILE, strlen (icon->double_click_script ? icon->double_click_script : "#!/bin/sh\n\n"));
    CGetHintPos (0, &y);

    get_hint_limits (&x, &y);
    x -= WIDGET_SPACING + TICK_BUTTON_WIDTH;
    CDrawPixmapButton ("edit.ok", win, x, yu, PIXMAP_BUTTON_TICK);
    yu += WIDGET_SPACING + TICK_BUTTON_WIDTH;
    CDrawPixmapButton ("edit.cancel", win, x, yu, PIXMAP_BUTTON_CROSS);

    CFocus (CIdent ("edit.title"));
    CSetSizeHintPos ("edit");
    CMapDialog ("edit");

    for (;;) {
	CNextEvent (0, &cwevent);
	if (!CIdent ("edit"))
	    break;
	if (!strcmp (cwevent.ident, "edit.cancel"))
	    break;
	if (cwevent.command == CK_Cancel)
	    break;
	if (!strcmp (cwevent.ident, "edit.ok") || (!cwevent.handled && cwevent.command == CK_Enter)) {
	    free_icon_elements (icon);
	    icon->title = edit_get_buffer_as_text (edit0->editor);
	    icon->xpm_filename = (char *) strdup (CIdent ("edit.filename")->text);
	    icon->prompt = (char *) strdup (CIdent ("edit.prompt")->text);
	    icon->confirm = (char *) strdup (CIdent ("edit.confirm")->text);
	    icon->x = atoi (CIdent ("edit.x")->text);
	    icon->y = atoi (CIdent ("edit.y")->text);
	    icon->drop_script = edit_get_buffer_as_text (edit1->editor);
	    icon->double_click_script = edit_get_buffer_as_text (edit2->editor);
	    icon->tool_hint = (char *) strdup (CIdent ("edit.toolhint")->text);
	    convert_commalist_to_array (CIdent ("edit.mimetypes")->text, &icon->mime_majors);
	    icon->options = 0;
	    icon->options |= CIdent ("edit.confdclick")->keypressed != 0 ? ICON_OPTION_CONFIRM_DOUBLE_CLICK : 0;
	    icon->options |= CIdent ("edit.promptdclick")->keypressed != 0 ? ICON_OPTION_PROMPT_DOUBLE_CLICK : 0;
	    icon->options |= CIdent ("edit.confdrop")->keypressed != 0 ? ICON_OPTION_CONFIRM_DROP : 0;
	    icon->options |= CIdent ("edit.promptdrop")->keypressed != 0 ? ICON_OPTION_PROMPT_DROP : 0;
	    result = 1;
	    break;
	}
    }

    CDestroyWidget ("edit");
    return result;
}

void change_config_file (CWidget * icon)
{
    char *a;
    static char *config_file = 0;
    a = CInputDialog ("iconconfig", CRoot, icon->x, icon->y, 300 | INPUT_DIALOG_BROWSE_SAVE, current_config_file, _(" Change Config File "), _(" Enter new config file : "));
    if (a) {
	current_config_file = a;
	if (config_file)
	    free (config_file);
	config_file = a;
    }
}

int save_icons (void)
{
    char *t;
    char s[256];
    XUngrabPointer (CDisplay, CurrentTime);
    t = loadfile (current_config_file, 0);
    if (t) {
/* create backup of config */
	if (savefile (catstrs (current_config_file, "~", 0), t, strlen (t), 0600) < 0)
	    goto error;
	free (t);
    }
    t = gather_icon_options_for_config ();
    if (savefile (current_config_file, t, strlen (t), 0600) < 0)
	goto error;
    free (t);
    return 0;
  error:
    sprintf (s, _ (" Error trying to save config file `%s' or backup file `%s~' "), current_config_file, current_config_file);
    CErrorDialog (CRoot, 20, 20, _ (" Save Icons "), get_sys_error (s));
    return 1;
}

void new_dialog (CWidget * w)
{
    CPowerIcon icon;
    XUngrabPointer (CDisplay, CurrentTime);
    memset (&icon, 0, sizeof (CPowerIcon));
    icon.x = w->x;
    icon.y = w->y + w->height;
    if (edit_dialog (&icon, _(" New Icon "))) {
	CDrawPowerIcon (&icon);
	save_icons ();
    }
}

void icon_menu (char *ident);

void close_all (CWidget * w)
{
    XUngrabPointer (CDisplay, CurrentTime);
    CSetOverrideRedirect ();
    switch (CQueryDialog (CRoot, w->x, w->y, _(" Exit "), _(" This will kill the icon application : "), _(" Kill "), _(" Save all and kill "), _(" Don't kill "), 0)) {
    case -1:
    case 2:
	CClearOverrideRedirect ();
	return;
    case 0:
	break;
    case 1:
	if (save_icons ()) {
	    CClearOverrideRedirect ();
	    return;
	}
	break;
    }
    CShutdown ();
    free_all_lists ();
    catstrs_clean ();
    mad_finalize (__FILE__, __LINE__);
    exit (0);
}

GC shape_gc;
Pixmap shape_pixmap = 0;
int this_thes;

void shape_create (Window win, int width, int height)
{
    shape_pixmap = XCreatePixmap (CDisplay, CRoot, width, height, 1);
    shape_gc = XCreateGC (CDisplay, shape_pixmap, 0, 0);
}

void shape_free (void)
{
    if (!shape_pixmap)
	return;
    XFreeGC (CDisplay, shape_gc);
    XFreePixmap (CDisplay, shape_pixmap);
    shape_pixmap = 0;
}

void this_striangle (int x0, int y0,
		      int x1, int y1,
		      int x2, int y2, int z0, int bf)
{
    long n;
    XPoint p[3];
    p[0].x = x0;
    p[0].y = y0;
    p[1].x = x1;
    p[1].y = y1;
    p[2].x = x2;
    p[2].y = y2;

    if ((((n = (x0 - x1) * (y0 - y2) - (y0 - y1) * (x0 - x2)) > 0) + bf) & 1 || bf == 2) {
	if (abs (n) < this_thes) {
	    if (abs (x0) + abs (x1) + abs (x2) + abs (y0) + abs (y1) + abs (y2) < 32000)
		XFillPolygon (CDisplay, shape_pixmap, shape_gc, p, 3, Convex, CoordModeOrigin);
	}
    }
}

void local_striangle (int x0, int y0,
		      int x1, int y1,
		      int x2, int y2, int z0, int bf);

void shape_combine (char *ident)
{
    CWidget *w;
    TD_Solid *object;
    w = CIdent (ident);
    object = w->solid;
    object->draw_striangle = this_striangle;
    gl_setcontext (w->gl_graphicscontext);
    gl_enableclipping ();
    this_thes = w->width * w->height;
    XSetForeground (CDisplay, shape_gc, 0);
    XFillRectangle (CDisplay, shape_pixmap, shape_gc, 0, 0, w->width - 4, w->height - 4);
    XSetForeground (CDisplay, shape_gc, 1);
    TD_draw_solid (object);
    XShapeCombineMask (CDisplay, w->winid, ShapeBounding, 2, 2, shape_pixmap, ShapeSet);
    object->draw_striangle = local_striangle;
}


extern int override_8bit_non_lookup;

void set_object_options (char *ident)
{
    int j, n;
    CWidget *w;
    TD_Solid *object;

    w = CIdent (ident);
    object = w->solid;

    n = object->num_surfaces;
    object->render = TD_SOLID;
#if 0
    object->option_flags &= (0xFFFFFFFFUL - TDOPTION_FLAT_TRIANGLE);
#endif

    for (j = 0; j < n; j++) {
	object->surf[j].backfacing = 1;
	object->surf[j].depth_per_color = 4;
	object->surf[j].mesh_color = color_palette (6);
	if (CDepth >= 8 && (ClassOfVisual (CVisual) == PseudoColor || ClassOfVisual (CVisual) == StaticGray)) {
#if 0
	    object->surf[j].shadow = color_widget (1);
	    object->surf[j].maxcolor = color_widget (15);
#else
	    override_8bit_non_lookup = 1;
	    object->surf[j].shadow = 1;
	    object->surf[j].maxcolor = 15;
#endif
	} else {
	    object->surf[j].shadow = 1;
	    object->surf[j].maxcolor = 15;
	}
    }
}

int cb_object (CWidget * w, XEvent * xevent, CEvent * cwevent);
int object_update (CWidget * w, XEvent * x, CEvent * ce);

void show_e (void)
{
    static char *object_text;
    Window win;
    int x, y, load_error = 0;
    x = WidthOfScreen (DefaultScreenOfDisplay (CDisplay)) / 2 - START_WIDTH / 2;
    y = HeightOfScreen (DefaultScreenOfDisplay (CDisplay)) / 2 - START_HEIGHT / 2;
    if (option_shape) {
	win = CRoot;
    } else {
	win = CDrawMainWindow ("objectwin", " You have new mail ");
	CGetHintPos (&x, &y);
    }

    CSetOverrideRedirect ();
    CDraw3DObject ("object", win, x, y, START_WIDTH, START_HEIGHT, 1, 25);
    CClearOverrideRedirect ();

    CAddCallback ("object", cb_object);
    set_object_options ("object");

    if (option_shape)
	shape_create (CIdent ("object")->winid, CIdent ("object")->width - 4, CIdent ("object")->height - 4);

    if (!object_text) {
	object_text = loadfile (e_data_file_name, 0);
	if (!object_text) {
	    object_text = "scale 200\n\nsphere 0 0 0 100\n\n";
	    load_error = 1;
	}
    }
    CDraw3DFromText ("object", object_text);

    if (option_shape) {
	shape_combine ("object");
    } else {
	CSetSizeHintPos ("objectwin");
	CMapDialog ("objectwin");
    }
    if (load_error) {
	CErrorDialog (CRoot, 20, 20, _ (" Check Mailbox "), \
	get_sys_error (catstrs (_ ("Unable to load 3D data from file "), \
				E_DATA, " ", 0)));
    }
}

void hide_e (void)
{
    if (option_shape)
	shape_free ();
    CDestroyWidget ("object");
    CDestroyWidget ("objectwin");
}

int object_update (CWidget * w, XEvent * x, CEvent * ce)
{
    static long mail_box_size = -2;
    static int count = 0;
    struct stat stats;

    if (!mail_box_file_name)
	return 1;

    w = CIdent ("object");

    if (!w)
	if (option_shape)
	    shape_free ();

    if (!w) {
	if (mail_box_size < 0) {
	    if (stat (mail_box_file_name, &stats)) {
		if (mail_box_size == -2) {
		    CErrorDialog (CRoot, 20, 20, _ (" Check Mailbox "), get_sys_error (_ ("Unable to stat mailbox, check you mailbox filename")));
		    CAddCallback ("AlarmCallback", 0);
		}
		return 1;
	    }
	    mail_box_size = stats.st_size;
	    return 1;
	}
	count++;
	if (count > CGetCursorBlinkRate () * mail_check_seconds) {
	    count = 0;
	    if (!stat (mail_box_file_name, &stats)) {
		if (stats.st_size > mail_box_size + 80) {	/* there are at least 80 characters in a mail header */
		    show_e ();
		}
		mail_box_size = stats.st_size;
	    }
	    return 1;
	}
    } else {
	mail_box_size = -1;
	w->solid->alpha += 0.1;
	w->solid->beta += 0.01;
	if (option_shape)
	    shape_combine ("object");
	CRedraw3DObject ("object", 1);
    }
    return 1;
}


int cb_object (CWidget * w, XEvent * xevent, CEvent * cwevent)
{
    static int y3dprev = 0, x3dprev = 0;
    static float alphaprev = 0, betaprev = 0;
    if (cwevent->type == ButtonPress && cwevent->double_click) {
	hide_e ();
	return 1;
    }
    if (cwevent->type == ButtonPress && cwevent->button == Button2)
	XLowerWindow (CDisplay, w->winid);
    if (cwevent->type == ButtonPress && cwevent->button == Button1) {
	XRaiseWindow (CDisplay, w->winid);
	x3dprev = cwevent->x;
	y3dprev = cwevent->y;
	alphaprev = w->solid->alpha;
	betaprev = w->solid->beta;
    }
    if ((cwevent->type == MotionNotify && (cwevent->state & Button1Mask)) ||
	(cwevent->type == ButtonRelease && cwevent->button == Button1)) {
	w->solid->alpha = (float) alphaprev + (float) ((float) cwevent->x - x3dprev) / 100;
	w->solid->beta = (float) betaprev + (float) ((float) cwevent->y - y3dprev) / 100;
	if (!CCheckWindowEvent (w->winid, ButtonPressMask | ButtonReleaseMask | ButtonMotionMask, 0)) {
	    if (option_shape)
		shape_combine (w->ident);
	    CRedraw3DObject (w->ident, 1);
	}
    }
    return 0;
}


void load_trivial_options (void);

extern struct drop drop;

char *load_config (char *file, long *l)
{
    char *t;
    t = loadfile (file, l);
    if (!t)
	return 0;
    if (strstr (t, "DndFile")
	|| strstr (file, "DndNotDnd")
	|| strstr (file, "DndUnknown")
	|| strstr (file, "DndRawData")
	|| strstr (file, "DndFile")
	|| strstr (file, "DndFiles")
	|| strstr (file, "DndText")
	|| strstr (file, "DndDir")
	|| strstr (file, "DndLink")
	|| strstr (file, "DndExe")
	|| strstr (file, "DndURL")
	|| strstr (file, "DndMIME"))
	CMessageDialog (0, 0, 0, 0, "Coolicon Warning",
			"Coolicon has detected that you have used one of the Dnd types in your\n" \
			"icon scripts. Dnd is no longer supported. Please refer to the\n" \
			"cooledit(1) and coolman(1) man pages for an explanation of the new XDND\n" \
			"protocol types. These will be MIME types with %%%%T denoting the top-level\n" \
			"type and %%%%s denoting the sub-type.");
    return t;
}

extern int option_auto_spellcheck;

int main (int argc, char **argv)
{
    char *config;
    int done = 0;
    Window win;
    CInitData coolicon_startup;
    CEvent cwevent;
    XEvent xevent;
    memset (&drop, 0, sizeof (drop));

    setlocale (LC_CTYPE, "");
    setlocale (LC_TIME, "");
    setlocale (LC_MESSAGES, "");
    bindtextdomain (PACKAGE, LOCALEDIR);
    textdomain (PACKAGE);

    option_auto_spellcheck = 0;

    load_trivial_options ();

    process_command_line (argc, argv);

/* initialise the library */
    memset (&coolicon_startup, 0, sizeof (coolicon_startup));
    coolicon_startup.name = argv[0];
    coolicon_startup.display = option_display;
    coolicon_startup.font = option_font;
    if (option_wait_for_display)
	coolicon_startup.options |= CINIT_OPTION_WAIT_FOR_DISPLAY;

    CInitialise (&coolicon_startup);

/* create main window that will not be displayed (needed because the
   library does some important initialisation with the first window) */
    win = CDrawMainWindow ("dummy", "Nothing");

    if (!current_config_file)
	current_config_file = (char *) strdup (catstrs (home_dir, "/.coolicon", 0));
    config = load_config (current_config_file, 0);
    if (config) {		/* user config file is optional */
	draw_icons_from_config (config);
	free (config);
    } else {
	config = load_config (SYSTEM_ICONS, 0);
	if (!config) {
	    char s[256];
	    sprintf (s, _ (" Error trying to load system wide icon config file `%s' "), SYSTEM_ICONS);
	    CErrorDialog (CRoot, 20, 20, _ (" Load Config "), get_sys_error (s));
	    exit (1);
	}
	draw_icons_from_config (config);
	free (config);
    }

    set_signals ();
    CAddCallback ("AlarmCallback", object_update);

/* Run the application. */
    while (!done) {
	CNextEvent (&xevent, &cwevent);
	if (xevent.type == AlarmEvent) {
	    if (cursor_revert_timeout <= 0 && cursor_revert_window) {
		edit_tri_cursor (cursor_revert_window);
		cursor_revert_window = 0;
	    } else {
		cursor_revert_timeout--;
	    }
	}
	if (CDndClass->stage == XDND_DROP_STAGE_IDLE && drop.data) {
	    CWidget *w;
	    w = CIdent (drop.ident);
	    if (w)
		execute_icon_drop (w, (CPowerIcon *) w->user, (char *) drop.data, drop.size, drop.atom_name);
	    free (drop.data);
	    free (drop.ident);
	    free (drop.atom_name);
	    memset (&drop, 0, sizeof (drop));
	}
    }
    return 0;
}

