/*
 * 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.
 *
 * See the COPYING file for license information.
 *
 * Guillaume Chazarain <guichaz@yahoo.fr>
 */

/*****************************
 * Change or hide the cursor *
 *****************************/

#include "gliv.h"
#include "cursors.h"
#include "options.h"
#include "all_cursors.h"

extern rt_struct *rt;
extern options_struct *options;
extern GtkWidget *gl_widget;

#define NB_CURSORS 6

/* Must be kept in sync with init_cursors(). */
typedef enum {
    CURSOR_DEFAULT = 0,
    CURSOR_MOVE,
    CURSOR_ROTATE,
    CURSOR_ZOOM,
    CURSOR_ZOOM_ROTATE,
    CURSOR_RECT_ZOOM
} cursor_type;

static cursor_type current_type = CURSOR_DEFAULT;
static GdkCursor **cursors = NULL;
static gint hide_cursor_disablers = 0;

typedef struct {
    gint width;
    gint height;
    gint x_hot;
    gint y_hot;
    guchar *bits;
} xbm_data;

#define MAKE_CURSOR(name)                                                   \
    do {                                                                    \
        source = gdk_bitmap_create_from_data(NULL, cursor_##name##_bits,    \
                                             cursor_##name##_width,         \
                                             cursor_##name##_height);       \
                                                                            \
        mask = gdk_bitmap_create_from_data(NULL, cursor_##name##_mask_bits, \
                                          cursor_##name##_mask_width,       \
                                          cursor_##name##_mask_height);     \
                                                                            \
        *fill = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg,          \
                                            cursor_##name##_x_hot,          \
                                            cursor_##name##_y_hot);         \
        gdk_pixmap_unref(source);                                           \
        gdk_pixmap_unref(mask);                                             \
        fill++;                                                             \
    } while (0)

static void init_cursors(void)
{
    GdkPixmap *source, *mask;
    GdkColor fg = { 0, 0, 0, 0 };       /* Black */
    GdkColor bg = { 65535, 65535, 65535, 65535 };       /* White */
    GdkCursor **fill;

    cursors = fill = g_new(GdkCursor *, NB_CURSORS);

    *fill++ = NULL;
    MAKE_CURSOR(move);
    MAKE_CURSOR(rotate);
    MAKE_CURSOR(zoom);
    MAKE_CURSOR(zoom_rotate);
    MAKE_CURSOR(rect_zoom);
}

/* Called by the timer or the menu. */
gboolean hide_cursor(void)
{
    static GdkCursor *hidden_cursor = NULL;

    if (hidden_cursor == NULL) {
        /* First time. */
        GdkPixmap *pmap;
        GdkColor *col;
        gchar *data;

        data = g_new0(gchar, 1);
        col = g_new0(GdkColor, 1);
        pmap = gdk_bitmap_create_from_data(NULL, data, 1, 1);
        hidden_cursor = gdk_cursor_new_from_pixmap(pmap, pmap, col, col, 0, 0);

        g_free(data);
        g_free(col);
        g_object_unref(pmap);

        gtk_widget_add_events(gl_widget, GDK_POINTER_MOTION_MASK);
    }

    gdk_window_set_cursor(gl_widget->window, hidden_cursor);
    rt->cursor_hidden = TRUE;
    return FALSE;
}

void show_cursor(void)
{
    if (cursors == NULL)
        init_cursors();

    rt->cursor_hidden = FALSE;
    gdk_window_set_cursor(gl_widget->window, cursors[current_type]);
    schedule_hide_cursor();
}

/* Set the appropriate cursor for the current situation. */
gboolean set_correct_cursor(void)
{
    GdkModifierType state;
    cursor_type new_cursor;

    if (cursors == NULL)
        init_cursors();

    gdk_display_get_pointer(gdk_display_get_default(), NULL, NULL, NULL,
                            &state);

    if (state & GDK_BUTTON3_MASK && (state & GDK_BUTTON1_MASK) == 0)
        new_cursor = CURSOR_RECT_ZOOM;

    else if (state & GDK_SHIFT_MASK) {
        if (state & GDK_CONTROL_MASK)
            new_cursor = CURSOR_ZOOM_ROTATE;
        else
            new_cursor = CURSOR_ZOOM;

    } else if (state & GDK_CONTROL_MASK)
        new_cursor = CURSOR_ROTATE;

    else if (state & GDK_BUTTON1_MASK)
        new_cursor = CURSOR_MOVE;

    else
        new_cursor = CURSOR_DEFAULT;

    if (new_cursor != current_type) {
        current_type = new_cursor;
        if (rt->cursor_hidden == FALSE)
            gdk_window_set_cursor(gl_widget->window, cursors[current_type]);
    }

    return FALSE;
}

/*
 * Used to prevent the cursor from being toggled automatically,
 * this is useful when displaying dialogs.
 */
void set_hide_cursor_enabled(gboolean enabled)
{
    gint previous = hide_cursor_disablers;

    hide_cursor_disablers += enabled ? -1 : 1;
    if (hide_cursor_disablers < 0)
        hide_cursor_disablers = 0;

    if ((hide_cursor_disablers > 0) == (previous > 0))
        /* Nothing to do. */
        return;

    if (hide_cursor_disablers > 0 && rt->cursor_hidden)
        show_cursor();

    schedule_hide_cursor();
}

void schedule_hide_cursor(void)
{
    static guint id = 0;

    if (id != 0)
        /* Remove previous schedule. */
        g_source_remove(id);

    if (options->delay == 0 || hide_cursor_disablers > 0)
        id = 0;
    else
        id = g_timeout_add(options->delay, (GSourceFunc) hide_cursor, NULL);
}
