#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "tlist.h"
#include "edv_list_cb.h"
#include "cfg.h"
#include "edv_cfg_list.h"
#include "imbr.h"
#include "endeavour2.h"
#include "edv_utils_gtk.h"
#include "config.h"


gint EDVCListKeyEventCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
);
gint EDVCListButtonEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
);
gint EDVCListMotionEventCB(
	GtkWidget *widget, GdkEventMotion *motion, gpointer data
);

gint EDVTListButtonEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
);
gint EDVTListMotionEventCB(
	GtkWidget *widget, GdkEventMotion *motion, gpointer data
);



#ifndef GDK_BUTTON1
# define GDK_BUTTON1	1
#endif
#ifndef GDK_BUTTON2
# define GDK_BUTTON2	2
#endif
#ifndef GDK_BUTTON3
# define GDK_BUTTON3	3
#endif


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)

static gboolean	do_scroll;
static gint	pointer_x_last,
		pointer_y_last;


/*
 *	GtkCList "key_press_event" or "key_release_event" signal
 *	callback.
 */
gint EDVCListKeyEventCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
)
{
	gint status = FALSE;
	gboolean press;
	guint state;
	GtkSelectionMode selection_mode;
	GtkCList *clist = (GtkCList *)widget;
	edv_core_struct *core = EDV_CORE(data);
	if((clist == NULL) || (key == NULL) || (core == NULL))
	    return(status);

#define SIGNAL_EMIT_STOP	{		\
 gtk_signal_emit_stop_by_name(			\
  GTK_OBJECT(widget),				\
  press ?					\
   "key_press_event" : "key_release_event"	\
 );						\
}

#define DO_ADJ_CLAMP_EMIT	{		\
 if(adj->value > (adj->upper - adj->page_size))	\
  adj->value = adj->upper - adj->page_size;	\
 if(adj->value < adj->lower)                    \
  adj->value = adj->lower;                      \
 gtk_signal_emit_by_name(                       \
  GTK_OBJECT(adj), "value_changed"              \
 );                                             \
}

#define DO_CLIST_FOCUS_ROW_CLAMP        {       \
 if(clist->focus_row >= clist->rows)            \
  clist->focus_row = clist->rows - 1;           \
 if(clist->focus_row < 0)                       \
  clist->focus_row = 0;                         \
}

	selection_mode = clist->selection_mode;
	press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE;
	state = key->state;

	switch(key->keyval)
	{
	  case GDK_Up:
	  case GDK_KP_Up:
	    if(state & GDK_CONTROL_MASK)
	    {
		/* Get adjustment and scroll up */
		GtkAdjustment *adj = clist->vadjustment;
		if((adj != NULL) && press)
		{
		    gtk_clist_freeze(clist);
		    adj->value -= adj->step_increment;
		    DO_ADJ_CLAMP_EMIT
		    gtk_clist_thaw(clist);
		}
	    }
	    else
	    {
		if(press)
		{
		    gint prev_focus_row = clist->focus_row;

		    gtk_clist_freeze(clist);

		    clist->focus_row--;
		    DO_CLIST_FOCUS_ROW_CLAMP

		    if(gtk_clist_row_is_visible(
			clist, clist->focus_row) != GTK_VISIBILITY_FULL
		    )
			gtk_clist_moveto(
			    clist,
			    clist->focus_row, -1,	/* Row, column */
			    0.0f, 0.0f			/* Row, column */
			);

		    /* Select rows in between? */
		    if((selection_mode == GTK_SELECTION_MULTIPLE) ||
		       (selection_mode == GTK_SELECTION_EXTENDED)
		    )
		    {
			if(state & GDK_SHIFT_MASK)
			{
			    gtk_clist_select_row(
				clist, prev_focus_row, 0
			    );
			    gtk_clist_select_row(
				clist, clist->focus_row, 0
			    );
			}
		    }

		    gtk_clist_thaw(clist);
		}
	    }
	    SIGNAL_EMIT_STOP
	    status = TRUE;
	    break;

	  case GDK_Down:
	  case GDK_KP_Down:
	    if(state & GDK_CONTROL_MASK)
	    {
		/* Get adjustment and scroll down */
		GtkAdjustment *adj = clist->vadjustment;
		if((adj != NULL) && press)
		{
		    gtk_clist_freeze(clist);
		    adj->value += adj->step_increment;
		    DO_ADJ_CLAMP_EMIT
		    gtk_clist_thaw(clist);
		}
	    }
	    else
	    {
		GtkAdjustment *adj = clist->hadjustment;
		if((adj != NULL) && press)
		{
		    gint prev_focus_row = clist->focus_row;

		    gtk_clist_freeze(clist);

		    clist->focus_row++;
		    DO_CLIST_FOCUS_ROW_CLAMP

		    if(gtk_clist_row_is_visible(
			clist, clist->focus_row) != GTK_VISIBILITY_FULL
		    )
			gtk_clist_moveto(
			    clist,
			    clist->focus_row, -1,	/* Row, column */
			    1.0f, 0.0f			/* Row, column */
			);

		    /* Select rows in between? */
		    if((selection_mode == GTK_SELECTION_MULTIPLE) ||
		       (selection_mode == GTK_SELECTION_EXTENDED)
		    )
		    {
			if(state & GDK_SHIFT_MASK)
			{
			    gtk_clist_select_row(
				clist, prev_focus_row, 0
			    );
			    gtk_clist_select_row(
				clist, clist->focus_row, 0
			    );
			}
		    }

		    gtk_clist_thaw(clist);
		}
	    }
	    SIGNAL_EMIT_STOP
	    status = TRUE;
	    break;

	  case GDK_Left:
	  case GDK_KP_Left:
	    if(TRUE)
	    {
		/* Get adjustment and scroll left */
		GtkAdjustment *adj = clist->hadjustment;
		if((adj != NULL) && press)
		{
		    gtk_clist_freeze(clist);

		    if(state & GDK_CONTROL_MASK)
			adj->value -= adj->step_increment * 4;
		    else if(state & GDK_SHIFT_MASK)
			adj->value = adj->lower;
		    else
			adj->value -= adj->step_increment;
		    DO_ADJ_CLAMP_EMIT

		    gtk_clist_thaw(clist);
		}
	    }
	    SIGNAL_EMIT_STOP
	    status = TRUE;
	    break;

	  case GDK_Right:
	  case GDK_KP_Right:
	    if(TRUE)
	    {
		/* Get adjustment and scroll right */
		GtkAdjustment *adj = clist->hadjustment;
		if((adj != NULL) && press)
		{
		    gtk_clist_freeze(clist);

		    if(state & GDK_CONTROL_MASK)
			adj->value += adj->step_increment * 4;
		    else if(state & GDK_SHIFT_MASK)
			adj->value = adj->upper - adj->page_size;
		    else
			adj->value += adj->step_increment;
		    DO_ADJ_CLAMP_EMIT

		    gtk_clist_thaw(clist);
		}
	    }
	    SIGNAL_EMIT_STOP
	    status = TRUE;
	    break;

	  case GDK_Page_Up:
	  case GDK_KP_Page_Up:
	    if(state & GDK_CONTROL_MASK)
	    {
		/* Scroll up one page step */
		GtkAdjustment *adj = clist->vadjustment;
		if((adj != NULL) && press)
		{
		    gtk_clist_freeze(clist);

		    adj->value -= adj->page_increment;
		    DO_ADJ_CLAMP_EMIT

		    gtk_clist_thaw(clist);
		}
	    }
	    else
	    {
		/* Move focus row up one page step */
		gint row_height = (GTK_CLIST_ROW_HEIGHT_SET(clist) ?
		    clist->row_height : EDV_LIST_ROW_SPACING) + 1;
		const GtkAdjustment *adj = clist->vadjustment;
		if(press && (row_height > 0) && (adj != NULL))
		{
		    gint prev_focus_row = clist->focus_row;

		    gtk_clist_freeze(clist);

		    clist->focus_row -= (gint)MAX(
			(adj->page_increment / row_height), 1.0f
		    );
		    DO_CLIST_FOCUS_ROW_CLAMP

		    if(gtk_clist_row_is_visible(
			clist, clist->focus_row) != GTK_VISIBILITY_FULL
		    )
			gtk_clist_moveto(
			    clist,
			    clist->focus_row, -1,	/* Row, column */
			    0.0f, 0.0f			/* Row, column */
			);

		    /* Select rows in between? */
		    if((selection_mode == GTK_SELECTION_MULTIPLE) ||
		       (selection_mode == GTK_SELECTION_EXTENDED)
		    )
		    {
			if(state & GDK_SHIFT_MASK)
			{
			    gint i;
			    for(i = MAX(prev_focus_row, clist->focus_row);
				i >= clist->focus_row;
				i--
			    )
				gtk_clist_select_row(
				    clist, i, 0
				);
			}
		    }

		    gtk_clist_thaw(clist);
		}
	    }
	    SIGNAL_EMIT_STOP
	    status = TRUE;
	    break;

	  case GDK_Page_Down:
	  case GDK_KP_Page_Down:
	    if(state & GDK_CONTROL_MASK)
	    {
		/* Scroll down one page step */
		GtkAdjustment *adj = clist->vadjustment;
		if((adj != NULL) && press)
		{
		    gtk_clist_freeze(clist);

		    adj->value += adj->page_increment;
		    DO_ADJ_CLAMP_EMIT

		    gtk_clist_thaw(clist);
		}
	    }
	    else
	    {
		/* Move focus row down one page step */
		gint row_height = (GTK_CLIST_ROW_HEIGHT_SET(clist) ?
		    clist->row_height : EDV_LIST_ROW_SPACING) + 1;
		const GtkAdjustment *adj = clist->vadjustment;
		if(press && (row_height > 0) && (adj != NULL))
		{
		    gint prev_focus_row = clist->focus_row;

		    gtk_clist_freeze(clist);

		    clist->focus_row += (gint)MAX(
			(adj->page_increment / row_height), 1.0f
		    );
		    DO_CLIST_FOCUS_ROW_CLAMP

		    if(gtk_clist_row_is_visible(
			clist, clist->focus_row) != GTK_VISIBILITY_FULL
		    )
			gtk_clist_moveto(
			    clist,
			    clist->focus_row, -1,	/* Row, column */
			    1.0f, 0.0f			/* Row, column */
			);

		    /* Select rows in between? */
		    if((selection_mode == GTK_SELECTION_MULTIPLE) ||
		       (selection_mode == GTK_SELECTION_EXTENDED)
		    )
		    {
			if(state & GDK_SHIFT_MASK)
			{
			    gint i;
			    for(i = MIN(prev_focus_row, clist->focus_row);
				i <= clist->focus_row;
				i++
			    )
				gtk_clist_select_row(
				    clist, i, 0
				);
			}
		    }

		    gtk_clist_thaw(clist);
		}
	    }
	    SIGNAL_EMIT_STOP
	    status = TRUE;
	    break;

	  case GDK_Home:
	  case GDK_KP_Home:
	    if(state & GDK_CONTROL_MASK)
	    {
		/* Scroll to top */
		GtkAdjustment *adj = clist->vadjustment;
		if((adj != NULL) && press)
		{
		    gtk_clist_freeze(clist);

		    adj->value = adj->lower;
		    DO_ADJ_CLAMP_EMIT

		    gtk_clist_thaw(clist);
		}
	    }
	    else
	    {
		/* Move focus row to top */
		if(press)
		{
		    gint prev_focus_row = clist->focus_row;

		    gtk_clist_freeze(clist);

		    clist->focus_row = 0;
		    DO_CLIST_FOCUS_ROW_CLAMP

		    if(gtk_clist_row_is_visible(
			clist, clist->focus_row) != GTK_VISIBILITY_FULL
		    )
			gtk_clist_moveto(
			    clist,
			    clist->focus_row, -1,	/* Row, column */
			    0.0f, 0.0f			/* Row, column */
			);

		    /* Select rows in between? */
		    if((selection_mode == GTK_SELECTION_MULTIPLE) ||
		       (selection_mode == GTK_SELECTION_EXTENDED)
		    )
		    {
			if(state & GDK_SHIFT_MASK)
			{
			    gint i;
			    for(i = MAX(prev_focus_row, clist->focus_row);
				i >= clist->focus_row;
				i--
			    )
				gtk_clist_select_row(
				    clist, i, 0
				);
			}
		    }

		    gtk_clist_thaw(clist);
		}
	    }
	    SIGNAL_EMIT_STOP
	    status = TRUE;
	    break;

	  case GDK_End:
	  case GDK_KP_End:
	    if(state & GDK_CONTROL_MASK)
	    {
		/* Scroll to bottom */
		GtkAdjustment *adj = clist->vadjustment;
		if((adj != NULL) && press)
		{
		    gtk_clist_freeze(clist);

		    adj->value = adj->upper - adj->page_size;
		    DO_ADJ_CLAMP_EMIT

		    gtk_clist_thaw(clist);
		}
	    }
	    else
	    {
		/* Move focus row to top */
		if(press)
		{
		    gint prev_focus_row = clist->focus_row;

		    gtk_clist_freeze(clist);

		    clist->focus_row = clist->rows - 1;
		    DO_CLIST_FOCUS_ROW_CLAMP

		    if(gtk_clist_row_is_visible(
			clist, clist->focus_row) != GTK_VISIBILITY_FULL
		    )
			gtk_clist_moveto(
			    clist,
			    clist->focus_row, -1,	/* Row, column */
			    1.0f, 0.0f			/* Row, column */
			);

		    /* Select rows in between? */
		    if((selection_mode == GTK_SELECTION_MULTIPLE) ||
		       (selection_mode == GTK_SELECTION_EXTENDED)
		    )
		    {
			if(state & GDK_SHIFT_MASK)
			{
			    gint i;
			    for(i = MIN(prev_focus_row, clist->focus_row);
				i <= clist->focus_row;
				i++
			    )
				gtk_clist_select_row(
				    clist, i, 0
				);
			}
		    }

		    gtk_clist_thaw(clist);
		}
	    }
	    SIGNAL_EMIT_STOP
	    status = TRUE;
	    break;
	}

	return(status);
#undef DO_CLIST_FOCUS_ROW_CLAMP
#undef DO_ADJ_CLAMP_EMIT
#undef SIGNAL_EMIT_STOP
}

/*
 *	GtkCList "button_press_event" or "button_release_event" signal
 *	callback.
 */
gint EDVCListButtonEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
	gint status = FALSE;
	GtkCList *clist = (GtkCList *)widget;
	const cfg_item_struct *cfg_list;
	edv_core_struct *core = EDV_CORE(data);
	if((clist == NULL) || (button == NULL) || (core == NULL))
	    return(status);

	cfg_list = core->cfg_list;

#define SIGNAL_EMIT_STOP	{		\
 gtk_signal_emit_stop_by_name(			\
  GTK_OBJECT(widget),				\
  (button->type == GDK_BUTTON_PRESS) ?		\
   "button_press_event" : "button_release_event"\
 );						\
}

	switch((gint)button->type)
	{
	  case GDK_BUTTON_PRESS:
	    switch(button->button)
	    {
	      case GDK_BUTTON2:
		/* Options specify middle click scrolls lists? */
		if(CFGItemListGetValueI(
		    cfg_list, EDV_CFG_PARM_LISTS_MIDDLE_CLICK_SCROLL
		))
		{
		    gint x, y;
		    GdkModifierType mask;
		    GdkCursor *cursor = EDVGetCursor(
			core, EDV_CURSOR_CODE_TRANSLATE
		    );

		    gdk_window_get_pointer(
			button->window, &x, &y, &mask
		    );

		    gdk_pointer_grab(
			button->window,
			FALSE,
			GDK_BUTTON_PRESS_MASK |
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_POINTER_MOTION_MASK |
			    GDK_POINTER_MOTION_HINT_MASK,
			NULL,
			cursor,
			button->time
		    );

		    do_scroll = TRUE;
		    pointer_x_last = x;
		    pointer_y_last = y;

		    SIGNAL_EMIT_STOP
		    status = TRUE;
		}
		break;
	    }
	    break;

	  case GDK_BUTTON_RELEASE:
	    switch(button->button)
	    {
	      case GDK_BUTTON2:
		/* Options specify middle click scrolls lists? */
		if(CFGItemListGetValueI(
		    cfg_list, EDV_CFG_PARM_LISTS_MIDDLE_CLICK_SCROLL
		))
		{
		    gdk_pointer_ungrab(button->time);

		    do_scroll = FALSE;
		    pointer_x_last = 0;
		    pointer_y_last = 0;

		    SIGNAL_EMIT_STOP
		    status = TRUE;
		}
		break;
	    }
	    break;
	}

	return(status);
#undef SIGNAL_EMIT_STOP
}

/*
 *	GtkCList "motion_notify_event" signal callback.
 */
gint EDVCListMotionEventCB(
	GtkWidget *widget, GdkEventMotion *motion, gpointer data
)
{
	gint status = FALSE;
	GtkCList *clist = (GtkCList *)widget;
	const cfg_item_struct *cfg_list;
	edv_core_struct *core = EDV_CORE(data);
	if((clist == NULL) || (motion == NULL) || (core == NULL))
	    return(status);

	cfg_list = core->cfg_list;

#define SIGNAL_EMIT_STOP	{		\
 gtk_signal_emit_stop_by_name(			\
  GTK_OBJECT(widget), "motion_notify_event"	\
 );						\
}

#define DO_SCROLL(dx,dy)	{		\
 GtkAdjustment *adj = clist->hadjustment;	\
 if((adj != NULL) && ((dx) != 0)) {		\
  if((adj->upper - adj->page_size) > adj->lower) { \
   adj->value = CLIP(				\
    adj->value - (dx),				\
    adj->lower, adj->upper - adj->page_size	\
   );						\
   gtk_signal_emit_by_name(			\
    GTK_OBJECT(adj), "value_changed"		\
   );						\
  }						\
 }						\
 adj = clist->vadjustment;			\
 if((adj != NULL) && ((dy) != 0)) {		\
  if((adj->upper - adj->page_size) > adj->lower) { \
   adj->value = CLIP(				\
    adj->value - (dy),				\
    adj->lower, adj->upper - adj->page_size	\
   );						\
   gtk_signal_emit_by_name(			\
    GTK_OBJECT(adj), "value_changed"		\
   );						\
  }						\
 }						\
}

	switch((gint)motion->type)
	{
	  case GDK_MOTION_NOTIFY:
	    if(do_scroll)
	    {
		gint dx, dy;

		if(motion->is_hint)
		{
		    gint x, y;
		    GdkModifierType mask;
		    gdk_window_get_pointer(
			motion->window, &x, &y, &mask
		    );
		    dx = x - pointer_x_last;
		    dy = y - pointer_y_last;
		    pointer_x_last = x;
		    pointer_y_last = y;
		}
		else
		{
		    dx = (gint)(motion->x - pointer_x_last);
		    dy = (gint)(motion->y - pointer_y_last);
		    pointer_x_last = (gint)motion->x;
		    pointer_y_last = (gint)motion->y;
		}

		DO_SCROLL(dx, dy);

		SIGNAL_EMIT_STOP
		status = TRUE;
	    }
	    break;
	}

	return(status);
#undef DO_SCROLL
#undef SIGNAL_EMIT_STOP
}


/*
 *	Thumbs List GtkDrawingArea "button_press_event" or
 *	"button_release_event" signal callback.
 */
gint EDVTListButtonEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
	gint status = FALSE;
	tlist_struct *tlist;
	const cfg_item_struct *cfg_list;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	edv_core_struct *core;
	if((widget == NULL) || (button == NULL) || (imbr == NULL))
	    return(status);

	core = EDV_CORE(imbr->core);
	tlist = imbr->tlist;
	if((core == NULL) || (tlist == NULL))
	    return(status);

	cfg_list = core->cfg_list;

#define SIGNAL_EMIT_STOP        {               \
 gtk_signal_emit_stop_by_name(                  \
  GTK_OBJECT(widget),                           \
  (button->type == GDK_BUTTON_PRESS) ?          \
   "button_press_event" : "button_release_event"\
 );                                             \
}

	switch((gint)button->type)
	{
	  case GDK_BUTTON_PRESS:
	    switch(button->button)
	    {
	      case GDK_BUTTON2:
		/* Options specify middle click scrolls lists? */
		if(EDV_GET_B(EDV_CFG_PARM_LISTS_MIDDLE_CLICK_SCROLL))
		{
		    gint x, y;
		    GdkModifierType mask;
		    GdkCursor *cursor = EDVGetCursor(
			core, EDV_CURSOR_CODE_TRANSLATE
		    );

		    gdk_window_get_pointer(
			button->window, &x, &y, &mask
		    );

		    gdk_pointer_grab(
			button->window,
			FALSE,
			GDK_BUTTON_PRESS_MASK |
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_POINTER_MOTION_MASK |
			    GDK_POINTER_MOTION_HINT_MASK,
			NULL,
			cursor,
			button->time
		    );

		    do_scroll = TRUE;
		    pointer_x_last = x;
		    pointer_y_last = y;

		    SIGNAL_EMIT_STOP
		    status = TRUE;
		}
		break;
	    }
	    break;

	  case GDK_BUTTON_RELEASE:
	    switch(button->button)
	    {
	      case GDK_BUTTON2:
		/* Options specify middle click scrolls lists? */
		if(EDV_GET_B(EDV_CFG_PARM_LISTS_MIDDLE_CLICK_SCROLL))
		{
		    gdk_pointer_ungrab(button->time);

		    do_scroll = FALSE;
		    pointer_x_last = 0;
		    pointer_y_last = 0;

		    SIGNAL_EMIT_STOP
		    status = TRUE;
		}
		break;
	    }
	    break;
	}

	return(status);
#undef SIGNAL_EMIT_STOP
}

/*
 *	Thumbs List GtkDrawingArea "motion_notify_event" signal
 *	callback.
 */
gint EDVTListMotionEventCB(
	GtkWidget *widget, GdkEventMotion *motion, gpointer data
)
{
	gint status = FALSE;
	tlist_struct *tlist;
	const cfg_item_struct *cfg_list;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	edv_core_struct *core;
	if((widget == NULL) || (motion == NULL) || (imbr == NULL))
	    return(status);

	core = EDV_CORE(imbr->core);
	tlist = imbr->tlist;
	if((core == NULL) || (tlist == NULL))
	    return(status);

	cfg_list = core->cfg_list;

#define SIGNAL_EMIT_STOP        {               \
 gtk_signal_emit_stop_by_name(                  \
  GTK_OBJECT(widget), "motion_notify_event"     \
 );                                             \
}

#define DO_SCROLL(dx,dy)        {               \
 if(tlist->flags & TLIST_HORIZONTAL) {		\
  GtkAdjustment *adj = tlist->hadj;		\
  if((adj != NULL) && ((dx) != 0)) {		\
   if((adj->upper - adj->page_size) > adj->lower) { \
    adj->value = CLIP(				\
     adj->value - (dx),				\
     adj->lower, adj->upper - adj->page_size	\
    );						\
    gtk_signal_emit_by_name(			\
     GTK_OBJECT(adj), "value_changed"		\
    );						\
   }						\
  }						\
 } else {					\
  GtkAdjustment *adj = tlist->vadj;		\
  if((adj != NULL) && ((dy) != 0)) {		\
   if((adj->upper - adj->page_size) > adj->lower) { \
    adj->value = CLIP(				\
     adj->value - (dy),				\
     adj->lower, adj->upper - adj->page_size	\
    );						\
    gtk_signal_emit_by_name(			\
     GTK_OBJECT(adj), "value_changed"		\
    );						\
   }						\
  }						\
 }						\
}

	switch((gint)motion->type)
	{
	  case GDK_MOTION_NOTIFY:
	    if(do_scroll)
	    {
		gint dx, dy;

		if(motion->is_hint)
		{
		    gint x, y;
		    GdkModifierType mask;
		    gdk_window_get_pointer(
			motion->window, &x, &y, &mask
		    );
		    dx = x - pointer_x_last;
		    dy = y - pointer_y_last;
		    pointer_x_last = x;
		    pointer_y_last = y;
		}
		else
		{
		    dx = (gint)(motion->x - pointer_x_last);
		    dy = (gint)(motion->y - pointer_y_last);
		    pointer_x_last = (gint)motion->x;
		    pointer_y_last = (gint)motion->y;
		}

		DO_SCROLL(dx, dy);

		SIGNAL_EMIT_STOP
		status = TRUE;
	    }
	    break;
	}

	return(status);
#undef DO_SCROLL
#undef SIGNAL_EMIT_STOP
}
