/*********************************************************************
 *
 *         EZWGL, the EZ Widget and Graphics Library
 *
 *             Copyright (C) 1996, 1997  Maorong Zou
 *  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **********************************************************************/
/*
 *  June 1996.  Beta Release.
 *  Sept 1996.  Release Version 1.0
 *  Dec 1996.  Release Version 1.1 Beta
 *  April 1997.  Release Version 1.2
 *  November 1997.  Release Version 1.3
 */
/*****************************************************************
 ***                                                           ***
 ***              Widget Working Area                          ***
 ***                                                           ***
 *****************************************************************/
#define _EZ_WIDGET_IWORK_AREA_C_
#include "EZ_Widget.h"
#include <X11/Xatom.h>

#define _UP       1
#define _LEFT     2
#define _DOWN	  3
#define _RIGHT    4
/*********************************************************************
 * 
 *  Functions implemented in this file:
 */
EZ_Widget        *EZ_CreateIWorkArea MY_ANSIARGS((EZ_Widget *parent));
void             EZ_DrawWidgetIWorkArea MY_ANSIARGS((EZ_Widget *widget));
void             EZ_ComputeWidgetIWorkAreaSize MY_ANSIARGS((EZ_Widget *widget, int *w, int *h));
void             EZ_FreeWidgetIWorkAreaData MY_ANSIARGS((EZ_Widget *widget));
void             EZ_IWorkAreaEventHandle MY_ANSIARGS((EZ_Widget *widget, XEvent *event));

void             EZ_GrowIWorkAreaDataSpace MY_ANSIARGS((EZ_Widget *widget, int increment));

void              EZ_IWorkAreaInsertItem MY_ANSIARGS((EZ_Widget *widget, EZ_Item *item));
void              EZ_IWorkAreaInsertItems MY_ANSIARGS((EZ_Widget *widget, EZ_Item **item, int n));
void              EZ_IWorkAreaDeleteItem MY_ANSIARGS((EZ_Widget *widget, EZ_Item *item));
void              EZ_IWorkAreaDeleteAllItems MY_ANSIARGS((EZ_Widget *widget));

void              EZ_IWorkAreaInsertItemBeforeAfter MY_ANSIARGS((EZ_Widget *widget, EZ_Item *item,
								 EZ_Item *newitem, int BefAft));
void              EZ_IWorkAreaInsertItemUsingIdx MY_ANSIARGS((EZ_Widget *widget, int idx, EZ_Item *item));
void              EZ_IWorkAreaDeleteItemUsingIdx MY_ANSIARGS((EZ_Widget *widget, int idx));
void              EZ_IWorkAreaDeleteSomeItemsUsingIndices  MY_ANSIARGS((EZ_Widget *widget, int *ids, int cnt));
EZ_Item           *EZ_FindPointerOnItemIWA MY_ANSIARGS((EZ_Item **data, int count, EZ_Item *guess,
						      int x, int y));

void              EZ_IWorkAreaSelectItem MY_ANSIARGS((EZ_Widget *widget, EZ_Item *item, int *location));
void              EZ_IWorkAreaSelectItemUsingIdx MY_ANSIARGS((EZ_Widget *widget, int idx, int *location));

/*********************************************************************
 * 
 * Local functions.
 */
static void   update_iwa_scrollbars MY_ANSIARGS((EZ_Widget *widget));
static void   IWATimerCallBack MY_ANSIARGS((EZ_Timer *timer, void *data));
static void   MoveIWorkAreaItem MY_ANSIARGS((EZ_Widget *widget, EZ_Item *item,
					     int nx, int ny, int ax, int ay,
					     int Rx, int Ry, int Rw, int Rh));
static int    EZ_FindItemInIWA MY_ANSIARGS((EZ_Widget *widget, EZ_Item *item));
static void   EZ_MakeIWASelectionVisible MY_ANSIARGS((EZ_Widget *widget, EZ_Item *sitem, EZ_Item *nitem));
static EZ_Item *EZ_FindClosestItem MY_ANSIARGS((EZ_Item **data,int nitems,EZ_Item *ositem, int dir));
/*********************************************************************
 * 
 *  Local Variables.
 */
static EZ_WidgetHandle EZ_IWorkAreaHandle =
{
  EZ_ComputeWidgetIWorkAreaSize,
  EZ_DrawWidgetIWorkArea,
  EZ_FreeWidgetIWorkAreaData,
  EZ_IWorkAreaEventHandle,
};

/*********************************************************************/

EZ_Widget  *EZ_CreateIWorkArea(parent)
     EZ_Widget  *parent;     /* parent widget    */
{
  EZ_Widget  *wptr;

  wptr = EZ_CreateNewWidget(parent);
  EZ_WidgetHandlingFunctions[EZ_WIDGET_IWORK_AREA] = &EZ_IWorkAreaHandle;

  EZ_SetWidgetTypeAndNames(wptr,EZ_WIDGET_IWORK_AREA);
  EZ_WidgetBorderStyle(wptr)  = EZ_BORDER_DOWN;
  EZ_WidgetBorderWidth(wptr)  = 2;
  EZ_WidgetPadX(wptr) = 1;
  EZ_WidgetPadY(wptr) = 1;
  EZ_WidgetPadB(wptr) = 2;

  EZ_IWorkAreaSelection(wptr) = (EZ_Item *)NULL;
  EZ_IWorkAreaNItems(wptr)    = 0;

  EZ_IWorkAreaHighlightBG(wptr) = ~0;
  EZ_IWorkAreaHighlightFG(wptr) = ~0;
  EZ_IWorkAreaTextBG(wptr)      = ~0;

  EZ_IWorkAreaDirty(wptr)       = 1;

  EZ_IWorkAreaYSize(wptr)       = 1;   
  EZ_IWorkAreaYPageSize(wptr)   = 1;   
  EZ_IWorkAreaYStart(wptr)      = 0;   

  EZ_IWorkAreaXSize(wptr)       = 1;   
  EZ_IWorkAreaXPageSize(wptr)   = 1;   
  EZ_IWorkAreaXStart(wptr)      = 0;   

  EZ_IWorkAreaHScroll(wptr)     = (EZ_Widget *)NULL;   
  EZ_IWorkAreaVScroll(wptr)     = (EZ_Widget *)NULL;   

  EZ_IWorkAreaWorkArea(wptr)    = (EZ_Widget *)NULL;      

  EZ_IWorkAreaDisplayed(wptr) = 0;
  EZ_IWorkAreaItemMovable(wptr) = 1;
  EZ_IWorkAreaDrawn(wptr) = 0;

  EZ_IWorkAreaGeometryManager(wptr) = 
    (EZ_WorkAreaGeometryManager) EZ_WorkAreaGeometryManagerDefault;
  EZ_IWorkAreaGMData(wptr)  = (void *)NULL;

  EZ_IWorkAreaData(wptr)  = (EZ_Item **)NULL;
  EZ_IWorkAreaDataSpace(wptr)  = 0;

  EZ_GrowIWorkAreaDataSpace(wptr, 64);

  EZ_CreateTimer(0,310000,-1, (EZ_CallBack)IWATimerCallBack, wptr, 0);

  EZ_SetWidgetFocusableFlag(wptr);  
  
  EZ_Insert2DnDWidgetList(wptr);
  EZ_WidgetDnDHandler(wptr) = (EZ_DnDMsgHandler)EZ_IWorkAreaDnDHandle;

  return(wptr);
}
/************************************************************************************************/
void  EZ_GrowIWorkAreaDataSpace(wptr, incr)
     EZ_Widget *wptr;
     int incr;
{
  int aval = EZ_IWorkAreaDataSpace(wptr);
  int nitems = EZ_IWorkAreaNItems(wptr);
  EZ_Item **oldData = EZ_IWorkAreaData(wptr);

  if(nitems + incr + 4 > aval)
    {
      int i;
      if(oldData) 
	oldData = (EZ_Item **)my_realloc(oldData, (nitems+incr+64) * sizeof(EZ_Item *), _IWORK_AREA_DATA_);
      else 
	oldData = (EZ_Item **)  my_malloc( (nitems+incr+64) * sizeof(EZ_Item *), _IWORK_AREA_DATA_);
      if( oldData == NULL)  EZ_OutOfMemory("EZ_CreateIWorkArea");
      EZ_IWorkAreaData(wptr)  = oldData;
      EZ_IWorkAreaDataSpace(wptr)  = nitems+incr+64;

      for(i = aval; i < nitems+incr+64; i++) oldData[i] = (EZ_Item *)NULL;
    }
}
/************************************************************************************************/
void   EZ_ComputeWidgetIWorkAreaSize(widget, w, h)
     EZ_Widget *widget;
     int             *w, *h;
{
  int cw, ch;

  if(!(EZ_GetWidgetSizeComputedFlag(widget)))
    { 
      EZ_SetWidgetSizeComputedFlag(widget);
    }
  cw = EZ_WidgetPadX(widget) + EZ_WidgetBorderWidth(widget) + EZ_WidgetPadB(widget);
  ch = EZ_WidgetPadY(widget) + EZ_WidgetBorderWidth(widget) + EZ_WidgetPadB(widget);
  cw = cw + cw + 30;
  ch = ch + ch + 30;

  EZ_IWorkAreaXPageSize(widget)     = 30; 
  EZ_IWorkAreaYPageSize(widget)     = 30; 

  EZ_IWorkAreaDirty(widget) = 1;

  *w = cw;
  *h = ch;
}

/************************************************************************************************/
void  EZ_FreeWidgetIWorkAreaData(widget)
     EZ_Widget *widget;
{
  int i, nitems = EZ_IWorkAreaNItems(widget);
  EZ_Item **oldData = EZ_IWorkAreaData(widget);

  for(i = 0; i < nitems; i++)
    EZ_DestroyItem(oldData[i]);
  (void)my_free( (char *)oldData);
}
/************************************************************************************************/

void EZ_DrawWidgetIWorkArea(wptr)
     EZ_Widget *wptr;
{
  int             w, h, bw, padb, padb2;
  Pixmap          pixmap;
  Window          win;
  GC              gc;
  unsigned long   bgpv;

  win = EZ_WidgetWindow(wptr);
  w   = EZ_WidgetWidth(wptr);
  h   = EZ_WidgetHeight(wptr);
  bw = EZ_WidgetBorderWidth(wptr);
  padb = EZ_WidgetPadB(wptr);
  padb2 = padb + padb;

  /*-----------------------------------------------------------
   *  Create a pixmap, draw into this pixmap.
   *----------------------------------------------------------*/
  pixmap = XCreatePixmap(EZ_Display, win, w, h, EZ_Depth);    
  {
    EZ_Bitmap *bitmap = EZ_WidgetPixmap(wptr);
    if(bitmap && EZ_BitmapType(bitmap) == EZ_BITMAP_IS_PIXMAP)
      {
	int xx = EZ_IWorkAreaXStart(wptr);
	int yy = EZ_IWorkAreaYStart(wptr);
	gc = EZ_TILEGC;
	XSetTile(EZ_Display,gc,bitmap->pixmap);
	XSetTSOrigin(EZ_Display, gc, xx,yy);
      }
    else if(EZ_IWorkAreaTextBG(wptr) != ~0)
      {
	XSetForeground(EZ_Display, EZ_WRITABLEGC, EZ_IWorkAreaTextBG(wptr));	
	gc = EZ_WRITABLEGC;
      }
    else EZ_GetBackgroundGC(wptr, &gc, &bgpv, 0, 0);
  }
  XFillRectangle(EZ_DisplayForWidgets, pixmap, gc, padb,padb, w -padb2, h-padb2); 
  /* display the lists */
  if(EZ_IWorkAreaNItems(wptr) > 0)
    {
      EZ_Item **data = EZ_IWorkAreaData(wptr);
      int nitems = EZ_IWorkAreaNItems(wptr); 
      int Wsize, Hsize;
      int xoff = EZ_WidgetBorderWidth(wptr) + EZ_WidgetPadX(wptr) + padb;
      int yoff = EZ_WidgetBorderWidth(wptr) + EZ_WidgetPadY(wptr) + padb;
      int hspacing = EZ_WidgetSepX(wptr);
      int vspacing = EZ_WidgetSepY(wptr);
      
      int x, y, i;
      int Rx = bw+padb;
      int Ry = bw+padb;
      int Rw = w -(Rx << 1);
      int Rh = h -(Rx << 1);

      EZ_IWorkAreaXPageSize(wptr) = w;
      EZ_IWorkAreaYPageSize(wptr) = h;

      if(EZ_IWorkAreaDirty(wptr))
	{
	  /* if there is a geom manager, call it whenever Dirty is set */
	  if(EZ_IWorkAreaGeometryManager(wptr))
	    {
	      (EZ_IWorkAreaGeometryManager(wptr))(EZ_IWorkAreaGMData(wptr),
						  data, nitems,
						  Rx,Ry,(w - (xoff << 1)), (h - (yoff << 1)),
						  hspacing, vspacing,  &Wsize, &Hsize);
	      EZ_IWorkAreaDisplayed(wptr) = 1;
	    }
	  else  /* otherwise, compute the geomtry only once */
	    {
	      if(EZ_IWorkAreaDisplayed(wptr) == 0)
		{
		  EZ_IWorkAreaDisplayed(wptr) = 1;
		  EZ_WorkAreaGeometryManagerDefault(NULL,
						    data, nitems, Rx,Ry,(w - (xoff << 1)), (h - (yoff << 1)),
						    hspacing, vspacing, &Wsize, &Hsize);
		};
	    }
	  
	  EZ_IWorkAreaXSize(wptr) = Wsize + (xoff << 1);
	  EZ_IWorkAreaYSize(wptr) = Hsize + (yoff << 1);
	  EZ_IWorkAreaDirty(wptr)= 0;
	  /* fix XStart and YStart */
	  if(EZ_IWorkAreaYStart(wptr) > 0) EZ_IWorkAreaYStart(wptr) = 0;
	  else
	    {
	      y = EZ_WidgetHeight(wptr) - EZ_IWorkAreaYSize(wptr);
	      if(y < 0 && EZ_IWorkAreaYStart(wptr) < y) EZ_IWorkAreaYStart(wptr) = y;
	      else if(y > 0)  EZ_IWorkAreaYStart(wptr) = 0;
	    }

	  if(EZ_IWorkAreaXStart(wptr) > 0) EZ_IWorkAreaXStart(wptr) = 0;
	  else
	    {
	      x = EZ_WidgetWidth(wptr) - EZ_IWorkAreaXSize(wptr);
	      if(x < 0 && EZ_IWorkAreaXStart(wptr) < x) EZ_IWorkAreaXStart(wptr) = x;
	      else if(x > 0) EZ_IWorkAreaXStart(wptr) = 0;
	    }
	}

      x = EZ_IWorkAreaXStart(wptr) + xoff;
      y = EZ_IWorkAreaYStart(wptr) + yoff;

      for(i = 0; i < nitems; i++)
	{
	  EZ_Item *item = data[i];
	  if(item)
	    {
	      int x0 = EZ_ItemAOriginX(item);
	      int y0 = EZ_ItemAOriginY(item);
	      EZ_DisplayOneItem(wptr, pixmap, x+x0, y+y0, item, Rx, Ry, Rw,Rh);
	    }
	}
      /* highlight selection */
      {
	EZ_Item *sitem = EZ_IWorkAreaSelection(wptr);
	if(sitem)
	  {
	    unsigned long fg = EZ_IWorkAreaHighlightFG(wptr);
	    unsigned long bg = EZ_IWorkAreaHighlightBG(wptr);
	    EZ_HighlightOneItem(wptr, pixmap, sitem, fg, bg, Rx,Ry,Rw,Rh);
	  }
      }
    }
  if(padb > 0)
    {
      EZ_GetParentBgGCN(wptr, &gc,&bgpv);
      XFillRectangle(EZ_Display, pixmap, gc, 0,0, w, padb);
      XFillRectangle(EZ_Display, pixmap, gc, 0,h-padb, w, padb);       
      XFillRectangle(EZ_Display, pixmap, gc, 0,padb, padb, h-padb2); 
      XFillRectangle(EZ_Display, pixmap, gc, w-padb,padb, padb, h-padb2); 
    }
  EZ_DrawRectBorder(wptr, pixmap);
  XCopyArea(EZ_Display,pixmap,win, EZ_WRITABLEGC,0,0,w,h,0,0); 
  XFreePixmap(EZ_DisplayForWidgets, pixmap); 

  update_iwa_scrollbars(wptr);

  if(EZ_IWorkAreaDrawn(wptr) == 0)
    {
      EZ_Item *nitem = EZ_IWorkAreaSelection(wptr);
      EZ_IWorkAreaDrawn(wptr) = 1;
      if(nitem) EZ_MakeIWASelectionVisible(wptr, NULL, nitem);
    }
}

static void  update_iwa_scrollbars(wptr)
     EZ_Widget *wptr;
{
  if(EZ_IWorkAreaHScroll(wptr) != (EZ_Widget *)NULL)
    {
      EZ_UpdateScrollbarI(EZ_IWorkAreaHScroll(wptr),
			  EZ_IWorkAreaXSize(wptr),
			  EZ_IWorkAreaXPageSize(wptr),
			  EZ_IWorkAreaXStart(wptr));
    }

  if(EZ_IWorkAreaVScroll(wptr) != (EZ_Widget *)NULL)
    {
      EZ_UpdateScrollbarI(EZ_IWorkAreaVScroll(wptr),
			  EZ_IWorkAreaYSize(wptr),
			  EZ_IWorkAreaYPageSize(wptr),
			  -EZ_IWorkAreaYStart(wptr));
    }
}
/*****************************************************************************/
void EZ_IWorkAreaEventHandle(widget, event)
     EZ_Widget *widget;
     XEvent          *event;
{
  XEvent    xevent;
  int       Ox, Oy, oldx, oldy, dx,dy, tx,ty,x, y, yoffset, xoffset, done, padb;
  int       ix0,iy0,iww,ihh, iarea, snap = 0, moved = 0,iax0,iay0, Sx,Sy, rootx,rooty;
  int       bw, Rx,Ry,Rw,Rh, ww, hh,wox,woy, grabed;
  Window    root, child;
  unsigned  int    mask;

  if(event->type == Expose)   EZ_DrawWidget(widget);      
  if( EZ_GetWidgetDisabledFlag(widget) )  return;

  switch(event->type)
    {
    case SelectionRequest:
      EZ_HandleSelectionRequest(widget,event);
      break;
    case ButtonPress:
      if(event->xbutton.button == EZ_Btn1) 	  /* get keyboard focus */
	{ 
	  Window fw; int rt;
	  EZ_Widget *tmp;
	  XGetInputFocus(EZ_Display, &fw, &rt);
	  tmp = EZ_LookupWidgetFromMappedHT(fw);
	  if(tmp != widget) EZ_SetFocusTo(widget);
	}
      if(EZ_IWorkAreaNItems(widget) <= 0) {return;}
      if(event->xbutton.button == EZ_Btn1)
	{ /* button 1 */
	  Time  time_stamp = event->xbutton.time;   
	  int   elapsedtime = EZ_LastButton1ClickTime - time_stamp; 
	  EZ_Item *ositem, *nsitem;
	  EZ_Item **data = EZ_IWorkAreaData(widget);
	  int nitems = EZ_IWorkAreaNItems(widget);
	  
	  EZ_LastButton1ClickTime = time_stamp;
	  
	  padb = EZ_WidgetPadB(widget);
	  bw = EZ_WidgetBorderWidth(widget) + padb;
	  xoffset = bw + EZ_WidgetPadX(widget);
	  yoffset = bw + EZ_WidgetPadY(widget);

	  Rx = bw;
	  Ry = bw;
	  ww = EZ_WidgetWidth(widget);
	  Rw = ww - (bw << 1);
	  hh = EZ_WidgetHeight(widget);
	  Rh = hh -(bw << 1);

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

	  ositem = EZ_IWorkAreaSelection(widget);
	  nsitem = EZ_FindPointerOnItemIWA(data, nitems, ositem, x,y);
	  
	  if(nsitem != NULL) /* click is  on an item */
	    {
	      if(nsitem != ositem)
		{
		  EZ_IWorkAreaSelection(widget) = nsitem;
		  if(ositem) EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),ositem, Rx,Ry,Rw,Rh);
		  if(1)
		    {
		      unsigned long fg = EZ_IWorkAreaHighlightFG(widget);
		      unsigned long bg = EZ_IWorkAreaHighlightBG(widget);
		      EZ_HighlightOneItem(widget, EZ_WidgetWindow(widget),
					  nsitem, fg, bg, Rx,Ry,Rw,Rh);
		    }
		  EZ_HandleMotionCallBacks(widget,EZ_WidgetMotionCallBack(widget));
		}
	      /*
	       * a double click cause the execuation of the call back function
	       */
	      if(nsitem && ABSV(elapsedtime) < DOUBLE_CLICK_TIME_INTERVAL)
		{ 
		  EZ_HandleItemCallBacks(nsitem);
		  EZ_ExecuteWidgetCallBacks(widget);
		  return;
		}
	    
	      /* to make gcc happy */
	      {	Ox = Oy = 0; oldx = oldy = 1; iww = ihh = 1; }

	      /* waiting for a button release */

	      if(EZ_IWorkAreaItemMovable(widget)) /* not a drag src */
		{
		  ix0 = iy0 = iax0 = iay0 = Sx = Sy = iarea = 0; /* */
		  done = 0;
		  moved = 0;   /* catch the first motion */
		  grabed = 0; /* has picked up an item or not */
		  while(!done)
		    {
		      do {
			XNextEvent(EZ_Display, &xevent); 
			if(EZ_FilterEvent(&xevent))
			  EZ_InvokePrivateEventHandler(&xevent);
			
			if(xevent.type == MotionNotify) moved = 1;
			else if(xevent.type == ButtonRelease)
			  {
			    if(xevent.xbutton.button == EZ_Btn1) { done = 1; break;}
			  }
			else if(xevent.type == Expose || xevent.type == FocusIn || xevent.type == FocusOut)  
			  EZ_WidgetDispatchEvent(&xevent);
		      } while(XPending(EZ_Display) && !done);
		      
		      if(!done) /* not done yet */
			{
			  if(moved)
			    {
			      if(grabed == 0)  /* pointer moved, start drag */
				{
				  XTranslateCoordinates(EZ_Display, EZ_WidgetWindow(widget),
							RootWindow(EZ_Display, EZ_ScreenNum),
							0, 0,
							&wox,&woy, &child);
				  ix0 = EZ_ItemOriginX(nsitem);
				  iy0 = EZ_ItemOriginY(nsitem);
				  iax0 = EZ_ItemAOriginX(nsitem);
				  iay0 = EZ_ItemAOriginY(nsitem);
				  iww = EZ_ItemAWidth(nsitem);
				  ihh = EZ_ItemAHeight(nsitem);
				  iarea  = iww * ihh;
				  
				  oldx = wox + x;    /* pointer position in Root */
				  oldy = woy + y;
				  tx = ix0 - x;     /* offset: item origin and pointer position */
				  ty = iy0 - y;
				  Sx = Ox = oldx + tx;
				  Sy = Oy = oldy + ty;
				  
				  /* create a snapshot of the item */
				  if(iarea < 40000 && EZ_MakeSnapShot(widget,1,ix0,iy0,iww,ihh)) snap = 1; 
				  grabed = 1;
				}
			      XQueryPointer(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
					    &root,       /* root return win */
					    &child,      /* child ret win   */
					    &rootx, &rooty,   /* x, y in root    */
					    &tx,&ty,            /* x, y in win     */
					    &mask);  
			      dx = rootx - oldx;  dy = rooty - oldy;
			      oldx = rootx;   oldy = rooty;
			      
			      if(dx | dy)
				{
				  Ox += dx; Oy += dy;
				  EZ_MoveSnapShotOrOutline(snap, Ox, Oy, iww, ihh, 1);
				  EZ_MoveSnapShotOrOutline(snap, Ox, Oy, iww, ihh, 0);
				  XFlush(EZ_Display);
				}
			    }/* moved */
			} /* not done */
		      else /* done */
			{
			  if(grabed) /* dragged */
			    {
			      int nx,ny,pw,ph;
			      int inside = 1;

			      EZ_MoveSnapShotOrOutline(snap, 0,0,0,0,1);
			      XFlush(EZ_Display);

			      pw = EZ_WidgetWidth(widget);
			      ph = EZ_WidgetHeight(widget);
			      dx = Sx - Ox;
			      dy = Sy - Oy;

			      nx = ix0 - dx;
			      ny = iy0 - dy;
			      {
				int x,y,w,h;
				EZ_IntersectRectangles(0,0,pw,ph,nx,ny,iww,ihh, &x,&y, &w, &h);
				if( ((w * h) << 2) <= iww * ihh) inside = 0; /* > 3/4 is out */
			      }
			      if(inside == 0)
				{
				  int i, count, tx, ty,txx,tyy,dxx,dyy,sx,sy;
                                  if(iarea < 1000) count = 128;
                                  else if(iarea < 2000) count = 64;
                                  else if(iarea < 3000) count = 32;
                                  else if(iarea < 10000) count = 16;
                                  else if(iarea < 20000) count = 8;
                                  else count = 4;

                                  if(snap == 0) count = count >> 2;

                                  i = (ABSV(dx) + ABSV(dy)) >> 8;
                                  count *= (i + 1);

                                  tx = dx/count; dxx = dx - tx * count;
                                  ty = dy/count; dyy = dy - ty * count;

                                  sx = sy = 1;
                                  if(dxx < 0) { sx= -1; dxx = -dxx;}
				  if(dyy < 0) { sy= -1; dyy = -dyy;}
                                  
                                  txx = tyy = 0;
                                  for(i = 0; i < count; i++)
                                    {
                                      txx += dxx;  if(txx >= count) { txx -= count; Ox += sx;}
                                      tyy += dyy;  if(tyy >= count) { tyy -= count; Oy += sy;}
                                      Ox += tx;
                                      Oy += ty;
                                      EZ_MoveSnapShotOrOutline(snap, Ox, Oy, iww, ihh, 1);
                                      EZ_MoveSnapShotOrOutline(snap, Ox, Oy, iww, ihh, 0);
                                      XFlush(EZ_Display);
                                    }
                                  EZ_MoveSnapShotOrOutline(snap, 0, 0, 0, 0, 1);
                                }
			      else
				{
				  if(EZ_ItemType(nsitem) != EZ_WIDGET_ITEM)
				    MoveIWorkAreaItem(widget, nsitem, nx, ny,
						      iax0 - dx, iay0 - dy, Rx,Ry,Rw,Rh);
				  else /* widget_item is troublesome, */
				    {
				      EZ_ItemOriginX(nsitem) = nx;
				      EZ_ItemOriginY(nsitem) = ny;
				      EZ_ItemAOriginX(nsitem) = iax0 - dx;
				      EZ_ItemAOriginY(nsitem) = iay0 - dy;
				      EZ_DrawWidget(widget);
				    }
				}
			    }
			}
		    } /* while */
		}
	    } /* nsitem != NULL */
	  else /* click is on the background, do scroll */
	    {
	      done = 0;
	      xevent.type = 0;
	      while(!done)
		{
		  if(XPending(EZ_Display))
		    {
		      while(XPending(EZ_Display))
			{
			  if(xevent.type == MotionNotify)
			    {
			      x = xevent.xmotion.x;
			      y = xevent.xmotion.y;
			    }
			  else if(xevent.type == ButtonRelease)
			    {
			      if( xevent.xbutton.button == EZ_Btn1) break;
			    }
			  else if(xevent.type == Expose || xevent.type == FocusIn || xevent.type == FocusOut) 
			    EZ_WidgetDispatchEvent(&xevent);
			  XNextEvent(EZ_Display, &xevent);
			  if(EZ_FilterEvent(&xevent))
			    EZ_InvokePrivateEventHandler(&xevent);
			}
		    }
		  else { EZ_SitForALittleBit(50000); }
		  if(xevent.type == ButtonRelease && xevent.xbutton.button == EZ_Btn1) done = 1;	      
		  
		  if( x < xoffset || x > ww - xoffset ||
		     y <= yoffset  || y >hh - yoffset)
		    {
		      if(y <= yoffset)
			{
			  /* scroll down */
			  if(EZ_IWorkAreaYStart(widget) < 0)
			    {
			      int tmp = EZ_IWorkAreaYStart(widget) + 10;
			      EZ_IWorkAreaYStart(widget) = (tmp < 0 ? tmp: 0);
			      EZ_DrawWidget(widget);
			    }
			}
		      else if( y > EZ_WidgetHeight(widget) - yoffset)
			{
			  /* scroll up */ 
			  int  th = EZ_IWorkAreaYSize(widget);
			  if(th > EZ_WidgetHeight(widget))
			    {
			      int dif = EZ_WidgetHeight(widget) - th;
			      int tmp = EZ_IWorkAreaYStart(widget) - 10;
			      int nOff =  (tmp > dif ? tmp: dif);
			      if(EZ_IWorkAreaYStart(widget) != nOff)
				{
				  EZ_IWorkAreaYStart(widget) = nOff;
				  EZ_DrawWidget(widget);
				}	  
			    }
			}
		      else if(x <= xoffset)
			{
			  /* scroll right */
			  if(EZ_IWorkAreaXStart(widget) < 0)
			    {
			      int tmp = EZ_IWorkAreaXStart(widget) + 10;
			      EZ_IWorkAreaXStart(widget) = (tmp < 0 ? tmp: 0);
			      EZ_DrawWidget(widget);
			    }
			}
		      else if( x > EZ_WidgetWidth(widget) - xoffset)
			{
			  /* scroll left */ 
			  int  tw = EZ_IWorkAreaXSize(widget);
			  if(tw > EZ_WidgetWidth(widget))
			    {
			      int dif = EZ_WidgetWidth(widget) - tw;
			      int tmp = EZ_IWorkAreaXStart(widget) - 10;
			      int nOff = (tmp > dif ? tmp: dif);
			      if(EZ_IWorkAreaXStart(widget) != nOff)
				{
				  EZ_IWorkAreaXStart(widget) = nOff;
				  EZ_DrawWidget(widget); 
				}
			    }
			}
		    }
		}
	    }
	}
      else if(event->xbutton.button == EZ_Btn3)
	{ /* button 3 */
	  Window ptWin = event->xbutton.window;
	  Window oldPtWin = ptWin;
	  Window widgetwin = ptWin;
	  unsigned int actionType = (event->xbutton.state) & (ShiftMask | ControlMask | Mod1Mask);
	  int dragCancelled = 0;
	  int helpRequested = 0;
	  EZ_Item *ositem, *nsitem;
	  EZ_Item **data = EZ_IWorkAreaData(widget);
	  int nitems = EZ_IWorkAreaNItems(widget);
	  
	  bw = EZ_WidgetBorderWidth(widget) + EZ_WidgetPadB(widget);
	  xoffset = bw + EZ_WidgetPadX(widget);
	  yoffset = bw + EZ_WidgetPadY(widget);

	  Rx = bw;
	  Ry = bw;
	  ww = EZ_WidgetWidth(widget);
	  Rw = ww - (bw << 1);
	  hh = EZ_WidgetHeight(widget);
	  Rh = hh -(bw << 1);

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

	  ositem = EZ_IWorkAreaSelection(widget);
	  nsitem = EZ_FindPointerOnItemIWA(data, nitems, ositem, x,y);
	  
	  if( nsitem != NULL) /* click is  on an item */
	    {
	      if(nsitem != ositem)
		{
		  EZ_IWorkAreaSelection(widget) = nsitem;
		  if(ositem) EZ_UpdateOneItem(widget,widgetwin,ositem, Rx,Ry,Rw,Rh);
		  if(nsitem)
		    {
		      unsigned long fg = EZ_IWorkAreaHighlightFG(widget);
		      unsigned long bg = EZ_IWorkAreaHighlightBG(widget);
		      EZ_HighlightOneItem(widget, widgetwin,
					  nsitem, fg, bg, Rx,Ry,Rw,Rh);
		    }
		  EZ_HandleMotionCallBacks(widget,EZ_WidgetMotionCallBack(widget));
		}
	    
	      /* to make gcc happy */
	      {	Ox = Oy = 0; oldx = oldy = 1; iww = ihh = 1; }
	      
	      /* waiting for a button release */

	      if(nsitem && EZ_ItemIsDnDSrc(nsitem))
		{
		  unsigned long serial = 0L;
		  int newEvent = 0;
		  done = 0;
		  moved = 0;   /* catch the first motion */
		  grabed = 0; /* has picked up an item or not */
		  while(!done)
		    {
		      while(XPending(EZ_Display) && !done)
			{
			  XNextEvent(EZ_Display, &xevent); 
			  if(EZ_FilterEvent(&xevent))
			    EZ_InvokePrivateEventHandler(&xevent);
			  newEvent = 1;

			  if(xevent.type == MotionNotify)
			    {
			      moved = 1; 
			      serial = xevent.xmotion.serial;
			      ptWin = xevent.xmotion.window; 
			      if(xevent.xmotion.window == widgetwin)
				{
				  if(xevent.xmotion.x < 0 ||  xevent.xmotion.y < 0 ||
				     xevent.xmotion.x >= EZ_WidgetWidth(widget) ||  
				     xevent.xmotion.y >= EZ_WidgetHeight(widget))
				    ptWin = 0L;
				}
			      if(helpRequested)
				{
				  EZ_DnDSendCancelHelpMsg();
				  EZ_DnDInfo.ptWin = (unsigned long)ptWin;
				  helpRequested = 0;
				}
			    }
			  else if(xevent.type == ButtonRelease)
			    {
			      if(xevent.xbutton.button == EZ_Btn3) 
				{
				  ptWin = xevent.xbutton.window; 
				  if(xevent.xbutton.window == widgetwin)
				    {
				      if(xevent.xbutton.x < 0 ||  xevent.xbutton.y < 0 ||
					 xevent.xbutton.x >= EZ_WidgetWidth(widget) ||  
					 xevent.xbutton.y >= EZ_WidgetHeight(widget))
					ptWin = 0L;
				    }
				  done = 1; 
				  break;
				}
			    }
			  else if(xevent.type == KeyPress)			
			    {
			      KeySym            keysym;
			      XComposeStatus    compose; 
			      char              tmpbuffer[4];
			      int               count;
			      count = XLookupString(&(xevent.xkey), tmpbuffer, 4, &keysym, &compose);
			      switch(keysym)
				{
				case XK_F1: 
#ifdef XK_KP_F1
				case XK_KP_F1:  /* help */
#endif
				  if(grabed && helpRequested == 0 && ptWin != 0)
				    {
				      EZ_DnDInfo.ptWin = (unsigned long)ptWin;
				      EZ_DnDSendRequestHelpMsg();
				      helpRequested = 1;
				    }
				  break;
				case XK_Escape:   /* cancel */
				  {
				    dragCancelled = 1;
				    done = 1;
				  }
				break;
				default:
				  break;
				}
			    }
			  else if(xevent.type == Expose || xevent.type == FocusIn || xevent.type == FocusOut)  
			    EZ_WidgetDispatchEvent(&xevent);
			  
			  if(oldPtWin != ptWin)
			    {
			      if(grabed && oldPtWin != 0) 
				{
				  unsigned long twin = EZ_DnDInfo.ptWin;
				  EZ_DnDInfo.ptWin = (unsigned long)(oldPtWin);
				  EZ_DnDSendLeaveWindowMsg();
				  EZ_DnDInfo.ptWin = twin;
				}
			      oldPtWin = ptWin;
			    }
			}
		      /* check the timer */
		      if(newEvent == 0)
			{
			  EZ_CheckDnDTimer();
			  EZ_SitForALittleBit(1000);
			}
		      
		      if(!done && newEvent) /* not done yet */
			{
			  newEvent = 0;
			  if(moved)
			    {
			      if(grabed == 0)  /* pointer moved, start drag */
				{
				  XTranslateCoordinates(EZ_Display, widgetwin,
							RootWindow(EZ_Display, EZ_ScreenNum),
							0, 0, &wox,&woy, &child);
				  ix0 = EZ_ItemOriginX(nsitem);
				  iy0 = EZ_ItemOriginY(nsitem);
				  iax0 = EZ_ItemAOriginX(nsitem);
				  iay0 = EZ_ItemAOriginY(nsitem);
				  iww = EZ_ItemAWidth(nsitem);
				  ihh = EZ_ItemAHeight(nsitem);
				  iarea  = iww * ihh;
				  
				  oldx = wox + x;    /* pointer position in Root */
				  oldy = woy + y;
				  tx = ix0 - x;     /* offset: item origin and pointer position */
				  ty = iy0 - y;
				  Sx = Ox = oldx + tx;
				  Sy = Oy = oldy + ty;
				  
				  EZ_InitDrag(serial, EZ_DND_OBJ_IS_ITEM, widgetwin,
					      widget, nsitem, Sx,Sy, iww, ihh, oldx, oldy, tx, ty);
				  if(EZ_DnDInfo.id == serial)
				    {
				      Cursor cursor = EZ_ItemDnDDragCursor(nsitem);
				      if(cursor != (Cursor ) NULL)
					{ 
					  snap = EZ_DND_DRAG_ICON_CURSOR;
					  EZ_DnDInfo.cursor = cursor;
					}
				      else if(iarea < 40000)
					snap = EZ_MakeSnapShot(widget,1,ix0,iy0,iww,ihh);
				      else snap = EZ_DND_DRAG_ICON_OUTLINE;
				      {
					EZ_DnDInfo.actionType = actionType;
					EZ_DnDInfo.px = oldx;
					EZ_DnDInfo.py = oldy;
					EZ_DnDInfo.dragIconType = snap;
					{
					  char *smsg; int smsg_len;
					  EZ_GenerateDragStartedMessage(&smsg, &smsg_len);
					  EZ_BroadcastDnDMessage(EZ_DND_DRAG_START,smsg, smsg_len, 0);
					}
					/*EZ_BroadcastDnDMessage(EZ_DND_DRAG_START,NULL, 0, 0);*/
				      }
				      if(EZ_PointerGrabed == 0)  /* grab the pointer ! */
					{
					  if(XGrabPointer(EZ_Display,widgetwin, True,
							  ButtonReleaseMask|ButtonPressMask|PointerMotionMask,
							  GrabModeAsync, GrabModeAsync, None, 
							  (cursor == (Cursor)NULL)?None : cursor, CurrentTime)
					     == GrabSuccess) EZ_PointerGrabed = 1;
					}
				      grabed = 1;
				    }
				}
			      if(grabed)
				{
				  XQueryPointer(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
						&root,       /* root return win */
						&child,      /* child ret win   */
						&rootx, &rooty,   /* x, y in root    */
						&tx,&ty,            /* x, y in win     */
						&mask);  
				  dx = rootx - oldx;  dy = rooty - oldy;
				  oldx = rootx;   oldy = rooty;
				  
				  if(dx | dy)
				    {
				      Ox += dx; Oy += dy;
				      if(snap != EZ_DND_DRAG_ICON_CURSOR)
					EZ_MoveSnapShotOrOutline(snap, Ox, Oy, iww, ihh, 1);
				      EZ_DnDInfo.px = oldx;
				      EZ_DnDInfo.py = oldy;
				      EZ_DnDInfo.ptWin = (unsigned long)ptWin;
				      EZ_BroadcastDnDMessage(EZ_DND_DRAG_MOTION, NULL, 0, 0);
				      if(snap != EZ_DND_DRAG_ICON_CURSOR)
					EZ_MoveSnapShotOrOutline(snap, Ox, Oy, iww, ihh, 0);
				    } 
				}
			    }  /* moved */
			}/* not done */
		      else if(done) /* done */
			{
			  if(EZ_PointerGrabed)
			    {
			      XUngrabPointer(EZ_Display,CurrentTime);
			      EZ_PointerGrabed = 0;
			    }
			  if(grabed) /* dragged */
			    {
			      char *message;
			      int length;
			      Window commWin;
			      if(snap != EZ_DND_DRAG_ICON_CURSOR)
				EZ_MoveSnapShotOrOutline(snap, 0,0,0,0,1);
			      if(ptWin != 0) commWin = (Window) EZ_WindowIsDnDTarget(ptWin);
			      else commWin = 0L;
			      
			      /* have to call the next func to release grab !!! */
			      EZ_FinishDrag(commWin, ptWin, oldx, oldy); /* prepare for drop */

			      if(commWin != 0L)
				{
				  if(helpRequested)
				    {
				      EZ_DnDSendCancelHelpMsg();
				      helpRequested = 0;
				    }

				  if(dragCancelled == 0)
				    {
				      EZ_DnDInfo.actionType = actionType;
				      EZ_GenerateDragIsDropedMessage(&message, &length);
				      EZ_SendDnDMessage(EZ_DND_DRAG_DROPPED, message, length, 1);
				    }
				  else  /* cancelled */
				    {
				      EZ_SendDnDMessage(EZ_DND_DRAG_CANCELLED, NULL, 0, 0);
				      EZ_AbortDrag();
				    }
				}
			      else if(ptWin == RootWindow(EZ_Display, EZ_ScreenNum) &&
				      dragCancelled == 0)
				{
				  EZ_DnDDataEncoder  *encoder = EZ_FindSpecialEncoder();
				  if(encoder)
				    {
				      char *msg; int mlen, needFree, ok = EZ_DND_FAILURE;

				      EZ_DnDInfo.srcStatus = EZ_DRAG_CONVERTING;
				      ok = (encoder->encoder) (encoder->object,
							       encoder->data,
							       &msg, &mlen, &needFree);
				      if(ok != EZ_DND_FAILURE)
					{
					  if((encoder->callback).callback)
					    {
					      EZ_DnDInfo.srcStatus = EZ_DRAG_EXECUTING_CALLBACK;
					      ((encoder->callback).callback)(encoder->object, 
									 (encoder->callback).data);
					    }
					  EZ_DnDInfo.id = 0; /* forget about this drag */   
					}
				      else EZ_AbortDrag();
				    }
				  else  EZ_AbortDrag();
				}
			      else EZ_AbortDrag();
			    }
			}
		    } /* while */
		}   /* item is drag src */
	    } /* nsitem != NULL */
	}
      break;
    case EnterNotify:
      break;
    case LeaveNotify:
      break;
    case KeyPress:
      if(EZ_IWorkAreaNItems(widget) <= 0) {return;}
      {
	EZ_Item **data = EZ_IWorkAreaData(widget);
	int nitems = EZ_IWorkAreaNItems(widget);
	int               count;
	KeySym            keysym;
	XComposeStatus    compose; 
	char              tmpbuffer[32];
	int               dir = 0, buffersize = 32;
	count = XLookupString(&(event->xkey), tmpbuffer, buffersize, &keysym, &compose);
	tmpbuffer[count] = '\0'; 

	switch(keysym)
	  {
	  case XK_Up: case XK_k:  case XK_p: case XK_K:  case XK_P: 
#ifdef XK_KP_Up
	  case XK_KP_Up:
#endif
	    dir =  _UP;
	    break;
	  case XK_Down: case XK_n: case XK_j: case XK_N: case XK_J:  
#ifdef XK_KP_Down
	  case XK_KP_Down:
#endif
	    dir = _DOWN;
	    break;
	  case XK_Left: case XK_b:  case XK_h: case XK_B:  case XK_H:  
#ifdef XK_KP_Left
	  case XK_KP_Left:
#endif
	    dir = _LEFT;
	    break;
	  case XK_Right: case XK_f: case XK_l: case XK_F: case XK_L:  
#ifdef XK_KP_Right
	  case XK_KP_Right: 
#endif
	    dir = _RIGHT;
	    break;
	  case XK_Return: case XK_Linefeed: case XK_space: 
#ifdef XK_KP_Enter
	  case XK_KP_Enter:
#endif
	    {
	      EZ_Item *ositem = EZ_IWorkAreaSelection(widget);
	      if(ositem)
		{
		  EZ_HandleItemCallBacks(ositem);
		  EZ_ExecuteWidgetCallBacks(widget);
		}
	    }
	    break;
	  default:
	    break;
	  }
	if(dir != 0)
	  {
	    EZ_Item *ositem = EZ_IWorkAreaSelection(widget);
	    EZ_Item *tmp = EZ_FindClosestItem(data,nitems,ositem, dir);
	    if(tmp && tmp != ositem) 
	      {
		EZ_IWorkAreaSelection(widget) = tmp;
		EZ_MakeIWASelectionVisible(widget, ositem, tmp);
		EZ_HandleMotionCallBacks(widget,EZ_WidgetMotionCallBack(widget));
	      }
	  }
      }
      { 
	XEvent tmpevent; 
	while(XCheckTypedEvent(EZ_Display, KeyPress, &tmpevent))
	  if(EZ_FilterEvent(&tmpevent))
	    EZ_InvokePrivateEventHandler(&tmpevent);
      } 
      break;
    default:
      break;
    }
}
/******************************************************************************************/
EZ_Item   *EZ_FindPointerOnItemIWA(data, nitems, guess, x,y)
     EZ_Item **data, *guess;
     int nitems, x, y;
{
  EZ_Item *item;
  int i;

  if(guess && EZ_PickAnItem(guess,x,y)) return(guess);
  for(i = 0; i < nitems; i++)
    {
      item = data[i];
      if(EZ_PickAnItem(item,x,y)) return(item);
    }
  return(NULL);
}
/******************************************************************************************/
void  EZ_IWorkAreaInsertItem(widget, item)
     EZ_Widget *widget;
     EZ_Item *item;
{
  if(widget && item)
    {
      int nitems;
      EZ_Item **data;

      EZ_GrowIWorkAreaDataSpace(widget, 1);
      
      nitems = EZ_IWorkAreaNItems(widget);
      data = EZ_IWorkAreaData(widget);
      data[nitems] = item;
      EZ_IWorkAreaNItems(widget) += 1;
      EZ_IWorkAreaDirty(widget) = 1;
      if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
    }
}

void  EZ_IWorkAreaInsertItems(widget, items, n)
     EZ_Widget *widget;
     EZ_Item **items; int n;
{
  if(widget && n > 0)
    {
      int nitems, i;
      EZ_Item **data;

      EZ_GrowIWorkAreaDataSpace(widget, n+1);
      
      nitems = EZ_IWorkAreaNItems(widget);
      data = EZ_IWorkAreaData(widget);
      for(i = 0; i < n; i++) data[nitems+i] = items[i];
      EZ_IWorkAreaNItems(widget) += n;
      EZ_IWorkAreaDirty(widget) = 1;
      if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
    }
}

/**********************************************************************************/      
void EZ_IWorkAreaDeleteItem(widget, item)
     EZ_Widget *widget;
     EZ_Item *item;
{
  if(widget && item)
    {
      int nitems = EZ_IWorkAreaNItems(widget);
      EZ_Item **data = EZ_IWorkAreaData(widget);
      int i,j;
      
      for(i = 0; i < nitems; i++)
	{
	  if(data[i] == item) break;
	}
      if(i < nitems)
	{
	  if(item == EZ_IWorkAreaSelection(widget))
	    EZ_IWorkAreaSelection(widget) = (EZ_Item *)NULL;
	  EZ_IWorkAreaDirty(widget) = 1;
	  EZ_DestroyItem(item);
	  for(j = i; j < nitems; j++)
	    data[j] = data[j+1];        /* data always contains > 1 extra slot */
	  if(EZ_IWorkAreaNItems(widget) > 0)
	    EZ_IWorkAreaNItems(widget) -= 1;
	  if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
	}
    }
}

void EZ_IWorkAreaDeleteAllItems(widget)
     EZ_Widget *widget;
{
  if(widget)
    {
      int nitems = EZ_IWorkAreaNItems(widget);
      EZ_Item **data = EZ_IWorkAreaData(widget);
      int i, freezed = EZ_GetWidgetFreezedFlag(widget);

      if(freezed == 0) EZ_FreezeWidget(widget);
      EZ_IWorkAreaNItems(widget) = 0;
      EZ_IWorkAreaSelection(widget) = (EZ_Item *)NULL;
      EZ_IWorkAreaDirty(widget) = 1;

      for(i = 0; i < nitems; i++)
	{ EZ_DestroyItem(data[i]); data[i] = NULL;}

      EZ_IWorkAreaXStart(widget) = 0;
      EZ_IWorkAreaYStart(widget) = 0;

      if(freezed == 0) 
	{
	  EZ_ClearWidgetFreezedFlag(widget);
	  if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
	}
    }
}
/**********************************************************************************/
void  EZ_IWorkAreaInsertItemBeforeAfter(widget, item, newitem, which)
     EZ_Widget *widget;
     EZ_Item *item, *newitem;
     int which;
{
  if(widget)
    {
      int nitems, i,j;
      EZ_Item **data;
      EZ_GrowIWorkAreaDataSpace(widget, 1);
      nitems = EZ_IWorkAreaNItems(widget);
      data = EZ_IWorkAreaData(widget);

      for(i = 0; i < nitems; i++)
	{
	  if(data[i] == item) break;
	}
      if(i < nitems) 
	{  if(which)  i++; } /* after */

      for(j = nitems; j > i; j--)
	data[j] = data[j-1];  /* move */
      data[i] = newitem;
      EZ_IWorkAreaNItems(widget) += 1;
      EZ_IWorkAreaDirty(widget) = 1;
      if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
    }
}

void  EZ_IWorkAreaInsertItemUsingIdx(widget, idx, item)
     EZ_Widget *widget;
     EZ_Item *item;
     int idx;
{
  if(widget)
    {
      int nitems, i;
      EZ_Item **data;
      EZ_GrowIWorkAreaDataSpace(widget, 1);
      nitems = EZ_IWorkAreaNItems(widget);

      if(idx < 0 || idx >= nitems) idx = nitems;

      data = EZ_IWorkAreaData(widget);
      for(i = nitems; i > idx; i--)
	data[i] = data[i-1];  /* move */
      data[idx] = item;
      EZ_IWorkAreaDirty(widget) = 1;
      EZ_IWorkAreaNItems(widget) += 1;
      if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
    }
}

void EZ_IWorkAreaDeleteItemUsingIdx(widget, idx)
     EZ_Widget *widget;
     int idx;
{
  if(widget)
    {
      int nitems = EZ_IWorkAreaNItems(widget);
      EZ_Item *item, **data = EZ_IWorkAreaData(widget);
      int j;
      
      if(idx < 0 || idx >= nitems) return;
      item = data[idx];
      if(item == EZ_IWorkAreaSelection(widget))
	EZ_IWorkAreaSelection(widget) = (EZ_Item *)NULL;
      EZ_IWorkAreaDirty(widget) = 1;
      EZ_DestroyItem(item);
      for(j = idx; j < nitems; j++)
	data[j] = data[j+1];        /* data always contains > 1 extra slot */
      if(EZ_IWorkAreaNItems(widget) > 0)
	EZ_IWorkAreaNItems(widget) -= 1;
      if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
    }
}

void EZ_IWorkAreaDeleteSomeItemsUsingIndices(widget, indices, count)
     EZ_Widget *widget;
     int *indices, count;
{
  if(widget)
    {
      int nitems = EZ_IWorkAreaNItems(widget);
      EZ_Item *item, *selection, **data = EZ_IWorkAreaData(widget);
      int idx, i, j, deleted = 0;
      
      selection = EZ_IWorkAreaSelection(widget);
      for(i = 0; i < count; i++)
	{
	  idx = indices[i];

	  if(idx >= 0 && idx < nitems) 
	    {
	      if((item = data[idx]) != NULL)
		{
		  if(item == selection)
		    { EZ_IWorkAreaSelection(widget) = (EZ_Item *)NULL; selection = NULL;}
		  EZ_DestroyItem(item);
		  deleted++;
		  data[idx] = NULL;
		}
	    }
	}
      if(deleted)
	{
	  EZ_IWorkAreaDirty(widget) = 1;
	  i = 0, j = 0; 
	  while(j < nitems)
	    {
	      while(data[i] != NULL) i++;
	      j = (j > i? j: i);
	      while( j < nitems && data[j] == NULL) j++;
	      if(j < nitems)
		{
		  data[i] = data[j];
		  data[j] = NULL;
		  j++;
		  i++;
		}
	    }
	  j = nitems - deleted;
	  EZ_IWorkAreaNItems(widget) = (j >= 0? j: 0);
	  if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
	}
    }
}
/**********************************************************************************/
static void IWATimerCallBack(timer, cdata)
     EZ_Timer *timer; 
     void *cdata;
{
  EZ_Widget *widget = (EZ_Widget *)cdata;
  if(widget && EZ_LookupWidgetFromAllHT(widget) == widget &&
     EZ_WidgetType(widget) == EZ_WIDGET_IWORK_AREA)
    {
      if(EZ_WidgetWindow(widget) && EZ_WidgetMapped(widget) &&
	 EZ_IWorkAreaNItems(widget) > 0)
	{
	  EZ_Item *sitem = EZ_IWorkAreaSelection(widget);
	  if(sitem)
	    {
	      int bw = EZ_WidgetBorderWidth(widget) + EZ_WidgetPadB(widget);
	      int Rx = bw;
	      int Ry = bw;
	      int Rw = EZ_WidgetWidth(widget) - (Rx+Rx);
	      int Rh = EZ_WidgetHeight(widget) -(Rx+Rx);
	      EZ_FlashItemOutline(widget,sitem, Rx,Ry,Rw,Rh);
	    }
	}
    }
  else EZ_CancelTimer(timer);
}
/**********************************************************************************/
static void  MoveIWorkAreaItem(widget, item, nx, ny, ax, ay, Rx,Ry,Rw,Rh)
     EZ_Widget *widget;               /* assuming widget and item are not NULL */
     EZ_Item *item;
     int nx,ny,ax,ay,Rx,Ry,Rw,Rh;
{
  Pixmap          pixmap;
  Window          win;
  GC              gc;
  unsigned long   bgpv;
  int             x,y,w,h;
  EZ_Bitmap *bitmap = EZ_WidgetPixmap(widget);
  unsigned long fg = EZ_IWorkAreaHighlightFG(widget);
  unsigned long bg = EZ_IWorkAreaHighlightBG(widget);
  w = EZ_ItemAWidth(item);
  h = EZ_ItemAHeight(item);

  /* 2-13-97: if item is not completely within the border, redraw
   * the whole widget to update the border
   */
  if(nx < Rx || ny < Ry || nx+w > Rx+Rw || ny+h > Ry+Rh)
    {
      EZ_ItemOriginX(item) = nx;
      EZ_ItemOriginY(item) = ny;
      EZ_ItemAOriginX(item) = ax;
      EZ_ItemAOriginY(item) = ay;
      EZ_DrawWidget(widget);
      return;
    }

  win = EZ_WidgetWindow(widget);
  x = EZ_ItemOriginX(item);
  y = EZ_ItemOriginY(item);

  pixmap = XCreatePixmap(EZ_Display, win, w, h, EZ_Depth);    

  /* I: remove item from its old position */
  if(bitmap && EZ_BitmapType(bitmap) == EZ_BITMAP_IS_PIXMAP)
    {
      int xx = EZ_IWorkAreaXStart(widget);
      int yy = EZ_IWorkAreaYStart(widget);
      gc = EZ_TILEGC;
      XSetTile(EZ_Display,gc,bitmap->pixmap);
      XSetTSOrigin(EZ_Display, gc, xx-x,yy-y);
    }
  else if(EZ_IWorkAreaTextBG(widget) != ~0)
    {
      XSetForeground(EZ_Display, EZ_WRITABLEGC, EZ_IWorkAreaTextBG(widget));	
      gc = EZ_WRITABLEGC;
    }
  else EZ_GetBackgroundGC(widget, &gc, &bgpv, 0, 0);
  XFillRectangle(EZ_Display, pixmap, gc, 0,0, w, h); 
  {
    int x_r, y_r, w_r, h_r;
    EZ_IntersectRectangles(Rx,Ry,Rw,Rh,    /* the drawing area */
			   x,y,w,h,
			   &x_r,&y_r, &w_r, &h_r);
    if(w_r | h_r) XCopyArea(EZ_Display, pixmap, win, EZ_WRITABLEGC,x_r-x,y_r-y,w_r,h_r, x_r,y_r);
  }

  /* II: display item at its new position */
  EZ_ItemOriginX(item) = nx;
  EZ_ItemOriginY(item) = ny;
  EZ_ItemAOriginX(item) = ax;
  EZ_ItemAOriginY(item) = ay;
  EZ_DisplayOneItem(widget, win, nx, ny, item, Rx, Ry, Rw,Rh);

  /* III: update affected items at item's old position */
  {
    int i, nitems = EZ_IWorkAreaNItems(widget); 
    EZ_Item **data = EZ_IWorkAreaData(widget);    
    for(i = 0; i < nitems; i++)
      {
	EZ_Item *sitem = data[i];
	if(sitem && sitem != item)
	  {
	    int x0 = EZ_ItemOriginX(sitem);
	    int y0 = EZ_ItemOriginY(sitem);
	    int w0 = EZ_ItemAWidth(sitem);
	    int h0 = EZ_ItemAHeight(sitem);
	    if( x0 >= x + w || y0 >= y + h || x0 + w0 <= x  || y0 + h0 < y) ;
	    else { EZ_DisplayOneItem(widget, win, x0, y0, sitem, Rx, Ry, Rw,Rh); }
	  }
      }
      
  } 
  /* IV:  finally, highlight the item */
  EZ_HighlightOneItem(widget, win, item, fg, bg, Rx,Ry,Rw,Rh);  

}
/***********************************************************************************************/
static int EZ_FindItemInIWA(widget, item)
     EZ_Widget *widget; EZ_Item *item;
{
  if(item == NULL) return(-1);
  if(widget)
    {
      int nitems = EZ_IWorkAreaNItems(widget);
      if(nitems > 0)
	{
	  EZ_Item **data = EZ_IWorkAreaData(widget);
	  int i;
	  for(i = 0; i < nitems; i++)
	    if(data[i] == item) return(i);
	}
    }
  return(-1);
}
/***********************************************************************************************/
static void EZ_MakeIWASelectionVisible(widget, sitem, nitem) /* assume widget is mapped */
     EZ_Widget *widget; EZ_Item *sitem, *nitem;
{
  int bw,ww,hh,Rx,Ry,Rw,Rh;
  int x0 = EZ_ItemOriginX(nitem);
  int y0 = EZ_ItemOriginY(nitem);
  int iw = EZ_ItemWidth(nitem);
  int ih = EZ_ItemHeight(nitem);

  bw = EZ_WidgetBorderWidth(widget) + EZ_WidgetPadB(widget);
  Rx = Ry = bw;
  ww = EZ_WidgetWidth(widget);
  Rw = ww - (bw << 1);
  hh = EZ_WidgetHeight(widget);
  Rh = hh -(bw << 1);     

    
  if(y0 < 0 || x0 < 0 || x0 +iw > Rw+Rx|| y0 +ih > Rh+Ry)
    {
      if(y0 < 0)
	{
	  int itmp = -y0 + EZ_IWorkAreaYStart(widget) ;
	  EZ_IWorkAreaYStart(widget) = (itmp<0? itmp : 0);
	}
      else if(y0 + ih > Rh+Ry)
	{
	  int th = EZ_IWorkAreaYSize(widget);
	  if(th >= hh)
	    {
	      int itmp = EZ_IWorkAreaYStart(widget) - ih + hh - y0;
	      int dif = hh - th;
	      EZ_IWorkAreaYStart(widget) = (itmp > dif ? itmp: dif);
	    }
	}
      if(x0<0)
	{
	  int itmp = -x0 + EZ_IWorkAreaXStart(widget);
	  EZ_IWorkAreaXStart(widget) = (itmp < 0? itmp: 0);
	}
      else if(x0 +iw > Rw+Rx)
	{
	  int tw = EZ_IWorkAreaXSize(widget);
	  if(tw >= ww)
	    {
	      int itmp = EZ_IWorkAreaXStart(widget) - iw + ww -x0;
	      int dif = ww - tw;
	      EZ_IWorkAreaXStart(widget) = (itmp > dif? itmp: dif);
	    }
	} 
      EZ_IWorkAreaDirty(widget) = 1;      
      EZ_DrawWidget(widget);
    }
  else
    {
      unsigned long fg = EZ_IWorkAreaHighlightFG(widget);
      unsigned long bg = EZ_IWorkAreaHighlightBG(widget);
      if(sitem) EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),sitem, Rx,Ry,Rw,Rh);
      EZ_HighlightOneItem(widget,EZ_WidgetWindow(widget), nitem, fg, bg, Rx,Ry,Rw,Rh);
    }
}
/*********************************************************************************************/

void EZ_IWorkAreaSelectItemUsingIdx(widget, idx, location)
     EZ_Widget *widget; int idx, *location;
{
  if(widget)
    {
      int nitems = EZ_IWorkAreaNItems(widget);
      if(idx < 0 || idx >= nitems)
	{
	  EZ_Item *sitem = EZ_IWorkAreaSelection(widget);
	  EZ_IWorkAreaSelection(widget) = NULL;
	  if(EZ_WidgetMapped(widget) && sitem != NULL)
	    {
	      int bw,ww,hh,Rx,Ry,Rw,Rh;
	      bw = EZ_WidgetBorderWidth(widget) + EZ_WidgetPadB(widget);
	      Rx = Ry = bw;
	      ww = EZ_WidgetWidth(widget);
	      Rw = ww - (bw << 1);
	      hh = EZ_WidgetHeight(widget);
	      Rh = hh -(bw << 1);	      
	      EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),sitem, Rx,Ry,Rw,Rh);
	    }
	}
      else
	{
	  EZ_Item **data = EZ_IWorkAreaData(widget);
	  EZ_Item *sitem = EZ_IWorkAreaSelection(widget);
	  EZ_Item *nitem = data[idx];
	  EZ_IWorkAreaSelection(widget) = nitem;
	  if(EZ_WidgetMapped(widget) && sitem != nitem)
	    {
	      if(location)
		{
		  int x = EZ_ItemOriginX(nitem);
		  int y = EZ_ItemOriginY(nitem);
		  
		  EZ_IWorkAreaXStart(widget) += location[0] - x;
		  EZ_IWorkAreaYStart(widget) += location[1] - y;
		  EZ_DrawWidget(widget);
		}
	      EZ_MakeIWASelectionVisible(widget, sitem, nitem);
	      EZ_HandleMotionCallBacks(widget,EZ_WidgetMotionCallBack(widget));	      
	    }
	}
    }
}
void EZ_IWorkAreaSelectItem(widget, item, location)
     EZ_Widget *widget; EZ_Item *item; int *location;
{
  if(widget)
    {
      if(item == NULL)
	{
	  EZ_Item *sitem = EZ_IWorkAreaSelection(widget);
	  EZ_IWorkAreaSelection(widget) = NULL;
	  if(EZ_WidgetMapped(widget) && sitem != NULL)
	    {
	      int bw,ww,hh,Rx,Ry,Rw,Rh;
	      bw = EZ_WidgetBorderWidth(widget) + EZ_WidgetPadB(widget);
	      Rx = Ry = bw;
	      ww = EZ_WidgetWidth(widget);
	      Rw = ww - (bw << 1);
	      hh = EZ_WidgetHeight(widget);
	      Rh = hh -(bw << 1);	      
	      EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),sitem, Rx,Ry,Rw,Rh);
	    }
	}
      else
	{
	  int idx = EZ_FindItemInIWA(widget, item);
	  if(idx >= 0) EZ_IWorkAreaSelectItemUsingIdx(widget, idx, location);
	}
    }
}
/***********************************************************************************************/
static EZ_Item *EZ_FindClosestItem(data, nitems, ositem, dir)
     EZ_Item **data; int nitems; EZ_Item *ositem; int dir;
{
  if(ositem == NULL)
    {
      int i;  
      for(i = 0; i < nitems; i++)
	if(EZ_ItemVisible(data[i])) return(data[i]);
    }
  {
    EZ_Item *item, *sitem, *tmpItems[1024];
    int x = EZ_ItemOriginX(ositem);
    int y = EZ_ItemOriginY(ositem);
    int w = EZ_ItemWidth(ositem);
    int h = EZ_ItemHeight(ositem);
    int ww = 0;
    int hh = 0;
    int i, j, k = 0;

    if(dir & 0x1) /* up down */
      {
	while( k < 6 )
	  {
	    int sx = x - ww;
	    int sw = w + ww;
	    int x2 = x + sw;
	    j = 0;
	    for(i = 0; i < nitems; i++)
	      {
		int tx,tw;
		item = data[i];
		if(item != ositem)
		  {
		    tx = EZ_ItemOriginX(item);
		    tw = EZ_ItemWidth(item);
		    if( tx < x2  && tx + tw > sx ) tmpItems[j++] = item;
		  }
	      }
	    if(j > 0)
	      {
		int zy = (dir == _UP ? -100000 : 100000);
		nitems = j;
		sitem = NULL;
		for(i =0; i < nitems; i++)
		  {
		    int ty,th;
		    item = tmpItems[i];
		    ty = EZ_ItemOriginY(item);
		    th = EZ_ItemHeight(item);	    
		    if(dir == _UP)
		      {
			if(ty <= y && ty > zy)
			  {
			    sitem = item;
			    zy = ty;
			  }
		      }
		    else
		      {
			if(ty > y && ty <= zy)
			  {
			    sitem = item;
			    zy = ty;
			  }
		      }
		  }
		return(sitem);
	      }
	    k++;
	    ww = (ww + 1) << 1;
	    hh = (hh + 1) << 1;
	  }
      }
    else /* left right */
      {
	while( k < 6 )
	  {
	    int sy = y - hh;
	    int sh = h + hh;
	    int y2 = y + sh;
	    j = 0;
	    for(i = 0; i < nitems; i++)
	      {
		int ty,th;
		item = data[i];
		if(item != ositem)
		  {
		    ty = EZ_ItemOriginY(item);
		    th = EZ_ItemHeight(item);	    
		    if( ty < y2 && ty + th > sy ) tmpItems[j++] = item;
		  }
	      }
	    if(j > 0)
	      {
		int zx = (dir == _LEFT ? -100000 : 100000);
		nitems = j;
		sitem = NULL;
		for(i =0; i < nitems; i++)
		  {
		    int tx,tw;
		    item = tmpItems[i];
		    tx = EZ_ItemOriginX(item);
		    tw = EZ_ItemWidth(item);
		    if(dir == _LEFT)
		      {
			if(tx <= x && tx > zx)
			  {
			    sitem = item;
			    zx = tx;
			  }
		      }
		    else
		      {
			if(tx > x && tx <= zx)
			  {
			    sitem = item;
			    zx = tx;
			  }
		      }
		  }
		return(sitem);
	      }
	    k++;
	    ww = (ww + 1 ) << 1;
	    hh  =(hh + 1 ) << 1;
	  }
      }
    return(ositem);
  }
}
/***********************************************************************************************/
#undef _UP    
#undef _DOWN
#undef _LEFT
#undef _RIGHT
/***********************************************************************************************/
#undef _EZ_WIDGET_IWORK_AREA_C_
