/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

# include <stdio.h>

# include <X11/Intrinsic.h>
# include <X11/IntrinsicP.h>
# include <X11/StringDefs.h>
# include <X11/CoreP.h>
# include <X11/CompositeP.h>
# include <X11/ConstrainP.h>
# include <Xm/XmP.h>
# include <Xm/DrawingAP.h>
#include <Xm/TransltnsP.h>

# include      "Paper.h"
# include      "PaperP.h"

# ifndef MAX
#   define   MAX(a,b) ((a) > (b) ? (a) : (b))
# endif
# ifndef MIN
#   define   MIN(a,b) ((a) < (b) ? (a) : (b))
# endif
# define ABS(a) ( (a)>0?(a):-(a))

# define MINIMUM 0.01 /* 1% */
# define EQUAL(a,b)  ( ABS(a-b) < (MINIMUM/100.0))

static void ClassInitialize();
static void Initialize();
static void	ConstraintInitialize();
static		Boolean ConstraintSetValues();
static void	Destroy();
static void	Resize();
static		Boolean SetValues();
static		XtGeometryResult GeometryManager();
static void	ChangeManaged();
static void	InsertChild();
static void	DeleteChild();
static void	Redisplay();
static void	StartDrag();
static void	Drag();
static void	Join();
static void	Split();
static void	Inset();
static void	Group();
static void	Ungroup();
static void	EndDrag();
static void	SelectAll();
static void	SelectNone();
static void	Insert();
static void	Delete();


static void	start_edit(PaperWidget widget);
static void	end_edit(PaperWidget widget, Boolean cancel);
static void	edit_handler(Widget w, XtPointer cd, XEvent *e, Boolean
*continue_dispatch);
static void	size_widgets(PaperWidget widget, Widget w);
static void	place(PaperWidget tw, Widget w);
static XtResource resources[] = {
	{XmNeditable,XmCEditable,XtRBoolean,sizeof(Boolean), 
	XtOffset(PaperWidget, paper.editable), 
	XtRImmediate, (XtPointer)True },
	{ "white", "White", XmRPixel, sizeof(Pixel), 
	XtOffset(PaperWidget, paper.white), XmRString, "white"},
	{ XmNforeground, XmCForeground, XmRPixel, sizeof(Pixel), 
	XtOffset(PaperWidget, paper.foreground), XmRString, "black"},
	{
	"selected", "Selected", XmRPixel, sizeof(Pixel), 
	XtOffset(PaperWidget, paper.selected), XmRString, "red"},
	{
	"dash", "Dash", XmRPixel, sizeof(Pixel), 
	XtOffset(PaperWidget, paper.dash), XmRString, "blue"},
	{XmNjoinCallback,XtCCallback,XtRCallback,sizeof(caddr_t),
	XtOffset (PaperWidget, paper.join_cb),XtRCallback,NULL},
	{XmNsplitCallback,XtCCallback,XtRCallback,sizeof(caddr_t),
	XtOffset (PaperWidget, paper.split_cb),XtRCallback,NULL},
	{XmNgroupCallback,XtCCallback,XtRCallback,sizeof(caddr_t),
	XtOffset (PaperWidget, paper.group_cb),XtRCallback,NULL},
	{XmNungroupCallback,XtCCallback,XtRCallback,sizeof(caddr_t),
	XtOffset (PaperWidget, paper.ungroup_cb),XtRCallback,NULL},
	{XmNdeleteCallback,XtCCallback,XtRCallback,sizeof(caddr_t),
	XtOffset (PaperWidget, paper.delete_cb),XtRCallback,NULL},
	{XmNdrawCallback,XtCCallback,XtRCallback,sizeof(caddr_t),
	XtOffset (PaperWidget, paper.draw_cb),XtRCallback,NULL},
	{XmNdblClickCallback,XtCCallback,XtRCallback,sizeof(caddr_t),
	XtOffset (PaperWidget, paper.dblclick_cb),XtRCallback,NULL},
	{XmNchangeCallback,XtCCallback,XtRCallback,sizeof(caddr_t),
	XtOffset (PaperWidget, paper.change_cb),XtRCallback,NULL},
	{ XmNfontList, XmCFontList, XmRFontList, sizeof (XmFontList),
	XtOffset (PaperWidget, paper.graphics.font), XmRString, "fixed"},

	{ "gridColor", "GridColor", XmRPixel, sizeof(Pixel), 
	XtOffset(PaperWidget, paper.grid_color), XmRString, "LightGray"},

};
static char defaultTranslations[] = 
"<Btn1Down>:start_drag()\n\
<Btn1Motion>:drag()\n\
<Btn1Up>:end_drag()\n\
<Key>osfDelete:delete()\n\
<Key>osfBackSpace:delete()\n\
<Key>osfInsert:insert()\n\
Ctrl<Key>i:inset()\n\
Ctrl<Key>t:align_top()\n\
Ctrl<Key>l:align_left()\n\
Ctrl<Key>r:align_right()\n\
Ctrl<Key>b:align_bottom()\n\
Ctrl<Key>h:distribute_h()\n\
Ctrl<Key>v:distribute_v()\n\
Ctrl<Key>a:select_all()\n\
Ctrl<Key>n:select_none()\n\
Ctrl<Key>j:join()\n\
Ctrl<Key>g:group()\n\
Ctrl<Key>u:ungroup()\n\
Ctrl<Key>2:split(2,2)\n\
Ctrl<Key>3:split(3,3)\n\
Ctrl<Key>4:split(4,4)\n\
Ctrl<Key>5:split(5,5)\n\
Ctrl<Key>6:split(6,6)\n\
Ctrl<Key>7:split(7,7)\n\
Ctrl<Key>8:split(8,8)\n\
Ctrl<Key>9:split(9,9)\n\
<EnterWindow>:ManagerEnter()\n\
<LeaveWindow>:ManagerLeave()\n\
<FocusOut>:ManagerFocusOut()\n\
<FocusIn>:ManagerFocusIn()\n\
<Key>osfHelp: ManagerGadgetHelp()";

static XtActionsRec actionsList[] = {
	{ "align_top", (XtActionProc) PaperAlignTop},
	{ "align_bottom", (XtActionProc) PaperAlignBottom},
	{ "align_left", (XtActionProc) PaperAlignLeft},
	{ "align_right", (XtActionProc) PaperAlignRight},
	{ "distribute_h", (XtActionProc) PaperDistributeHorizontally},
	{ "distribute_v", (XtActionProc) PaperDistributeVertically},
	{ "inset", (XtActionProc) Inset},
	{ "start_drag", (XtActionProc) StartDrag},
	{ "drag", (XtActionProc) Drag},
	{ "end_drag", (XtActionProc) EndDrag},
	{ "join", (XtActionProc) Join},
	{ "group", (XtActionProc) Group},
	{ "ungroup", (XtActionProc) Ungroup},
	{ "split", (XtActionProc) Split},
	{ "select_all", (XtActionProc) SelectAll},
	{ "select_none", (XtActionProc) SelectNone}, 
	{ "insert", (XtActionProc) Insert},
	{ "delete", (XtActionProc) Delete}, 
};

static XmNavigability nav(Widget w)
{
	printf("here.... \n");
	return XmTAB_NAVIGABLE;
}


static void HighlightBorder( Widget w )
{   
    PaperWidget pw = (PaperWidget) w ;

    _XmDrawSimpleHighlight( XtDisplay( pw), XtWindow( pw), 
		     pw->paper.gc, 0, 0, 
		     XtWidth( pw), XtHeight( pw),
		     2);
}

#if 0

static void UnhighlightBorder( Widget w )
{   
    PaperWidget pw = (PaperWidget) w ;

    if(XmIsManager (pw->core.parent))  {   
        _XmDrawSimpleHighlight( XtDisplay( pw), XtWindow( pw), 
			       ((XmManagerWidget)(pw->core.parent))
			       ->manager.background_GC,
			       0, 0, XtWidth( w), XtHeight( w),
			       2) ;
    } else 

	_XmClearBorder( XtDisplay (pw), XtWindow (pw), 0, 0, XtWidth( w),
		       XtHeight( w) , 2) ;
}

static void FocusChange( Widget wid, XmFocusChange change)
{
	switch(change)
	{
		case XmENTER:
		case XmFOCUS_IN:
			HighlightBorder(wid);
			break;

		case XmLEAVE:
		case XmFOCUS_OUT:
			UnhighlightBorder(wid);
			break;
	}
}

#endif

static XmBaseClassExtRec BaseClassExtRec = {
    NULL,
    NULLQUARK,
    XmBaseClassExtVersion,
    sizeof(XmBaseClassExtRec),
    NULL,               /* InitializePrehook    */
    NULL,               /* SetValuesPrehook */
    NULL,               /* InitializePosthook   */
    NULL,               /* SetValuesPosthook    */
    NULL,               /* secondaryObjectClass */
    NULL,               /* secondaryCreate  */
    NULL,                       /* getSecRes data   */
    { 0 },              /* fastSubclass flags   */
    NULL,               /* get_values_prehook   */
    NULL,               /* get_values_posthook  */
    NULL,                               /* classPartInitPrehook */
    NULL,                               /* classPartInitPosthook*/
    NULL,                               /* ext_resources        */
    NULL,                               /* compiled_ext_resources*/
    0,                                  /* num_ext_resources    */
    FALSE,                              /* use_sub_resources    */
    XmInheritWidgetNavigable,           /* widgetNavigable      */
    XmInheritFocusChange,               /* focusChange          */
};



PaperClassRec paperClassRec = {
	{
	/* core_class fields  */
	(WidgetClass) &xmDrawingAreaClassRec,/* superclass         */
	"Paper",			/* class_name         */
	sizeof(PaperRec),		/* widget_size        */
	ClassInitialize,				/* class_init         */
	NULL,				/* class_part_init    */
	FALSE,				/* class_inited       */
	Initialize,			/* initialize         */
	NULL,				/* initialize_hook    */
	XtInheritRealize,		/* realize            */
	actionsList,			/* actions            */
	XtNumber(actionsList),		/* num_actions        */
	resources,			/* resources          */
	XtNumber(resources),		/* num_resources      */
	NULLQUARK,			/* xrm_class          */
	TRUE,				/* compress_motion    */
	XtExposeCompressMaximal,	/* compress_exposure  */
	TRUE,				/* compress_enterleave*/
	TRUE,				/* visible_interest   */
	Destroy,			/* destroy            */
	Resize,                         /* resize             */
	Redisplay,			/* expose             */
	SetValues,			/* set_values         */
	NULL,				/* set_values_hook    */
	XtInheritSetValuesAlmost,	/* set_values_almost  */
	NULL,				/* get_values_hook    */
	NULL,				/* accept_focus       */
	XtVersion,			/* version            */
	NULL,				/* callback_private   */
	defaultTranslations,		/* tm_table           */
	NULL,				/* query_geometry     */
	XtInheritDisplayAccelerator,	/* display_accelerator*/
	(XtPointer) &BaseClassExtRec,				/* extension          */
	},
	{
	/* composite_class fields */
	XtInheritGeometryManager,	/* geometry_manager    */
	XtInheritChangeManaged,         /* change_managed      */
	XtInheritInsertChild,		/* insert_child        */
	XtInheritDeleteChild,		/* delete_child        */
	NULL				/*&compext*/
	,				/* extension           */
	},
	{
	/* constraint_class fields */
	NULL,				/* subresources        */
	0,				/* subresource_count   */
	0,				/* constraint_size     */
	NULL,				/* initialize          */
	NULL,				/* destroy             */
	NULL,				/* set_values          */
	NULL,				/* extension           */
	},
	{
	_XmDrawingA_traversalTranslations,		/* default translations */
	NULL,				/* syn_resources          */
	0,				/* num_syn_resources      */
	NULL,				/* syn_cont_resources     */
	0,				/* num_syn_cont_resources */
	XmInheritParentProcess,         /* parent_process */
	NULL,				/* extension              */

	},
	/*  drawingArea class */
	{
	0,
	},
	{
	/* Paper class fields */
	0,				/* ignore              */
	}
};

WidgetClass	paperWidgetClass = (WidgetClass)& paperClassRec;
/*========================================================================*/
static PaperRectangle* groupable(PaperWidget w, PaperRectangle *r);
/*========================================================================*/

static void ClassInitialize()
{
	BaseClassExtRec.record_type = XmQmotif ;
}

static void create_gc(PaperWidget w)
{
	XGCValues	values;
	XtGCMask	valueMask;
	static char dash[] = {
		5, 1	};
	valueMask = GCForeground | GCBackground|GCFont;
	values.background = w->core.background_pixel;
	values.foreground = w->paper.foreground;
	values.font       = XLoadFont(XtDisplay(w),"fixed");

	w->paper.gc          = XtGetGC((Widget) w, valueMask, &values);
	w->paper.graphics.gc = XtGetGC((Widget) w, valueMask, &values);

	values.foreground = w->paper.white;
	w->paper.white_gc = XtGetGC((Widget) w, valueMask, &values);

	values.foreground = w->paper.selected;
	w->paper.selected_gc = XtGetGC((Widget) w, valueMask, &values);

	valueMask = GCBackground | GCForeground | GCLineStyle;
	values.line_style = LineOnOffDash;
	values.line_style = LineDoubleDash;
	values.foreground = w->paper.dash;
	w->paper.dash_gc = XtGetGC((Widget) w, valueMask, &values);

	values.foreground = w->paper.grid_color;
	w->paper.grid_gc = XtGetGC((Widget) w, valueMask, &values);

	valueMask = GCFunction | GCForeground | GCBackground;
	values.background = w->core.background_pixel;
	values.foreground = w->paper.foreground;
	values.foreground = values.foreground ^ values.background;
	values.background = 0;
	values.function = GXxor;
	w->paper.xor_gc = XtGetGC((Widget) w, valueMask, &values);
}
static void delete_gc(PaperWidget w)
{
	XtReleaseGC((Widget) w, w->paper.gc);
	XtReleaseGC((Widget) w, w->paper.graphics.gc);
	XtReleaseGC((Widget) w, w->paper.dash_gc);
	XtReleaseGC((Widget) w, w->paper.selected_gc);
	XtReleaseGC((Widget) w, w->paper.xor_gc);
	XtReleaseGC((Widget) w, w->paper.white_gc);
	XtReleaseGC((Widget) w, w->paper.grid_gc);
}
/* #define NORMALIZE(a) ( ((int)(a*100 + 0.5))/100.0 )  */
# define NORMALIZE(a) (a)

static void validate(PaperRectangle* r)
{
	if(r->top < 0)    r->top = 0;
	if(r->bottom < 0) r->bottom = 0;
	if(r->left < 0)   r->left = 0;
	if(r->right < 0)  r->right = 0;

	if(r->top > 1)    r->top = 1;
	if(r->bottom > 1) r->bottom = 1;
	if(r->left > 1)   r->left = 1;
	if(r->right > 1)  r->right = 1;

	if(r->top > r->bottom)
	{
		double x = r->bottom;
		r->bottom = r->top;
		r->top = x;
	}

	if(r->left > r->right)
	{
		double x = r->right;
		r->right = r->left;
		r->left = x;
	}

	if(r->top == r->bottom)
	{
		r->top = 0;
		r->bottom = 1;
	}

	if(r->left == r->right)
	{
		r->left = 0;
		r->right = 1;
	}

}

static PaperRectangle *new_rectangle(
double top, 
double left, 
double bottom, 
double right
)
{
	PaperRectangle *r = XtNew(PaperRectangle);

	r->top = NORMALIZE(top);
	r->left = NORMALIZE(left);
	r->bottom = NORMALIZE(bottom);
	r->right = NORMALIZE(right);
	r->next = 0;
	r->kids = 0;
	r->data = 0;
	r->selected = True;
	r->resizing = False;
	r->number   = 0;


	validate(r);


	return r;
}

static void r_dump(PaperRectangle *r, int depth)
{
	if(r)
	{
		int i;
		for(i = 0; i < depth ; i++) printf("   ");
		printf("[%g %g %g %g] ",r->top,r->left,r->bottom,r->right);
		printf("%p %d %d\n",r->data,r->number,r->selected);
		r_dump(r->kids,depth+1);
		r_dump(r->next,depth);
	}
}

static void call(PaperWidget w,const char* what,
PaperRectangle *r1,PaperRectangle* r2,PaperRectangle *r3)
{
	PaperCallbackStruct cb = { 
		0, 	};
	cb.r1 = r1;
	cb.r2 = r2;
	cb.r3 = r3;
	XtCallCallbacks((Widget)w,what,(XtPointer)&cb);
}

static PaperRectangle *delete_rectangle(PaperWidget w,PaperRectangle *r)
{
	PaperRectangle *s = r->next;

	call(w,XmNdeleteCallback,r,0,0);

	if (r->kids)
		delete_rectangle(w,r->kids);

	XtFree((XtPointer) r);
	return s;
}

static void paper_size(PaperWidget w)
{

	XRectangle  z = *(XRectangle*)&w->core.x;
	XRectangle y;
	double a,b;

	z.x += w->paper.hmargin;
	z.y += w->paper.vmargin;

	z.width -= 2*w->paper.hmargin;
	z.height -= 2*w->paper.vmargin;

	y.x = 0;
	y.y = 0;

	y.width  = (double) w->paper.hpage;
	y.height = (double) w->paper.vpage;

	a = z.width  / w->paper.hpage;
	b = z.height / w->paper.vpage;

	if(a>b) a = b;

	y.width  = a*w->paper.hpage;
	y.height = a*w->paper.vpage;

	w->paper.grid_x = (int)(y.width*w->paper.page_grid_x + 0.5);
	w->paper.grid_y = (int)(y.height*w->paper.page_grid_y + 0.5);

	y.x = (z.width - y.width)/2;
	y.y = (z.height - y.height)/2;

	w->paper.page = y;

	if(w->paper.offscreen)
	{
		Display *dpy   = XtDisplay(w);
		int     screen = DefaultScreen(dpy);
		Window  root   = RootWindow(dpy,screen);

		w->paper.pixmap = XCreatePixmap(dpy,root,
		    w->core.width,
		    w->core.height,
		    w->core.depth);
	}
}

static void Initialize(PaperWidget request, PaperWidget new)
{
	if (request->core.width <= 0)
		new->core.width = 5;

	if (request->core.height <= 0)
		new->core.height = 5;

	new->paper.rects = new_rectangle(0, 0, 1, 1);
	create_gc(new);
	new->paper.hmargin = 2;
	new->paper.vmargin = 2;
	new->paper.what = 0;
	new->paper.vpage = 21.0;
	new->paper.hpage = 29.7;
	new->paper.offscreen = True;
	new->paper.pixmap  = 0;
	new->paper.last_click = 0;
	new->paper.handles = True;
	new->paper.free_moves = True;
	new->paper.grid = False;
	new->paper.snap = False;
	new->paper.page_grid_x = 0.125;
	new->paper.page_grid_y = 0.125;
	paper_size(new);
}

static void Destroy(PaperWidget widget)
{
	PaperRectangle *r = widget->paper.rects;

	while (widget->paper.rects)
		widget->paper.rects = delete_rectangle(widget,widget->paper.
		    rects);

	delete_gc(widget);

	if(widget->paper.pixmap)
		XFreePixmap(XtDisplay(widget), widget->paper.pixmap);
}

static Boolean SetValues(PaperWidget current, PaperWidget request, 
PaperWidget new)
{
	if (current->paper.foreground != request->paper.foreground) {
		delete_gc(current);
		create_gc(new);
	}
	return True;
}
# define PERCENT(a,b,c) ((int)(((double)ref->a)*((double)r->b)+0.5)+ref->c)

static XRectangle rectangle(PaperWidget w, XRectangle *ref, 
PaperRectangle *r)
{
	XRectangle	x;

	int top    = PERCENT(height, top,    y);
	int bottom = PERCENT(height, bottom, y);
	int left   = PERCENT(width,  left,   x);
	int right  = PERCENT(width,  right,  x);

	x.x      = left;
	x.y      = top;
	x.width  = right - left;
	x.height = bottom - top;

	return x;
}
static Boolean in_rect(XEvent *event, XRectangle *x)
{
	return (event->xbutton.x > x->x && 
	    event->xbutton.y > x->y && 
	    event->xbutton.x < x->x + x->width && 
	    event->xbutton.y < x->y + x->height);
}
# define HANDLE 6
# define WHAT_SELECT	  -1

static int get_rects(PaperWidget w,
	PaperRectangle *lr, XRectangle *x, XRectangle *r)
{
	int		i = 0;
	int             offset;

	if(!w->paper.handles)
		return 0;

	offset = w->paper.editable ? HANDLE : 0;
	for (i = 0; i < 9; i++)
		r[i].width = r[i].height = offset;

# define WHAT_TOP_LEFT 0
	r[0].x = x->x;
	r[0].y = x->y;
	/* if (lr->top == 0 || lr->left == 0) r[0].width = r[0].height = 0; */

# define WHAT_TOP      1
	r[1].x = x->x + x->width / 2 - offset / 2;
	r[1].y = x->y;
	/* if (lr->top == 0) r[1].width = r[1].height = 0; */

# define WHAT_TOP_RIGHT 2
	r[2].x = x->x + x->width - offset;
	r[2].y = x->y;
	/* if (lr->top == 0 || lr->right == 1) r[2].width = r[2].height = 0; */

# define WHAT_LEFT 3
	r[3].x = x->x;
	r[3].y = x->y + x->height / 2 - offset / 2;
	/* if (lr->left == 0) r[3].width = r[3].height = 0; */

# define WHAT_CENTER	  4
	r[4].x = x->x + x->width / 2 - offset / 2;
	r[4].y = x->y + x->height / 2 - offset / 2;
	/* if (lr->top == 0 || lr->left == 0 || lr->bottom == 1 || lr-> right == 1) r[4].width = r[4].height = 0; */

# define WHAT_RIGHT 5
	r[5].x = x->x + x->width - offset;
	r[5].y = x->y + x->height / 2 - offset / 2;
	/* if (lr->right == 1) r[5].width = r[5].height = 0; */

# define WHAT_BOTTOM_LEFT 6
	r[6].x = x->x;
	r[6].y = x->y + x->height - offset;
	/* if (lr->left == 0 || lr->bottom == 1) r[6].width = r[6].height = 0; */

# define WHAT_BOTTOM	  7
	r[7].x = x->x + x->width / 2 - offset / 2;
	r[7].y = x->y + x->height - offset;
	/* if (lr->bottom == 1) r[7].width = r[7].height = 0; */

# define WHAT_BOTTOM_RIGHT 8
	r[8].x = x->x + x->width - offset;
	r[8].y = x->y + x->height - offset;
	/* if (lr->bottom == 1 || lr->right == 1) r[8].width = r[8].height = 0; */

	return 9;
}
static PaperRectangle *click(PaperWidget w, XEvent *event, int *what)
{
	PaperRectangle *r = w->paper.rects;
	*what = WHAT_SELECT;

	while (r) {
		XRectangle	x = rectangle(w, &w->paper.page, r);
		if (in_rect(event, &x)) {
			XRectangle	y[10];
			int		i;
			int		n = get_rects(w,r, &x, y);

			for (i = 0; i < n; i++)
				if (in_rect(event, &y[i]))
				{
					*what = i;
				}

			return r;
		}
		r = r->next;
	}
	return 0;
}

static void get_drag_handle(PaperWidget w,XRectangle* result)
{
	XRectangle  x = rectangle(w, &w->paper.page, w->paper.drag);
	XRectangle  y[10];
	get_rects(w,w->paper.drag, &x, y);
	*result = y[w->paper.what];
}


static void draw(PaperWidget w, Drawable d,XRectangle *ref, GC gc, 
PaperRectangle *r)
{
	XRectangle	x = rectangle(w, ref, r);
	PaperRectangle *k = r->kids;

	PaperCallbackStruct cb = { 
		0, 	};
	cb.r1             = r;
	cb.graphics       = &w->paper.graphics;
	cb.graphics->r    = r;
	cb.graphics->rect = &x;

	XSetClipRectangles(XtDisplay(w),w->paper.graphics.gc,0,0,&x,1,Unsorted);
	XtCallCallbacks((Widget)w,XmNdrawCallback,(XtPointer)&cb);
	XSetClipMask(XtDisplay(w),w->paper.graphics.gc,None);

	while (k) {
		draw(w, d, &x, w->paper.dash_gc, k);
		k = k->next;
	}
	XDrawRectangles(XtDisplay(w), d, gc, &x, 1);
	if (r->selected && w->paper.editable) {
		XRectangle	y[10];
		int		n = get_rects(w,r, &x, y);

		XFillRectangles(XtDisplay(w), d, gc, y, n);
	}
}

static void redraw(PaperWidget w,Drawable d)
{
	PaperRectangle *r = w->paper.rects;
	w->paper.graphics.drawable = d;

	XFillRectangles(XtDisplay(w), d, w->paper.white_gc,&w->paper.page,1);

	if(w->paper.grid)
	{
		int x = w->paper.page.x;
		int y = w->paper.page.y;

		x += w->paper.grid_x;
		y += w->paper.grid_y;

		while( x <=  w->paper.page.width + w->paper.page.x)
		{
			XDrawLine(XtDisplay(w), d, w->paper.grid_gc, 
				x, w->paper.page.y, 
				x, w->paper.page.height + w->paper.page.y);

			x += w->paper.grid_x;
		}
		
		while( y <= w->paper.page.height + w->paper.page.y)
		{
			XDrawLine(XtDisplay(w), d, w->paper.grid_gc, 
				w->paper.page.x, y, 
				w->paper.page.width+w->paper.page.x,y);
			y += w->paper.grid_y;
		}
	}

	while (r) {
		if (!r->selected)
			draw(w, d, &w->paper.page, w->paper.gc, r);
		r = r->next;
	}

	r = w->paper.rects;
	while (r) {
		if (r->selected)
			draw(w, d, &w->paper.page, w->paper.selected_gc, r);
		r = r->next;
	}
}

static void Redisplay(PaperWidget w, XEvent *event, Region region)
{
	XEvent		ev;
	if(w->paper.pixmap)
	{
		XSetForeground(XtDisplay(w),w->paper.gc,w->core.background_pixel);
		XFillRectangle(XtDisplay(w),w->paper.pixmap,w->paper.gc,
		    0,0,w->core.width,w->core.height);
		XSetForeground(XtDisplay(w),w->paper.gc,w->paper.foreground);
		redraw(w,w->paper.pixmap);
		XCopyArea(XtDisplay(w),w->paper.pixmap,XtWindow(w),
		    w->paper.gc,0,0,w->core.width,w->core.height,0,0);
	}
	else 
		redraw(w,XtWindow(w));
}

static PaperRectangle* sort(PaperRectangle* r)
{
	int n = 0;
	PaperRectangle *first = r;
	int more = 1;

	if(!r) return 0;

	while(more)
	{
		PaperRectangle* a = first;
		PaperRectangle* b = first->next;
		PaperRectangle* c = 0;
		more = 0;


		while(b)
		{
			if(b->top < a->top || (b->top == a->top && b->left < a->left))
			{

				a->next = b->next;
				b->next = a;

				if(c) c->next = b;
				else first   = b;


				more = 1;
				break;
			}

			c = a;
			a = b;
			b = b->next;
		}

	}


	r = first;
	while(r)
	{
		r->kids = sort(r->kids);
		r->number = ++n;
		r = r->next;
	}
	return first;
}

static void update(PaperWidget w)
{
	w->paper.rects = sort(w->paper.rects);

	if(XtIsRealized(w))
		if(w->paper.pixmap)
			Redisplay(w,0,0);
		else 
			XClearArea(XtDisplay(w),XtWindow(w),0,0,0,0,True);

	call(w,XmNchangeCallback,0,0,0);
}


static void detach_rectangle(PaperWidget w, PaperRectangle *r)
{
	PaperRectangle *p = w->paper.rects;
	PaperRectangle *q = 0;

	while (p) {
		if (p == r) {
			if (q)
				q->next = r->next;
			else
				w->paper.rects = r->next;

			r->next = 0;
			return;
		}
		q = p;
		p = p->next;
	}
}

static void remove_rectangle(PaperWidget w, PaperRectangle *r)
{
	detach_rectangle(w, r);
	delete_rectangle(w,r);
}

static void split_rectangle(PaperWidget w, PaperRectangle *r, int h, int v,double mh,double mv)
{
	int		i, j;
	double		height = (r->bottom - r->top) / v;
	double		width = (r->right - r->left) / h;
	double		x = r->left;

	for (i = 0; i < h; i++) {
		double		y = r->top;

		for (j = 0; j < v; j++)
		{
			PaperRectangle *t = new_rectangle(
				(j == 0) ? y : (y+mv/2.0), 
				(i == 0) ? x : (x+mv/2.0), 
				(j == v - 1) ? r->bottom : (y + height - mv/2.0), 
			    	(i == h - 1) ? r->right  : (x + width - mh /2.0)
			    );
			t->next = w->paper.rects;
			w->paper.rects = t;
			y += height;
			call(w,XmNsplitCallback,r,t,0);
		}
		x += width;
	}
	remove_rectangle(w, r);
}

static void Resize(PaperWidget w)
{
	if(w->paper.pixmap)
	{
		XFreePixmap(XtDisplay(w), w->paper.pixmap);
		w->paper.pixmap = 0;
	}

	paper_size(w);
	update(w);
	if (XtIsRealized(w))
		XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True);
}

Widget CreatePaperWidget(Widget par, const char *nam, Arg *al, int ac)
{
	return XtCreateWidget(nam, paperWidgetClass, par, al, ac);
}

void PaperSplitSelection(Widget _w, int h, int v,double mh,double mv)
{
	PaperWidget	w = (PaperWidget) _w;
	PaperRectangle *r = w->paper.rects;

	if (h == 0 || v == 0)
		return;

	while (r) {
		PaperRectangle *s = r->next;

		if (r->selected && !r->kids)
			split_rectangle(w, r, h, v, mh, mv);

		r = s;
	}
	update(w);

}
static Boolean join(PaperWidget w, PaperRectangle *r)
{
	PaperRectangle *s = w->paper.rects;
	if (s = groupable(w,r) ) {
		PaperRectangle *t = new_rectangle(
		    MIN(s->top, r->top), 
		    MIN(s->left, r-> left), 
		    MAX(s->bottom, r-> bottom), 
		    MAX(s->right, r-> right)
		    );

		call(w,XmNjoinCallback,r,s,t);

		t->next = w->paper.rects;
		w->paper.rects = t;
		remove_rectangle(w, s);
		remove_rectangle(w, r);
		return True;
	}
	return False;
}

void PaperJoinSelection(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	Boolean         more = True;
	while (more) {
		PaperRectangle *r = w->paper.rects;

		more = False;
		while (r) {
			if (r->selected && !r->kids)
				if (join(w, r)) {
					more = True;
					break;
				}
			r = r->next;
		}
	}
	update(w);
}

void PaperDeleteSelection(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	int more = 1;

	while(more)
	{
		PaperRectangle *r = w->paper.rects;
		more = 0;
		while (r) {
			if(r->selected)
			{
				remove_rectangle(w,r);
				more = 1;
				break;
			}
			r = r->next;
		}
	}
	update(w);
}


static void select_all(PaperWidget w, Boolean on)
{
	PaperRectangle *r = w->paper.rects;
	int		n = 0;

	while (r) {
		if (r->selected != on) {
			r->selected = on;
			n++;
		}
		r = r->next;
	}
	if (n) update(w);

}

static void SelectAll(PaperWidget w,XEvent *event,char **args,int nargs)
{
	select_all(w, True);
}

static void SelectNone(PaperWidget w,XEvent *event,char **args,int nargs)
{
	select_all(w, False);
}

static double resize_top(PaperWidget,PaperRectangle*,double,Boolean,Boolean);
static double resize_bottom(PaperWidget,PaperRectangle*,double,Boolean,Boolean);
static double resize_left(PaperWidget,PaperRectangle*,double,Boolean,Boolean);
static double resize_right(PaperWidget,PaperRectangle*,double,Boolean,Boolean);

typedef double (*ResizeProc)(PaperWidget,PaperRectangle*,double,Boolean,Boolean);

typedef double (*GetProc)(PaperRectangle*);

static double get_top(PaperRectangle* r)    { return r->top; }
static double get_left(PaperRectangle* r)   { return r->left; }
static double get_bottom(PaperRectangle* r) { return r->bottom; }
static double get_right(PaperRectangle* r)  { return r->right; }

typedef void (*SetProc)(PaperRectangle*,double);

static void set_top(PaperRectangle* r,double a)    { r->top  = a; }
static void set_left(PaperRectangle* r,double a)   { r->left = a; }
static void set_bottom(PaperRectangle* r,double a) { r->bottom = a; }
static void set_right(PaperRectangle* r,double a)  { r->right = a; }

typedef int (*CompareProc)(double,double);
static int smaller(double a,double b) { return a < b; }
static int greater(double a,double b) { return a > b; }

typedef double (*PageProc)(PaperWidget);
static double page_width(PaperWidget w)  { return w->paper.hpage; }
static double page_height(PaperWidget w) { return w->paper.vpage; }
static double grid_width(PaperWidget w)  { return w->paper.page_grid_x; }
static double grid_height(PaperWidget w) { return w->paper.page_grid_y; }

typedef struct {
	SetProc    set_this;
	SetProc    set_opposite;
	GetProc    this;
	GetProc    opposite;
	GetProc    other_low;
	GetProc    other_high;
	ResizeProc resize_opposite;
	CompareProc compare;
	double      minimum;
	PageProc    page;
	PageProc    grid;
} Helper;

static Helper topHelper = {
	set_top,
	set_bottom,
	get_top,
	get_bottom,
	get_left,
	get_right,
	resize_bottom,
	greater,
	MINIMUM,
	page_height,
	grid_height,
};

static Helper bottomHelper = {
	set_bottom,
	set_top,
	get_bottom,
	get_top,
	get_left,
	get_right,
	resize_top,
	smaller,
	-MINIMUM,
	page_height,
	grid_height,
};

static Helper leftHelper = {
	set_left,
	set_right,
	get_left,
	get_right,
	get_top,
	get_bottom,
	resize_right,
	greater,
	MINIMUM,
	page_width,
	grid_width,
};
 
static Helper rightHelper = {
	set_right,
	set_left,
	get_right,
	get_left,
	get_top,
	get_bottom,
	resize_left,
	smaller,
	-MINIMUM,
	page_width,
	grid_width,
};

static double resize_x(Helper* x,PaperWidget w,
	PaperRectangle *r,double amount,Boolean doit,Boolean free)
{
	double		a;
	int		m = 0;
	PaperRectangle *s = w->paper.rects;

	if (r->resizing)
		return amount;

	r->resizing = True;
	if(!free)
	while (s) {
		if (MAX(x->other_low(s), x->other_low(r)) < 
			MIN(x->other_high(s), x->other_high(r))) 
		{
			a = x->this(r) + amount;
/*			if ( x->compare(a,x->this(s)) && !x->compare(a,x->opposite(s))) */
			if(EQUAL(x->opposite(s),x->this(r)))
			 {
				a = x->resize_opposite(w, s, amount, doit,free);
				if (ABS(a) < ABS(amount))
					amount = a;
			}
		}
		s = s->next;
	}
	a = NORMALIZE(x->this(r) + amount);
	if (a < 0)
		a = 0;

	if (a > 1)
		a = 1;


	if (x->compare(a + x->minimum,x->opposite(r)))
		a = x->opposite(r) - x->minimum;


	amount = a - x->this(r);
	if (doit)
		x->set_this(r,a); 

	r->resizing = False;
	if (doit && amount)
		w->paper.dirty = True;

	return amount;
}

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

static double resize_top(PaperWidget w,PaperRectangle *r,
	double amount,Boolean doit,Boolean free)
{
	return resize_x(&topHelper,w,r,amount,doit,free);
}

static double resize_bottom(PaperWidget w,PaperRectangle *r,
	double amount,Boolean doit,Boolean free)
{
	return resize_x(&bottomHelper,w,r,amount,doit,free);
}

static double resize_left(PaperWidget w,PaperRectangle *r,
	double amount,Boolean doit,Boolean free)
{
	return resize_x(&leftHelper,w,r,amount,doit,free);
}

static double resize_right(PaperWidget w,PaperRectangle *r,
	double amount,Boolean doit,Boolean free)
{
	return resize_x(&rightHelper,w,r,amount,doit,free);
}

/*--------------------------------------------------------------------*/
static void StartDrag(PaperWidget w,XEvent *event,char **args,int nargs)
{
	Boolean         shift = (event->xkey.state & ShiftMask);
	PaperRectangle *r = click(w, event, &w->paper.what);

	w->paper.drag = r;

	XmProcessTraversal((Widget)w,XmTRAVERSE_CURRENT);

	w->paper.startx = event->xbutton.x;
	w->paper.starty = event->xbutton.y;
	w->paper.lastx = event->xbutton.x;
	w->paper.lastx = event->xbutton.y;

	if (w->paper.what == WHAT_SELECT) {
		if (!shift)
			select_all(w, False);

		if (r) {
			r->selected = !r->selected;
			update(w);
		}
	}
	else {
		w->paper.constraint = shift;
		w->paper.which_constraint = -1;
	}

	w->paper.sel.x = 
	    w->paper.sel.y = 
	    w->paper.sel.width = 
	    w->paper.sel.height = 0;

	if( w->paper.last_click && ((event->xbutton.time - w->paper.last_click) < 
	    XtGetMultiClickTime(XtDisplay(w))))
	{
		if(r) call(w,XmNdblClickCallback,r,0,0);
		w->paper.what = WHAT_SELECT;
		w->paper.last_click = 0;
	}
	else
		w->paper.last_click = event->xbutton.time;
}

static void Drag(PaperWidget w,XEvent *event,char **args,int nargs)
{
	Boolean control = (event->xkey.state & ControlMask);

	Boolean free    = w->paper.free_moves;

	if(control) free = !free;

	if (w->paper.what == WHAT_SELECT) {
		int		newx = event->xbutton.x;
		int		newy = event->xbutton.y;

		if (newx != w->paper.lastx && newy != w->paper.lastx) {
			XDrawRectangles(XtDisplay(w), XtWindow(w), 
			    w->paper.xor_gc, 
			    & w->paper.sel, 1);
			w->paper.lastx = newx;
			w->paper.lasty = newy;
			w->paper.sel.x = MIN(newx, w->paper.startx);
			w->paper.sel.y = MIN(newy, w->paper.starty);
			w->paper.sel.width = MAX(newx, w->paper.
			    startx)
			    - w->paper.sel.x;
			w->paper.sel.height = MAX(newy, w->paper.
			    starty)
			    - w->paper.sel.y;
			XDrawRectangles(XtDisplay(w), XtWindow(w), 
			    w->paper.xor_gc, 
			    & w->paper.sel, 1);
		}
	}
	else {

		Boolean         b;
		PaperRectangle *r = w->paper.rects;
		double amount_x;
		double amount_y;
		amount_x = (double)(event->xbutton.x - w->paper.startx) / (double)w->paper.page.width;
		amount_y = (double)(event->xbutton.y - w->paper.starty) / (double)w->paper.page.height;

		if(w->paper.snap)
		{
			int x = event->xbutton.x;
			int y = event->xbutton.y;
			int x0,y0,x1,y1;
			XRectangle hit;

			get_drag_handle(w,&hit);

			printf ("%d %d\n",x,y);
			printf ("%d %d %d %d\n",hit.x,hit.y,hit.width,hit.height);

			switch (w->paper.what) {

				case WHAT_TOP_LEFT:
					x = hit.x;
					y = hit.y;
					break;

				case WHAT_TOP:
					y = hit.y;
					break;

				case WHAT_TOP_RIGHT:
					x = hit.x + hit.width;
					y = hit.y;
					break;

				case WHAT_LEFT:
					x = hit.x;
					break;

				case WHAT_CENTER:
					x = hit.x + hit.width / 2;
					y = hit.y + hit.height / 2;
					break;

				case WHAT_RIGHT:
					x = hit.x + hit.width;
					break;

				case WHAT_BOTTOM_LEFT:
					x = hit.x;
					y = hit.y + hit.height;
					break;

				case WHAT_BOTTOM:
					y = hit.y + hit.height;
					break;

				case WHAT_BOTTOM_RIGHT:
					x = hit.x + hit.width;
					y = hit.y + hit.height;
					break;
			}

			w->paper.startx = x;
			w->paper.starty = y;

			x = event->xbutton.x;
			y = event->xbutton.y;

			x -= w->paper.page.x;
			x -= w->paper.page.y;

			x0 = ((int)(x / w->paper.grid_x)) * w->paper.grid_x;
			x1 = x0 + w->paper.grid_x;

			y0 = ((int)(y / w->paper.grid_y)) * w->paper.grid_y;
			y1 = y0 + w->paper.grid_y;

			x =  (x - x0) < (x1 - x) ? x0 : x1;
			y =  (y - y0) < (y1 - y) ? y0 : y1;

			x += w->paper.page.x;
			y += w->paper.page.y;

			amount_x = (double)(x - w->paper.startx) / (double)w->paper.page.width;
			amount_y = (double)(y - w->paper.starty) / (double)w->paper.page.height;

		}


		if(w->paper.constraint)
		{
			if(w->paper.which_constraint == -1)
			{
				w->paper.which_constraint = ABS(amount_x) > ABS(amount_y);
			}

			if(w->paper.which_constraint)
				amount_y = 0;
			else
				amount_x = 0;
		}

		if(!w->paper.editable)
			return;

		while (r) {
			if (r->selected)
				for (b = 0; b < 2; b++)
					switch (w->paper.what) {

					case WHAT_TOP_LEFT:
						amount_y = resize_top(w, r, amount_y, b,free);
						amount_x = resize_left(w, r, amount_x, b,free);
						break;

					case WHAT_TOP:
						amount_y = resize_top(w, r, amount_y, b,free);
						break;

					case WHAT_TOP_RIGHT:
						amount_y = resize_top(w, r, amount_y, b,free);
						amount_x = resize_right(w, r, amount_x, b,free);
						break;

					case WHAT_LEFT:
						amount_x = resize_left(w, r, amount_x, b,free);
						break;

					case WHAT_CENTER:
						amount_y = resize_top(w, r, amount_y, b,free);
						amount_x = resize_left(w, r, amount_x, b,free);
						amount_y = resize_bottom(w, r, amount_y, b,free);
						amount_x = resize_right(w, r, amount_x, b,free);
						break;

					case WHAT_RIGHT:
						amount_x = resize_right(w, r, amount_x, b,free);
						break;

					case WHAT_BOTTOM_LEFT:
						amount_y = resize_bottom(w, r, amount_y, b,free);
						amount_x = resize_left(w, r, amount_x, b,free);
						break;

					case WHAT_BOTTOM:
						amount_y = resize_bottom(w, r, amount_y, b,free);
						break;

					case WHAT_BOTTOM_RIGHT:
						amount_y = resize_bottom(w, r, amount_y, b,free);
						amount_x = resize_right(w, r, amount_x, b,free);
						break;
					}
			r = r->next;
		}
		w->paper.startx = event->xbutton.x;
		w->paper.starty = event->xbutton.y;
		if (w->paper.dirty) {
			update(w);
			w->paper.dirty = False;
		}
	}
}

static Boolean InterRect(XRectangle *r1, XRectangle *r2)
{
	if ((r1->x + r1->width) < r2->x || (r1->y + r1->height) < r2->y)
		return False;

	if (r1->x > (r2->x + r2->width) || r1->y > (r2->y + r2->height))
		return False;

	return True;
}

static void EndDrag(PaperWidget w,XEvent *event,char **args, int nargs)
{
	if (w->paper.what == WHAT_SELECT) {
		PaperRectangle *r = w->paper.rects;

		XDrawRectangles(XtDisplay(w), XtWindow(w), 
		    w->paper.xor_gc, 
		    & w->paper.sel, 1);
		while (r) {
			XRectangle	x = rectangle(w, &w->paper.page, r);

			if (InterRect(&w->paper.sel, &x)) {
				r->selected = True;
				w->paper.dirty = True;
			}
			r = r->next;
		}
	}
	if (w->paper.dirty) {
		update(w);
		w->paper.dirty = False;
	}
}

static PaperRectangle *chain(PaperRectangle *r, PaperRectangle *s)
{
	PaperRectangle *p = r;
	PaperRectangle *q = 0;

	if (!r)
		return s;

	if (!s)
		return r;

	while (p) {
		q = p;
		p = p->next;
	}
	q->next = s;
	return r;
}
static PaperRectangle *ungroup(PaperWidget w, PaperRectangle *r)
{
	PaperRectangle *s = r->kids;
	PaperRectangle *t;

	detach_rectangle(w, r);
	r->selected = False;
	if (!s)
		return r;


	t = r;
	while (s) {


		s->top    = (t->bottom - t->top)  *s->top + t->top;
		s->bottom = (t->bottom - t->top)  *s->bottom + t->top;
		s->left   = (t->right  - t->left) *s->left + t->left;
		s->right  = (t->right  - t->left) *s->right + t->left;

		s->selected = False;

		call(w,XmNungroupCallback,r,s,0);

		s = s->next;
	}
	s = r->kids;
	r->kids = 0;
	delete_rectangle(w,r);
	return s;
}


static PaperRectangle* groupable(PaperWidget w, PaperRectangle *r)
{
	PaperRectangle *s = w->paper.rects;
	while (s) {
		if (s != r && s->selected) {

			double top    = MIN(s->top, r->top);
			double left   = MIN(s->left, r-> left);
			double bottom = MAX(s->bottom, r-> bottom);
			double right  = MAX(s->right, r-> right);


			PaperRectangle *t = w->paper.rects;
			int ok = 1;

			while (t && ok) {
				if (t != r && t != s) {
					double tt = MAX(t->top,    top);
					double ll = MAX(t->left,   left);
					double bb = MIN(t->bottom, bottom);
					double rr = MIN(t->right,  right);

					if(tt<bb && ll<rr)
						ok = 0;
				}
				t = t->next;
			}

			if(ok) return s;

		}
		s = s->next;
	}

	return 0;
}


static Boolean group(PaperWidget w, PaperRectangle *r)
{
	PaperRectangle *s = w->paper.rects;
	PaperRectangle *x;

	if (s = groupable(w,r))
	{
		PaperRectangle *t = new_rectangle(
		    MIN(s->top, r->top), 
		    MIN(s->left, r-> left), 
		    MAX(s->bottom, r-> bottom), 
		    MAX(s->right, r-> right)
		    );

		t->next = w->paper.rects;
		w->paper.rects = t;
		s = ungroup(w, s);
		r = ungroup(w, r);

		for(x = s; x ; x = x->next)
			call(w,XmNgroupCallback,x,t,0);

		for(x = r; x ; x = x->next)
			call(w,XmNgroupCallback,x,t,0);

		t->kids = chain(r, s);
		s = t->kids;
		while (s) {

			s->top = (s->top - t->top) / (t-> bottom - t->top);
			s->bottom = 1 - (t->bottom - s-> bottom) / (t-> bottom - t->top);
			s->left = (s->left - t->left) / ( t->right - t->left);
			s->right = 1 - (t->right - s-> right) / (t-> right - t->left);


			s->selected = False;
			s = s->next;
		}
		return True;
	}
	return False;
}

void PaperGroupSelection(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	Boolean         more = True;

	/* r_dump(w->paper.rects,0); */

	while (more) {
		PaperRectangle *r = w->paper.rects;

		more = False;
		while (r) {
			if (r->selected)
				if (group(w, r)) {
					more = True;
					break;
				}
			r = r->next;
		}
	}

	/* r_dump(w->paper.rects,0); */

	update(w);
}

void PaperUngroupSelection(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	Boolean         more = True;
	while (more) {
		PaperRectangle *r = w->paper.rects;

		more = False;
		while (r) {
			if (r->selected && r->kids) {

				PaperRectangle *s = ungroup(w, r);

				more = True;
				while (s) {
					r = s->next;
					s->next = w->paper.rects;
					w->paper.rects = s;
					s->selected = True;
					s = r;
				}
				break;
			}
			r = r->next;
		}
	}
	update(w);

}

static void unselect(PaperRectangle* r)
{
	while(r)
	{
		unselect(r->kids);
		r->selected = False;
		r = r->next;
	}
}

PaperRectangle* PaperGetPages(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	return w->paper.rects;
}

void PaperSetPages(Widget _w,PaperRectangle* r)
{
	PaperWidget	w = (PaperWidget) _w;
	while(w->paper.rects)
		remove_rectangle(w,w->paper.rects);

	w->paper.rects = r ? r : new_rectangle(0, 0, 1, 1);
	unselect(w->paper.rects);

	while(r)
	{
		validate(r);
		r = r->next;
	}

	update(w);
}

PaperRectangle* PaperFind(Widget _w,XEvent *ev)
{
	int what;
	PaperWidget	w = (PaperWidget) _w;
	return click(w,ev,&what);
}

void PaperLocation(Widget _w,int x,int y,double* px,double* py)
{
	int what;
	PaperWidget	w = (PaperWidget) _w;

	*px = x;
	*py = y;

	*px -=  w->paper.page.x;
	*py -=  w->paper.page.y;

	*px /= w->paper.page.width;
	*py /= w->paper.page.height;
}

PaperRectangle* PaperNewPage(Widget _w,double top,double left,
	double bottom,double right)
{
	return new_rectangle(top,left,bottom,right);
}

static void Join(PaperWidget w,XEvent *event,char **args,int nargs)
{
	PaperJoinSelection((Widget)w);
}

static void Inset(PaperWidget w,XEvent *event,char **args,int nargs)
{
	PaperInsetSelection((Widget)w,0.01,0.01,0.01,0.01);
}

static void Insert(PaperWidget w,XEvent *event,char **args,int nargs)
{
	PaperAddPage((Widget)w,0,0,0.25,0.25);
}

static void Delete(PaperWidget w,XEvent *event,char **args,int nargs)
{
	PaperDeleteSelection((Widget)w);
}

static void Split(PaperWidget w,XEvent *event,char **args,int nargs)
{

	Boolean         shift = (event->xkey.state & ShiftMask);
	double mh = shift?0.01:0.0;
	double mv = shift?0.01:0.0;
	PaperSplitSelection((Widget)w,atol(args[0]),atol(args[1]),mh,mv);
}

static void Group(PaperWidget w,XEvent *event,char **args,int nargs)
{
	PaperGroupSelection((Widget)w);
}

static void Ungroup(PaperWidget w,XEvent *event,char **args,int nargs)
{
	PaperUngroupSelection((Widget)w);
}

void PaperUpdate(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	update(w);
}

void PaperInsetSelection(Widget _w,double top,
	double left,double bottom,double right)
{
	PaperWidget	w = (PaperWidget) _w;

	PaperRectangle *r = w->paper.rects;

	while (r)
	{
		if (r->selected)
		{
			r->top    += top;
			r->left   += left;
			r->bottom -= bottom;
			r->right  -= right;
		}
		r = r->next;
	}

	update(w);
}

void PaperSetSize(Widget _w, double h, double v)
{
	PaperWidget	w = (PaperWidget) _w;
	w->paper.hpage = h;
	w->paper.vpage = v;
	paper_size(w);
	update(w);
}

void PaperSetGrid(Widget _w, double x, double y)
{
	PaperWidget	w = (PaperWidget) _w;
	w->paper.page_grid_x = x;
	w->paper.page_grid_y = y;
	paper_size(w);
	update(w);
}

void PaperShowGrid(Widget _w, Boolean on)
{
	PaperWidget w = (PaperWidget) _w;
	w->paper.grid = on;
	update(w);
}

void PaperSnapGrid(Widget _w, Boolean on)
{
	PaperWidget w = (PaperWidget) _w;
	w->paper.snap = on;
}

static void align(Helper* x,PaperWidget w)
{
	PaperRectangle *r = w->paper.rects;
	double a = -1;

	while (r)
	{
		if (r->selected)
			if(a == -1) a = x->this(r);
			else  {
				double w =  x->opposite(r) - x->this(r);
				x->set_this(r,a);
				x->set_opposite(r,a+w);
			}
		r = r->next;
	}

	update(w);
}

void PaperGetSize(Widget _w,double* h,double* v)
{
	PaperWidget	w = (PaperWidget) _w;
	*h = w->paper.hpage;
	*v = w->paper.vpage;
}

void PaperGetGrid(Widget _w,double* x,double* y)
{
	PaperWidget	w = (PaperWidget) _w;
	*x = w->paper.page_grid_x;
	*y = w->paper.page_grid_y;
}

void PaperAlignTop(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	align(&topHelper,w);
}

void PaperAlignLeft(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	align(&leftHelper,w);
}

void PaperAlignRight(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	align(&rightHelper,w);
}

void PaperAlignBottom(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	align(&bottomHelper,w);
}
	
	
static void distribute(Helper* x,PaperWidget w)
{
	PaperRectangle *r = w->paper.rects;
	double min = 1;
	double max = 0;
	int n = 0;
	int m = 0;
	double size = 0;
	double pos = -1;

	while (r)
	{
		if (r->selected)
		{
			if(x->this(r)     < min) min = x->this(r);
			if(x->opposite(r) > max) max = x->opposite(r); 
			size += x->opposite(r) - x->this(r);
			n++;
		}
		r = r->next;
	}

	if(n < 2)
		return;

	size  = max - min - size;
	size /= (n-1);
	pos   = min;

	r = w->paper.rects;
	while (r)
	{
		if (r->selected)
		{
			double w =  x->opposite(r) - x->this(r);
			x->set_this(r,pos);			
			x->set_opposite(r,pos+w);
			pos += size + w;
		}
		r = r->next;
	}

	update(w);
}

void PaperDistributeHorizontally(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;	
	distribute(&leftHelper,w);
}

void PaperDistributeVertically(Widget _w)
{
	PaperWidget	w = (PaperWidget) _w;
	distribute(&topHelper,w);
}

void PaperSelectFirst(Widget _w)
{
	PaperWidget w = (PaperWidget) _w;
	unselect(w->paper.rects);
	if(w->paper.rects) {
		w->paper.rects->selected = True;
		update(w);
	}
}

void PaperUnselect(Widget _w)
{
	PaperWidget w = (PaperWidget) _w;
	unselect(w->paper.rects);
}

void PaperSelectAll(Widget _w, Boolean flag)
{
	PaperWidget w = (PaperWidget) _w;
	select_all(w,flag);
}

void PaperSetFreeMoves(Widget _w, Boolean flag)
{
	PaperWidget w = (PaperWidget) _w;
	w->paper.free_moves = flag;
}

Boolean PaperGetFreeMoves(Widget _w)
{
	PaperWidget w = (PaperWidget) _w;
	return w->paper.free_moves;
}

PaperRectangle*  PaperAddPage(Widget _w,double top,double left,double bottom,double right)
{
	PaperWidget w = (PaperWidget) _w;
	PaperRectangle *r = new_rectangle(top,left,bottom,right);
	validate(r);
	r->next = w->paper.rects;
	w->paper.rects = r;
	unselect(w->paper.rects);
	r->selected = True;
	update(w);
	return r;
}

