/*********************************************************************
 *
 *         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
 */
#define _EZ_DND_C_

#include "EZ_Widget.h"

/*********************************************************************
 * 
 *  Global Variables
 */
EZ_DnDWidgetListEntry  EZ_DnDWidgetList;
/* atom list */
#define EZ_DefineDnDAtom(a,b)  Atom a;
#include "EZ_DnDAtom.h"
#undef EZ_DefineDnDAtom

/*********************************************************************
 * 
 *  Functions implemented in this file:
 */
Atom EZ_GetAtom  MY_ANSIARGS((char *name));
int  EZ_DnDTargetTryNextTarget MY_ANSIARGS((void));
void EZ_InitializeDnD MY_ANSIARGS((void));
void EZ_Insert2DnDWidgetList MY_ANSIARGS((EZ_Widget *widget));
void EZ_RemveWidgetFromDnDList MY_ANSIARGS((EZ_Widget *widget));
void EZ_DestroyDnDList MY_ANSIARGS((void));
void EZ_GrabButtonRelease MY_ANSIARGS((void));
void EZ_UngrabButtonRelease MY_ANSIARGS((void));
void EZ_DnDSendDataConversionRequest MY_ANSIARGS((void));
void EZ_InitDrag MY_ANSIARGS((unsigned long id,
			      int type,
			      Window window,
			      EZ_Widget *widget,
			      void      *item,
			      int x, int y, int w, int h,
			      int px, int py, int xoff, int yoff));
void EZ_FinishDrag MY_ANSIARGS((Window cwin, Window win, int px, int py));
void EZ_AbortDrag MY_ANSIARGS((void));

void EZ_DnDMatchTargetsWithDecoders MY_ANSIARGS((void));
void EZ_DnDFindNextDecoder MY_ANSIARGS((void));
void EZ_DnDFindDataEncoder MY_ANSIARGS((Atom target));
void EZ_DnDSendErrorMessage MY_ANSIARGS((int sender, int errType, EZ_DnDMessage *theMsg));
void EZ_DnDSendConversion MY_ANSIARGS((Atom type, char *message, int length ));
void EZ_HighlightDropSiteWidget MY_ANSIARGS((void));
void EZ_DnDInformDeliverError MY_ANSIARGS((int type, EZ_DnDMessage *theMsg));
void EZ_EncodeDnDMessageHeader MY_ANSIARGS((int type, char *message, int length, Window *receiver));
void EZ_DecodeDnDMessageHeader  MY_ANSIARGS((char *message, EZ_DnDMessage *theMsg));

void EZ_DnDSendRequestHelpMsg MY_ANSIARGS((void));
void EZ_DnDSendCancelHelpMsg MY_ANSIARGS((void));
void EZ_DnDShowTargetHelp MY_ANSIARGS((void));
void EZ_DnDCancelTargetHelp MY_ANSIARGS((void));
void EZ_DnDSendLeaveWindowMsg MY_ANSIARGS((void));

void EZ_EnableDnDBubbleHelp MY_ANSIARGS((void));
void EZ_DisableDnDBubbleHelp MY_ANSIARGS((void));
int  EZ_CheckDnDTimer MY_ANSIARGS((void));

void EZ_Insert2DnDWindowList MY_ANSIARGS((EZ_Widget *widget));
void EZ_RemoveFromDnDWindowList MY_ANSIARGS((EZ_Widget *widget));
void EZ_ResetGVDnD1C MY_ANSIARGS((void));
void EZ_DnDFinishDrag MY_ANSIARGS((void));
void EZ_DnDFinishDrop MY_ANSIARGS((void));
void EZ_DnDGetDragConversionTargets MY_ANSIARGS((Atom **targets_ret, int **ntargets_ret, 
						 unsigned int **t));
void EZ_DnDGetMatchedConversionTargets MY_ANSIARGS((Atom **targets_ret, int **ntargets_ret, 
						    unsigned int **t));

/***************************************************************************************/
static int  find_widget_in_dnd_list MY_ANSIARGS((EZ_Widget *widget, 
						 EZ_DnDWidgetListEntry  *head));
/***************************************************************************************/

Atom EZ_GetAtom(name)
     char *name;
{
  Atom tmp = XInternAtom(EZ_Display, name, False);
  return(tmp);
}
/***************************************************************************************/
static void DnDHelpTimerCallback();

void EZ_InitializeDnD()
{
  EZ_DnDWidgetList.widget = (EZ_Widget *)NULL;
  EZ_DnDWidgetList.next = (EZ_DnDWidgetListEntry *)NULL;

  EZ_DnDInfo.cursor = (Cursor)NULL;
  EZ_DnDInfo.siteHelpStatus = 0;
  EZ_DnDInfo.siteHelpString = NULL;  
  EZ_DnDInfo.site = NULL;
  EZ_DnDInfo.id = 0;
  EZ_DnDInfo.srcApplicationId = 0;
  EZ_DnDInfo.targetApplicationId = 0;
  
  EZ_DnDInfo.timerEnabled = 1;
  EZ_DnDInfo.timer = EZ_CreateTimer(1,0,-1, DnDHelpTimerCallback, NULL, 0);
  EZ_DnDInfo.dropMessage = (char *)my_malloc(12 * sizeof(char), _MISC_DATA_);
  (void) strcpy(EZ_DnDInfo.dropMessage, "Hi, there!");
  EZ_DnDInfo.dropMessageLength = 0;
  
#define EZ_DefineDnDAtom(a,b) { a = XInternAtom(EZ_Display, b, False); }
#include "EZ_DnDAtom.h"
#undef EZ_DefineDnDAtom
}
/***************************************************************************************/
void EZ_Insert2DnDWidgetList(widget)
     EZ_Widget *widget;
{
  if(widget && EZ_LookupWidgetFromAllHT(widget))
    {
      EZ_DnDWidgetListEntry  *tmp, *head = &EZ_DnDWidgetList;

      if(find_widget_in_dnd_list(widget, head)) return;      

      tmp = (EZ_DnDWidgetListEntry *)my_malloc(sizeof(EZ_DnDWidgetListEntry), _DND_LIST_);
      if(tmp)
	{
	  tmp->next = head->next;
	  tmp->widget = widget;
	  head->next = tmp;
	}
      else EZ_OutOfMemory("EZ_Insert2DnDWidgetList");
    }
}

static int find_widget_in_dnd_list(widget, head)
     EZ_Widget *widget;
     EZ_DnDWidgetListEntry  *head;
{
  EZ_DnDWidgetListEntry  *tmp = head->next;
  
  while(tmp)
    {
      if(tmp->widget == widget) { return(1); break; }
      tmp = tmp->next;
    }
  return(0);
}
/***************************************************************************************/
void EZ_DestroyDnDList()
{
  EZ_DnDWidgetListEntry  *head = &EZ_DnDWidgetList;  
  EZ_DnDWidgetListEntry  *tmp = head->next;
  head->next = NULL;

  while(tmp)
    {
      head = tmp->next;
      (void)my_free((char *) tmp);
      tmp = head;
    }
}
/***************************************************************************************/
void EZ_RemveWidgetFromDnDList(widget)
     EZ_Widget *widget;
{
  if(widget)
    {
      EZ_DnDWidgetListEntry  *front = &EZ_DnDWidgetList, *cur = front->next;

      while(cur)
	{
	  if(cur->widget == widget)
	    {
	      front->next = cur->next;
	      (void)my_free((char *)cur);
	      break;
	    }
	  else
	    {
	      front = cur;
	      cur = front->next;
	    }
	}
    }
}
/***************************************************************************************/
void EZ_GrabButtonRelease()
{
  int    (*OldErrorHandler)();
  Window         cwindow;
  char           *p, *q;
  EZ_ApplRoster *roster = EZ_OpenEZWGLRoster(0);
  for(p = roster->data; (p - roster->data) < roster->length;)
    {
      q = p;
      while( *p != 0) p++; p++;
      
      if(sscanf(q,"%lx",&cwindow) == 1)
	{
	  if(cwindow != EZ_DummyWindow && EZ_VerifyExistence(cwindow))
	    {
	      Atom          aType;
	      int           aFormat = 0, ans;
	      char          *data = NULL;
	      unsigned long bytesAfter, length;
	      OldErrorHandler = XSetErrorHandler(EZ_XErrorHandler);
	      EZ_XErrorCode = 0; 
	      ans = XGetWindowProperty(EZ_Display,
				       cwindow,
				       EZ_WLIST_ATOM,
				       0, EZ_XServerDataReqSize,
				       False,
				       XA_WINDOW,
				       &aType,
				       &aFormat,
				       &length,
				       &bytesAfter,
				       (unsigned char **)&data);
	      if(EZ_XErrorCode == 0 && (ans == Success))
		{
		  if((aFormat == 32) && (aType == XA_WINDOW) && length > 0)
		    {
		      Window *wins = (Window *)data;
		      int i;
		      for(i = 0; i < length; i++)
			XSelectInput(EZ_Display, wins[i], ButtonReleaseMask|PointerMotionMask|
				     ExposureMask|PointerMotionHintMask|KeyPressMask);
		    }
		}
	      if(data != NULL) XFree(data);
	      XSetErrorHandler(OldErrorHandler);  
	    }
	}
    }
  XSelectInput(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
	       ButtonReleaseMask|PointerMotionMask|
	       ExposureMask|PointerMotionHintMask|KeyPressMask);
  EZ_CloseEZWGLRoster(roster);
}
/***************************************************************************************/
void EZ_UngrabButtonRelease()
{
  int    (*OldErrorHandler)();
  Window         cwindow;
  char           *p, *q;
  EZ_ApplRoster *roster = EZ_OpenEZWGLRoster(0);
  for(p = roster->data; (p - roster->data) < roster->length;)
    {
      q = p;
      while( *p != 0) p++; p++;
      
      if(sscanf(q,"%lx",&cwindow) == 1)
	{
	  if(cwindow != EZ_DummyWindow && EZ_VerifyExistence(cwindow))
	    {
	      Atom          aType;
	      int           aFormat = 0, ans;
	      char          *data = NULL;
	      unsigned long bytesAfter, length;
	      OldErrorHandler = XSetErrorHandler(EZ_XErrorHandler);
	      EZ_XErrorCode = 0; 
	      ans = XGetWindowProperty(EZ_Display,
				       cwindow,
				       EZ_WLIST_ATOM,
				       0, EZ_XServerDataReqSize,
				       False,
				       XA_WINDOW,
				       &aType,
				       &aFormat,
				       &length,
				       &bytesAfter,
				       (unsigned char **)&data);
	      if(EZ_XErrorCode == 0 && (ans == Success))
		{
		  if((aFormat == 32) && (aType == XA_WINDOW) && length > 0)
		    {
		      Window *wins = (Window *)data;
		      int i;
		      for(i = 0; i < length; i++)
			XSelectInput(EZ_Display, wins[i], 0L);
		    }
		}
	      if(data != NULL) XFree(data);
	      XSetErrorHandler(OldErrorHandler);  
	    }
	}
    }
  XSelectInput(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum), 0L);
  EZ_CloseEZWGLRoster(roster);
}
/***********************************************************************************************
 *  This func is called just before a drag started: Button is pressed over a dragSrc 
 *  and has been moved. Initialize the DragInfo struct.
 *
 *   Called by DragSrc only.
 */
void EZ_InitDrag(id, type, window, widget, item, x,y, w, h, px, py, xoff, yoff)
     unsigned long id;                /* unique id for this and the only drag */
     Window window;                   /* window it is started */
     int type;                        /* type: EZ_DND_OBJ_IS_ITEM or WIDGET */
     EZ_Widget *widget;               /* if item, it is the widget the item belongs, otherwise same as item */
     void      *item;                 /* the item (or the same as widget) */
     int x,y,w,h;                     /* geometry of item */
     int px,py,xoff,yoff;             /* pointer coor and offsets of pointer and item origin */
{
  if(EZ_DnDInfo.id != 0)
    {
      /* if not yet finished with the current drag, send a message to
       * the current drop target to check whether it is still interested
       * in the current drop. The target should response to this request.
       */
      unsigned long CommWin;
      unsigned long Win;

      if(EZ_DnDInfo.ImSrc)
	{
	  CommWin = EZ_DnDInfo.targetCommWin;
	  Win = EZ_DnDInfo.targetWin;
	  if(EZ_DnDInfo.targetApplicationId == EZ_VerifyExistence(CommWin) &&
	     CommWin == EZ_WindowIsDnDTarget((Window)Win))
	    {
	      EZ_SendDnDMessage(EZ_DND_SRC_REQUEST_UNLOCK, NULL, 0, 0);
	      return;
	    }
	  else EZ_DnDFinishDrag(); /* party no longer exist */
	}
      else if(EZ_DnDInfo.ImTarget)  /* src != target if we are here */
	{
	  CommWin = EZ_DnDInfo.srcCommWin;
	  Win = EZ_DnDInfo.srcWin;
	  if(EZ_DnDInfo.srcApplicationId == EZ_VerifyExistence(CommWin) &&
	     CommWin == EZ_WindowIsDnDSrc((Window)Win))
	    {
	      EZ_SendDnDMessage(EZ_DND_TARGET_REQUEST_UNLOCK, NULL, 0, 0);
	      return;	      
	    }
	  else EZ_DnDFinishDrop(); /* party no longer exist */
	}
    }
  EZ_DnDInfo.id = id;
  EZ_DnDInfo.actionType = 0;
  EZ_DnDInfo.ImSrc = 1;
  EZ_DnDInfo.ImTarget = 0;
  EZ_DnDInfo.srcType = type;
  EZ_DnDInfo.srcCommWin = (unsigned long)EZ_DummyWindow;
  EZ_DnDInfo.srcWin =  (unsigned long)window;
  EZ_DnDInfo.srcItem =  (unsigned long)item;
  EZ_DnDInfo.srcApplicationId = EZ_ApplicationSetupTime;
  EZ_DnDInfo.srcX =  x;
  EZ_DnDInfo.srcY =  y;
  EZ_DnDInfo.srcW =  w;
  EZ_DnDInfo.srcH =  h;
  EZ_DnDInfo.srcX1 = xoff;
  EZ_DnDInfo.srcY1 = yoff;

  EZ_DnDInfo.srcStatus = EZ_DRAG_STARTED;

  EZ_DnDInfo.srcNTargets = 0;
  EZ_DnDInfo.srcEncoder = (EZ_DnDDataEncoder  *)NULL;
  EZ_DnDInfo.thisAtom = 0L;  
  
  EZ_DnDInfo.px = px;
  EZ_DnDInfo.py = py;
  EZ_DnDInfo.ptWin = (unsigned long)window;
  EZ_DnDInfo.cursor = (Cursor)NULL;
  
  EZ_DnDInfo.ImTarget = 0;
  EZ_DnDInfo.targetType = 0;
  EZ_DnDInfo.targetCommWin = 0L;
  EZ_DnDInfo.targetWin =  0L;
  EZ_DnDInfo.targetItem =  0L;
  EZ_DnDInfo.targetApplicationId = 0;
  EZ_DnDInfo.targetX =  0;
  EZ_DnDInfo.targetY =  0;
  EZ_DnDInfo.targetW =  0;
  EZ_DnDInfo.targetH =  0;
  EZ_DnDInfo.targetX1 =  0;
  EZ_DnDInfo.targetY1 =  0;

  EZ_DnDInfo.targetStatus = 0;

  EZ_DnDInfo.targetDecoder = (EZ_DnDDataDecoder  *)NULL;

  EZ_DnDInfo.site = NULL;
  EZ_DnDInfo.siteHelpString = NULL;
  EZ_DnDInfo.siteHelpStatus = 0;

  EZ_GrabButtonRelease(); 
}
/***********************************************************************
 *  This func is called when a drag is droped in a DropTarget, a window.
 *  Just fill out the info needed for communication.
 *
 *  Called by DnDSrc only. 
 */
void EZ_FinishDrag(commWin, win, px, py)
     Window commWin, win; /* info about target */
     int px, py;
{
  EZ_DnDInfo.srcStatus = EZ_DRAG_RELEASED;

  EZ_DnDInfo.px = px;
  EZ_DnDInfo.py = py;
  EZ_DnDInfo.ptWin = (unsigned long)win;
  
  EZ_DnDInfo.targetCommWin = (unsigned long)commWin;
  EZ_DnDInfo.targetWin =  (unsigned long)win;
  EZ_DnDInfo.targetApplicationId = (commWin == 0L? 0L: EZ_VerifyExistence(commWin));

  EZ_UngrabButtonRelease(); 
}

/***********************************************************************
 *  
 *  If for some reason a drop cannot be done, this is called by the
 *  drag src.
 */
void EZ_AbortDrag()
{
  if( EZ_DnDInfo.id )
    {
      int snap; 

      EZ_DnDInfo.srcStatus = EZ_DRAG_ABORTING;
      snap = EZ_DnDInfo.dragIconType;

      if(snap == EZ_DND_DRAG_ICON_CURSOR)
	{
	  int desX, desY, srcX, srcY;
	  int i, count, dx,dy,tx,ty,nx,ny, cx,cy;
	  Window win = (Window)EZ_DnDInfo.srcWin;
	  
	  XFlush(EZ_Display);
	  if(XGrabPointer(EZ_Display,win, True, 0L, GrabModeAsync, GrabModeAsync, None, 
			  EZ_DnDInfo.cursor, CurrentTime) == GrabSuccess)
	    EZ_PointerGrabed = 1;

	  srcX = EZ_DnDInfo.px;     /* current cursor position */
	  srcY = EZ_DnDInfo.py;
	  desX = EZ_DnDInfo.srcX - EZ_DnDInfo.srcX1;
	  desY = EZ_DnDInfo.srcY - EZ_DnDInfo.srcY1;
	  
	  dx = desX - srcX; 
	  dy = desY - srcY;
	  count = ( ((ABSV(dx) + ABSV(dy)) >> 8) + 1) << 2;
	  
	  tx = dx/count; 
	  ty = dy/count; 
	  cx = nx = srcX;
	  cy = ny = srcY;
	  for(i = 0; i < count; i++)
	    {
	      if(i < count-1) { nx += tx; ny += ty;}
	      else {tx = desX - cx; ty = desY - cy;}
	      XWarpPointer(EZ_Display, RootWindow(EZ_Display,EZ_ScreenNum),
			   None,  cx, cy, EZ_XDisplayWidth, EZ_XDisplayHeight,
			   tx,ty);
	      cx = nx; cy = ny;
	    }
	  if(EZ_PointerGrabed)
	    {
	      XUngrabPointer(EZ_Display,CurrentTime);
	      EZ_PointerGrabed = 0;
	    }
	}
      else
	{
	  int i, iarea, count, tx, ty, txx,tyy, dxx, dyy,sx,sy;
	  int dx,dy, nx, ny, snap,iww,ihh, ox, oy;

	  EZ_DnDInfo.srcStatus = EZ_DRAG_ABORTING;
	  iww = EZ_DnDInfo.srcW;
	  ihh = EZ_DnDInfo.srcH;
	  iarea = iww * ihh;
	  
	  if(iarea)
	    {
	      if(iarea < 1000) count = 64;
	      else if(iarea < 2000) count = 32;
	      else if(iarea < 3000) count = 20;
	      else if(iarea < 10000) count = 16;
	      else if(iarea < 20000) count = 8;
	      else count = 4;

	      
	      snap = EZ_DnDInfo.dragIconType;
	      if(snap == 0) count = count >> 2;

	      ox = EZ_DnDInfo.srcX - EZ_DnDInfo.srcX1;
	      oy = EZ_DnDInfo.srcY - EZ_DnDInfo.srcY1;

	      nx = EZ_DnDInfo.px + EZ_DnDInfo.srcX1;
	      ny = EZ_DnDInfo.py + EZ_DnDInfo.srcY1;
	      
	      dx = EZ_DnDInfo.srcX - nx;
	      dy = EZ_DnDInfo.srcY - ny;
	      
	      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; nx += sx;}
		  tyy += dyy;  if(tyy >= count) { tyy -= count; ny += sy;}
		  nx += tx;
		  ny += ty;
		  XDrawLine(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
			    EZ_XORGC, ox, oy, nx, ny);
		  XDrawLine(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
			    EZ_XORGC, ox, oy, nx+iww, ny);

		  XDrawLine(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
			    EZ_XORGC, ox, oy, nx+iww, ny+ihh);
		  XDrawLine(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
			    EZ_XORGC, ox, oy, nx, ny+ihh);
		  EZ_MoveSnapShotOrOutline(snap, nx, ny, iww, ihh, 1);

		  XDrawLine(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
			    EZ_XORGC, ox, oy, nx, ny);
		  XDrawLine(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
			    EZ_XORGC, ox, oy, nx+iww, ny);
		  XDrawLine(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
			    EZ_XORGC, ox, oy, nx+iww, ny+ihh);
		  XDrawLine(EZ_Display, RootWindow(EZ_Display, EZ_ScreenNum),
			    EZ_XORGC, ox, oy, nx, ny+ihh);
		  EZ_MoveSnapShotOrOutline(snap, nx, ny, iww, ihh, 0);

		  XFlush(EZ_Display);
		}
	      EZ_MoveSnapShotOrOutline(snap, 0, 0, 0, 0, 1);
	      XFlush(EZ_Display);
	    }
	}
      EZ_DnDInfo.id = 0L;
    }
}

/************************************************************************************************
 *  given an array of targets src can convert, the dropSite (dropTarget) match all the targets
 *  with its registered decodes, and record the matches in EZ_DnDInf.targetNTargets and
 *  EZ_DnDInfo.targetTargets. 
 */
void EZ_DnDMatchTargetsWithDecoders()
{
  int ntargets = 0;
  EZ_Item   *item = NULL;
  EZ_Widget *widget = NULL;
  void      *obj = NULL;

  if(EZ_DnDInfo.targetItem)
    {
      EZ_DnDDataDecoder   *decoders = NULL;

      if(EZ_DnDInfo.targetType == EZ_DND_OBJ_IS_ITEM)
	{
	  item = (EZ_Item *)EZ_DnDInfo.targetItem;
	  if(item && EZ_LookupItemFromHT(item))
	    decoders = EZ_ItemDnDDataDecoders(item);
	  else item = NULL;
	}
      else if(EZ_DnDInfo.targetType == EZ_DND_OBJ_IS_WIDGET)
	{
	  widget = (EZ_Widget *)EZ_DnDInfo.targetItem;
	  if(widget && EZ_LookupWidgetFromAllHT(widget))
	    decoders = EZ_WidgetDnDDataDecoders(widget);
	  else widget = NULL;
	}
      
      if(item)  { EZ_ItemBlocked(item) += 1; obj = item;}
      if(widget){ EZ_WidgetBlocked(widget) += 1; obj = widget;}
      
      if(decoders)
	{
	  EZ_DnDDataDecoder  *ptr;
	  Atom  *TargetAtoms = EZ_DnDInfo.targetTargets;
	  Atom  *SrcAtoms = EZ_DnDInfo.srcTargets;
	  int    count = EZ_DnDInfo.srcNTargets;
	  unsigned int action_type = EZ_DnDInfo.actionType;

	  int i;

	  for(i = 0; i < count; i++)
	    {
	      Atom target = SrcAtoms[i];
	      ptr = decoders;

	      while(ptr)
		{
		  if(ptr->type == target  && ptr->tag == action_type)
		    {
		      if(ptr->decoder) 
			{TargetAtoms[ntargets] = target; ntargets++;}
		      break;
		    }
		  else ptr = ptr->next;
		}
	    }
	  EZ_DnDInfo.targetNTargets = ntargets;

	  /* if dnd target has filters (encoders for Atom EZ_DND_DROP_START_ATOM), invoke it now */
	  {
	    EZ_DnDDataDecoder *sdecoder = EZ_FindDecoderGivenTarget(decoders, EZ_DND_DROP_START_ATOM);

	    if(sdecoder)
	      {
		if(sdecoder->decoder)
		  (sdecoder->decoder)(obj, sdecoder->data, EZ_DnDInfo.dropMessage,
				      EZ_DnDInfo.dropMessageLength);
		if((sdecoder->callback).callback)   
		  ((sdecoder->callback).callback)(sdecoder->object,(sdecoder->callback).data);
		if(EZ_DnDInfo.targetNTargets > EZ_DND_MAX_TARGETS)
		  EZ_DnDInfo.targetNTargets = EZ_DND_MAX_TARGETS;
	      }
	  }
	}
    }
}

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

void EZ_DnDGetMatchedConversionTargets(targets_ret, ntargets_ret, at_ret)
     Atom **targets_ret; int **ntargets_ret; unsigned int **at_ret;
{
  if(targets_ret)  *targets_ret = EZ_DnDInfo.targetTargets;
  if(ntargets_ret) *ntargets_ret = &(EZ_DnDInfo.targetNTargets);
  if(at_ret)       *at_ret = &(EZ_DnDInfo.actionType);
}

void EZ_DnDGetDragConversionTargets(targets_ret, ntargets_ret, at_ret)
     Atom **targets_ret; int **ntargets_ret; unsigned int **at_ret;
{
  if(targets_ret)  *targets_ret = EZ_DnDInfo.srcTargets;
  if(ntargets_ret) *ntargets_ret = &(EZ_DnDInfo.srcNTargets);
  if(at_ret)       *at_ret = &(EZ_DnDInfo.actionType);
}

/*****************************************************************************/
void EZ_DnDFindNextDecoder()
{
  EZ_DnDDataDecoder *decoders = NULL;
  int count = EZ_DnDInfo.targetNTargets;

  if(EZ_DnDInfo.targetType == EZ_DND_OBJ_IS_ITEM)
    decoders = EZ_ItemDnDDataDecoders((EZ_Item *)(EZ_DnDInfo.targetItem));
  else if(EZ_DnDInfo.targetType == EZ_DND_OBJ_IS_WIDGET)
    decoders = EZ_WidgetDnDDataDecoders((EZ_Widget *)(EZ_DnDInfo.targetItem));
  
  EZ_DnDInfo.targetDecoder = NULL;
  if(decoders && count > 0)
    {
      Atom *atoms = EZ_DnDInfo.targetTargets;
      unsigned int action_type = EZ_DnDInfo.actionType;
      int i = 0;

      while(i < count && atoms[i] == 0) i++;
      if(i < count) 
	{
	  Atom target = atoms[i];
	  EZ_DnDDataDecoder *ptr = decoders;
	  while(ptr)
	    {
	      if(ptr->type == target && ptr->tag == action_type) 
		{
		  if(ptr->decoder)   EZ_DnDInfo.targetDecoder = ptr;
		  break;
		}
	      ptr = ptr->next;
	    }
	}
    }
}

/************************************************************************************************/
int EZ_DnDTargetTryNextTarget()  /* return 1 if no more targets available */
{
  int i, count = EZ_DnDInfo.targetNTargets;
  Atom *atoms = EZ_DnDInfo.targetTargets;
  EZ_DnDDataDecoder *decoder = EZ_DnDInfo.targetDecoder;
  Atom ctarget = decoder->type;
  int  done = 1;

  /* we try the possible targets sequentially */
  i = 0;
  while(i < count)
    {
      if(atoms[i] == ctarget) 
	{ atoms[i] = 0; break; }
      i++; 
    }
  if(i < count)
    {
      EZ_DnDFindNextDecoder();          /* try a new target */
      if(EZ_DnDInfo.targetDecoder)
	{
	  EZ_DnDInfo.targetStatus = EZ_DROP_WAITING_FOR_DATA;
	  EZ_DnDSendDataConversionRequest();
	  done = 0;
	}
    }
  return(done);
}
/************************************************************************************************/
void EZ_DnDFindDataEncoder(target)
     Atom           target;
{
  EZ_DnDDataEncoder  *ptr, *encoders = NULL;

  if(EZ_DnDInfo.srcType == EZ_DND_OBJ_IS_ITEM)
    {
      EZ_Item *item = (EZ_Item *)EZ_DnDInfo.srcItem;
      if(item && EZ_LookupItemFromHT(item))
	encoders = EZ_ItemDnDDataEncoders(item);
    }
  else if(EZ_DnDInfo.srcType == EZ_DND_OBJ_IS_WIDGET)
    {
      EZ_Widget *widget = (EZ_Widget *)EZ_DnDInfo.srcItem;
      if(widget && EZ_LookupWidgetFromAllHT(widget))
	encoders = EZ_WidgetDnDDataEncoders(widget);
    }

  EZ_DnDInfo.srcEncoder = NULL;
  if(encoders)
    {
      unsigned int action_type = EZ_DnDInfo.actionType;
      ptr = encoders;
      while( ptr )
	{
	  if(ptr->type  == target && ptr->tag == action_type && ptr->encoder)
	    {
	      EZ_DnDInfo.srcEncoder = ptr;
	      return;
	    }
	  ptr = ptr->next;
	}
    }
}
/************************************************************************************************/
void EZ_DnDSendDataConversionRequest()
{
  char message[32];
  char *str = message;
  sprintf(str, "%lx ", (EZ_DnDInfo.targetDecoder)->type);

  EZ_SendDnDMessage(EZ_DND_REQUEST_CONVERSION, message, 32, 0);  
}
/************************************************************************************************/
void  EZ_DnDSendErrorMessage(sender, errType, theMsg)
     int sender;    /* EZ_DND_SENDER_IS_SRC or EZ_DND_SENDER_IS_TARGET */
     int errType;   /* the error type */
     EZ_DnDMessage *theMsg;   /* previous message received */
{
  /* if this is called, it means we had an error
   * in the current DnD transaction. It means that
   * we might not have set up the DnDInfo structure
   * yet. This is why we need help from theMsg
   * The party called this function aborts itself right
   * after the call. No future correspondence can be
   * done regarding this DnD message id. So actually there is
   * no needs to restore the DnDInfo structure ...
   */
  if(sender == EZ_DND_SENDER_IS_SRC)  /* sender is a drag src */
    {
      unsigned long oldTCommWin = EZ_DnDInfo.targetCommWin;
      unsigned long oldTWin = EZ_DnDInfo.targetWin; 
      unsigned long oldTItem = EZ_DnDInfo.targetItem; 

      EZ_DnDInfo.targetCommWin = theMsg->fmCommWin;
      EZ_DnDInfo.targetWin = theMsg->fmWin;
      EZ_DnDInfo.targetItem = theMsg->fmItem;
      EZ_SendDnDMessage(errType, NULL, 0, 0);
      
      EZ_DnDInfo.targetCommWin = oldTCommWin;
      EZ_DnDInfo.targetWin = oldTWin;
      EZ_DnDInfo.targetItem = oldTItem;
    }
  else  if(sender == EZ_DND_SENDER_IS_TARGET)  /* sender is a drag target */
    {
      unsigned long oldSCommWin = EZ_DnDInfo.srcCommWin;
      unsigned long oldSWin = EZ_DnDInfo.srcWin; 
      unsigned long oldSItem = EZ_DnDInfo.srcItem; 

      EZ_DnDInfo.srcCommWin = theMsg->fmCommWin;
      EZ_DnDInfo.srcWin = theMsg->fmWin;
      EZ_DnDInfo.srcItem = theMsg->fmItem;
      EZ_SendDnDMessage(errType, NULL, 0, 0);
      
      EZ_DnDInfo.srcCommWin = oldSCommWin;
      EZ_DnDInfo.srcWin = oldSWin;
      EZ_DnDInfo.srcItem = oldSItem; 
    }
}

/************************************************************************************************/
void EZ_DnDSendConversion(type, message, length)
     Atom type;
     char *message;
     int  length;
{
  EZ_DnDInfo.srcStatus = EZ_DRAG_SENDING_DATA;
  EZ_DnDInfo.thisAtom = type;
  EZ_SendDnDMessage(EZ_DND_DATA, message, length, 0);
  EZ_DnDInfo.srcStatus = EZ_DRAG_WAITING_FOR_ACK;
}

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

void EZ_DnDSendLeaveWindowMsg()
{
  unsigned long cw,w;
  unsigned long commWin =  EZ_WindowIsDnDTarget((Window)EZ_DnDInfo.ptWin);
  if(commWin != 0)
    {
      cw = EZ_DnDInfo.targetCommWin;
      w = EZ_DnDInfo.targetWin;
      
      EZ_DnDInfo.targetCommWin = commWin;
      EZ_DnDInfo.targetWin = (unsigned long)EZ_DnDInfo.ptWin;
      EZ_SendDnDMessage(EZ_DND_DRAG_LEFT, NULL, 0,0);
      EZ_DnDInfo.targetCommWin = cw;
      EZ_DnDInfo.targetWin = w;
    }
}
/************************************************************************************************/
void EZ_HighlightDropSiteWidget()
{
  if(EZ_LastDropSite && EZ_LookupWidgetFromAllHT(EZ_LastDropSite))
    {
      if(EZ_WidgetMapped(EZ_LastDropSite))
	{
	  XPoint points[5];
	  int w, h;
	  Window  win;
	  GC gc = EZ_DNDXORGC; 

	  win =  EZ_WidgetWindow(EZ_LastDropSite);
	  w = (int) EZ_WidgetWidth(EZ_LastDropSite) -1;
	  h = (int) EZ_WidgetHeight(EZ_LastDropSite) -1;

	  points[0].x = 1;   points[0].y = 1;
	  points[1].x = w;   points[1].y = 1;
	  points[2].x = w;   points[2].y = h;
	  points[3].x = 1;   points[3].y = h;
	  points[4].x = 1;   points[4].y = 1;
	  XDrawLines(EZ_Display, win, gc, points, 5, CoordModeOrigin);
	}
    }
}
/************************************************************************************************/
void EZ_DnDInformDeliverError(message_type, theMsg)
     int message_type;
     EZ_DnDMessage *theMsg;
{
  (void)fprintf(stderr, "DnDMessageDeliverError: from [%lx %lx] to [%lx %lx]\n",
		theMsg->fmCommWin, theMsg->fmWin, theMsg->toCommWin, theMsg->toWin);
  (void)fprintf(stderr, "\t Message id=%lx, type=%d\n",theMsg->id, message_type);
}
/************************************************************************************************/
void EZ_EncodeDnDMessageHeader(type, message, length, receiver)
     int type;
     char *message;
     int length;
     Window *receiver;
{
  int toWhom;
  unsigned long msgLength = (unsigned long)length + EZ_DND_MSG_HEADER_SIZE;
  *receiver = (Window)NULL;
#define TO_DND_SRC      0
#define TO_DND_TARGET   1
#define TO_DND_UNKNOWN  -1
#define msgHeader  EZ_DnDInfo.theHeader

  switch(type)
    {
    case     EZ_DND_DRAG_START:
    case     EZ_DND_DRAG_MOTION:
    case     EZ_DND_DRAG_DROPPED:
    case     EZ_DND_CONVERSION_FAILED:
    case     EZ_DND_CONVERSION_REFUSED:
    case     EZ_DND_DATA:
    case     EZ_DND_ABORT_TO_TARGET:      
    case     EZ_DND_SRC_REQUEST_UNLOCK:
    case     EZ_DND_DRAG_CANCELLED:
    case     EZ_DND_SRC_REQUEST_HELP:
    case     EZ_DND_SRC_CANCEL_HELP:
    case     EZ_DND_DRAG_LEFT:
      toWhom = TO_DND_TARGET;
      break;
    case     EZ_DND_REQUEST_CONVERSION:
    case     EZ_DND_ABORT_TO_SRC:
    case     EZ_DND_TARGET_REQUEST_UNLOCK:
    case     EZ_DND_DROP_DONE: 
      toWhom = TO_DND_SRC;
      break;
    default:
      toWhom = TO_DND_UNKNOWN;
      break;
    }
  if(toWhom == TO_DND_SRC) /* I'm a DnDTarget, send to DnDSrc */
    { 
      /*   1  2    3     4     5    6    7    8     9    10    11    12   13    14   15  16 17 18 19  20 21 22 23*/
      /* type id len tType tCwin tWin tItem  tId  fCwin fWin  fItem  fId  ptWin ptX ptY   X  Y  W  H  x1 y1 ^^ atom*/
      /*                   1  2   3   4   5   6   7   8   9  10   11 12   13 14 15 16 17 18 19 20 21 22 */ 
      sprintf(msgHeader, "%x %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %d %d %d %d %d %d %d %d %d %lx",
	      /* 1 */type,
	      /* 2 */EZ_DnDInfo.id,
	      /* 3 */(unsigned long)msgLength,
	      /* 4 */(unsigned long)EZ_DND_RECEIVER_IS_SRC,
	      /* 5 */EZ_DnDInfo.srcCommWin,
	      /* 6 */EZ_DnDInfo.srcWin,
	      /* 7 */EZ_DnDInfo.srcItem,
	      /* 8 */EZ_DnDInfo.srcApplicationId,
	      /* 9 */EZ_DnDInfo.targetCommWin,	      
	      /* 10 */EZ_DnDInfo.targetWin,
	      /* 11 */EZ_DnDInfo.targetItem,
	      /* 12 */EZ_DnDInfo.targetApplicationId,
	      /* 13 */EZ_DnDInfo.ptWin,
	      /* 14 */EZ_DnDInfo.px,
	      /* 15 */EZ_DnDInfo.py,
	      /* 16 */EZ_DnDInfo.targetX,
	      /* 17 */EZ_DnDInfo.targetY,
	      /* 18 */EZ_DnDInfo.targetW,
	      /* 19 */EZ_DnDInfo.targetH,
	      /* 20 */EZ_DnDInfo.targetX1,
	      /* 21 */EZ_DnDInfo.targetY1,
	      /* 22 */ 0,
	      /* 23 */ 0L);
      *receiver = (Window)EZ_DnDInfo.srcCommWin;
    }
  else if(toWhom == TO_DND_TARGET) /* I'm a DnDSrc, send to DnDTarget */
    { 
      /*   1  2    3     4     5    6    7    8     9    10    11    12   13    14   15  16 17 18 19  20 21 22  23*/
      /* type id len tType tCwin tWin tItem  tId  fCwin fWin  fItem  fId  ptWin ptX ptY   X  Y  W  H  x1 y1 ^^ atom*/
      /*                   1  2   3   4   5   6   7   8   9  10  11  12  13  14 15 16 17 18 19 20 21 22 23*/
      sprintf(msgHeader, "%x %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %d %d %d %d %d %d %d %d %d %lx",
	      /* 1 */type,
	      /* 2 */EZ_DnDInfo.id,
	      /* 3 */(unsigned long)msgLength,
	      /* 4 */(unsigned long)EZ_DND_RECEIVER_IS_TARGET,
	      /* 5 */EZ_DnDInfo.targetCommWin,
	      /* 6 */EZ_DnDInfo.targetWin,
	      /* 7 */EZ_DnDInfo.targetItem,
	      /* 8 */EZ_DnDInfo.targetApplicationId,
	      /* 9 */EZ_DnDInfo.srcCommWin,	      
	      /* 10 */EZ_DnDInfo.srcWin,
	      /* 11 */EZ_DnDInfo.srcItem,
	      /* 12 */EZ_DnDInfo.srcApplicationId,
	      /* 13 */EZ_DnDInfo.ptWin,
	      /* 14 */EZ_DnDInfo.px,
	      /* 15 */EZ_DnDInfo.py,
	      /* 16 */EZ_DnDInfo.srcX,
	      /* 17 */EZ_DnDInfo.srcY,
	      /* 18 */EZ_DnDInfo.srcW,
	      /* 19 */EZ_DnDInfo.srcH,
	      /* 20 */EZ_DnDInfo.srcX1,
	      /* 21 */EZ_DnDInfo.srcY1,
	      /* 22 */EZ_DnDInfo.dragIconType,
	      /* 23 */EZ_DnDInfo.thisAtom);
      *receiver = (Window)EZ_DnDInfo.targetCommWin;
    }
#undef msgHeader
#undef TO_DND_SRC     
#undef TO_DND_TARGET  
#undef TO_DND_UNKNOWN 
}

void EZ_DecodeDnDMessageHeader(message, theMsg)
     char *message;
     EZ_DnDMessage *theMsg;
{
  theMsg->id = 0;

  /*   1  2    3     4     5    6    7    8     9    10    11    12   13    14   15  16 17 18 19  20 21 22 23 */
  /* type id len tType tCwin tWin tItem  tId  fCwin fWin  fItem  fId  ptWin ptX ptY   X  Y  W  H  x1 y1 ^^ dt */
  /*                 1  2   3   4   5   6   7   8   9  10  11  12  13  14 15 16 17 18 19 20 21 22 23 */
  sscanf(message,  "%x %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %d %d %d %d %d %d %d %d %d %lx",
	 /* 1 */&(theMsg->type),
	 /* 2 */&(theMsg->id),
	 /* 3 */&(theMsg->length),
	 /* 4 */&(theMsg->toType),
	 /* 5 */&(theMsg->toCommWin),
	 /* 6 */&(theMsg->toWin),
	 /* 7 */&(theMsg->toItem),
	 /* 8 */&(theMsg->toId),
	 /* 9 */&(theMsg->fmCommWin),
	 /* 10 */&(theMsg->fmWin),
	 /* 11 */&(theMsg->fmItem),
	 /* 12 */&(theMsg->fmId),
	 /* 13 */&(theMsg->ptWin),
	 /* 14 */&(theMsg->px),
	 /* 15 */&(theMsg->py),
	 /* 16 */&(theMsg->x),
	 /* 17 */&(theMsg->y),
	 /* 18 */&(theMsg->w),
	 /* 19 */&(theMsg->h),
	 /* 20 */&(theMsg->x1),
	 /* 21 */&(theMsg->y1),
	 /* 22 */&(theMsg->iconType),
	 /* 23 */&(theMsg->thisAtom));
  theMsg->message = message;
}
/*******************************************************************/
/*
dump_dndinfo()
{
  printf("EZ_DnDInfo.id %d\n", EZ_DnDInfo.id);

  printf("EZ_DnDInfo.ImSrc %d\n", EZ_DnDInfo.ImSrc);
  printf("EZ_DnDInfo.srcCommWin %lx\n", EZ_DnDInfo.srcCommWin);
  printf("EZ_DnDInfo.srcWin %lx\n", EZ_DnDInfo.srcWin);
  printf("EZ_DnDInfo.srcItem %lx\n\n", EZ_DnDInfo.srcItem);
  printf("EZ_DnDInfo.XYWH %d %d %d %d %d %d\n\n", EZ_DnDInfo.srcX,EZ_DnDInfo.srcY,
	 EZ_DnDInfo.srcW,EZ_DnDInfo.srcH, EZ_DnDInfo.srcX1,EZ_DnDInfo.srcY1);


  printf("pointer is at [%d %d]\n", EZ_DnDInfo.px,  EZ_DnDInfo.py);
  printf("EZ_DnDInfo.ImTarget %d\n", EZ_DnDInfo.ImTarget);
  printf("EZ_DnDInfo.targetCommWin %lx\n", EZ_DnDInfo.targetCommWin);
  printf("EZ_DnDInfo.targetWin %lx\n", EZ_DnDInfo.targetWin);
  printf("EZ_DnDInfo.targetItem %lx\n", EZ_DnDInfo.targetItem);
}
*/

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

void EZ_DnDSendCancelHelpMsg()
{
  unsigned long cw,w;
  unsigned long commWin = EZ_WindowIsDnDTarget((Window)EZ_DnDInfo.ptWin);
  cw = EZ_DnDInfo.targetCommWin;
  w = EZ_DnDInfo.targetWin;
  
  EZ_DnDInfo.targetCommWin = commWin;
  EZ_DnDInfo.targetWin = (unsigned long)EZ_DnDInfo.ptWin;
  EZ_SendDnDMessage(EZ_DND_SRC_CANCEL_HELP, NULL, 0,0);
  EZ_DnDInfo.targetCommWin = cw;
  EZ_DnDInfo.targetWin = w;
}

void EZ_DnDSendRequestHelpMsg()
{
  unsigned long cw,w;
  unsigned long commWin =  EZ_WindowIsDnDTarget((Window)EZ_DnDInfo.ptWin);
  cw = EZ_DnDInfo.targetCommWin;
  w = EZ_DnDInfo.targetWin;
  
  EZ_DnDInfo.targetCommWin = commWin;
  EZ_DnDInfo.targetWin = (unsigned long)EZ_DnDInfo.ptWin;
  EZ_SendDnDMessage(EZ_DND_SRC_REQUEST_HELP, NULL, 0,0);
  EZ_DnDInfo.targetCommWin = cw;
  EZ_DnDInfo.targetWin = w;
}
/****************************************************************************************************/
void EZ_EnableDnDBubbleHelp()
{
  if(EZ_DnDInfo.timerEnabled == 0 || EZ_DnDInfo.timer == NULL)
    {
      EZ_DnDInfo.timer = EZ_CreateTimer(1,0,-1, DnDHelpTimerCallback, NULL, 0);
      EZ_DnDInfo.timerEnabled = 1;
    }
}
void EZ_DisableDnDBubbleHelp()
{
  if(EZ_DnDInfo.timerEnabled)
    {
      EZ_CancelTimer(EZ_DnDInfo.timer);
      EZ_DnDInfo.timerEnabled = 0;
    }
}

static void DnDHelpTimerCallback(timer)
     EZ_Timer *timer;
{
  if(EZ_DnDInfo.siteHelpStatus == 0 && EZ_DnDInfo.site != NULL && EZ_DnDInfo.siteHelpString != NULL )
    EZ_DnDShowTargetHelp();
}   

/***********************************************************************************************
 *
 *  unpost the "Bubble" help on current drop site
 */
void EZ_DnDCancelTargetHelp()
{
  EZ_HideWidget(EZ_BubbleHelp);
  EZ_DnDInfo.site = NULL;
  EZ_DnDInfo.siteType = EZ_DND_OBJ_IS_UNKNOWN;
  EZ_DnDInfo.siteHelpString = NULL;
  EZ_DnDInfo.siteHelpStatus = 0;
}

/*
 *  post a "Bubble" help on current drop site
 */
void EZ_DnDShowTargetHelp()
{
  if(EZ_DnDInfo.siteHelpStatus == 0 && EZ_DnDInfo.siteType != EZ_DND_OBJ_IS_UNKNOWN &&
     EZ_DnDInfo.site != NULL && EZ_DnDInfo.siteHelpString != NULL)
    {
      int x,y,xx,yy,w,h,h1, ww,hh;
      int offset = 16;
      if(EZ_DnDInfo.dragIconType == EZ_DND_DRAG_ICON_CURSOR) offset = 0;	    
      
      if(EZ_DnDInfo.siteType == EZ_DND_OBJ_IS_ITEM)
	{
	  EZ_SetWidgetLabelString(EZ_BubbleHelp, EZ_DnDInfo.siteHelpString);
	  EZ_ClearWidgetSizeComputedFlag(EZ_BubbleHelp);
	  EZ_ComputeWidgetWindowSize(EZ_BubbleHelp, &ww, &hh, NULL);

	  xx = EZ_DnDInfo.px;
	  yy = EZ_DnDInfo.py;
	  h = EZ_DnDInfo.srcH;
	  h1 = (int)EZ_ItemAHeight((EZ_Item *)EZ_DnDInfo.site);
	  h = MAXV(h,h1);

	  x = xx - (ww >> 1); if(x < 0) x = 0;
	  y = yy - (hh + h + offset); if(y < 0) y = yy + ( h +offset);

	  EZ_SetWidgetPosition(EZ_BubbleHelp,x,y);
	  EZ_DisplayWidget(EZ_BubbleHelp);
	  EZ_DnDInfo.siteHelpStatus = 1;
	}
      else if(EZ_DnDInfo.siteType == EZ_DND_OBJ_IS_WIDGET)
	{
	  int top = 1;
	  int wox,woy,dx,dy,tw,th;
	  Window child;
	  EZ_Widget *widget = (EZ_Widget *)EZ_DnDInfo.site;

	  offset = 4;
	  
	  /* figure out the origin of widget */
	  XTranslateCoordinates(EZ_Display, EZ_WidgetWindow(widget),
				RootWindow(EZ_Display, EZ_ScreenNum),
				0, 0, &wox,&woy, &child);
	  tw = (int)EZ_WidgetWidth(widget);
	  th = (int)EZ_WidgetHeight(widget);

	  if(th > tw) top = 0;  /* pop help to the left */

	  /* ptr location */
	  xx = EZ_DnDInfo.px;
	  yy = EZ_DnDInfo.py;
	  
	  /* offset */
	  dx = xx - wox;
	  dy = yy - woy;

	  h = EZ_DnDInfo.srcH;
	  w = EZ_DnDInfo.srcW;

	  EZ_SetWidgetLabelString(EZ_BubbleHelp, EZ_DnDInfo.siteHelpString);
	  EZ_ClearWidgetSizeComputedFlag(EZ_BubbleHelp);
	  EZ_ComputeWidgetWindowSize(EZ_BubbleHelp, &ww, &hh, NULL);
	  
	  if(top)
	    {
	      int tmp = MAXV(dy, h);
	      y = yy - (tmp + offset + hh); 
	      if(y < 0)
		{
		  tmp = woy + th - yy;
		  tmp = MAXV(tmp, h);
		  y = yy + (tmp +offset); 
		}
	      x = xx - (ww >> 1); if(x < 0) x = 0;
	    }
	  else
	    {
	      int tmp = MAXV(dx, w);
	      x = xx - ( tmp + offset + ww);
	      if(x < 0)
		{
		  tmp = wox + tw - xx;
		  tmp = MAXV(tmp,w);
		  x = xx + (tmp + offset);
		}
	      y = yy - (hh >> 1); if(y < 0) y = 0;
	    }
	  EZ_SetWidgetPosition(EZ_BubbleHelp,x,y);
	  EZ_DisplayWidget(EZ_BubbleHelp);
	  EZ_DnDInfo.siteHelpStatus = 1;
	}
    }
}
/****************************************************************************************************/
extern void   EZ_GetCurrentTime();
int EZ_CheckDnDTimer()
{
  struct timeval ctv,*tv;
  long ts, tus;
  
  tv = &ctv;
  EZ_GetCurrentTime(tv);
  ts = tv->tv_sec; tus = tv->tv_usec;

  return(EZ_CheckRealTimers(ts, tus));
}
/****************************************************************************************************/
static int    windowsCnt = 0;
static Window windows[512];
/****************************************************************************************************/
void EZ_ResetGVDnDC() { windowsCnt = 0; }
/****************************************************************************************************/
void EZ_Insert2DnDWindowList(widget)
     EZ_Widget *widget;
{
  Window window;

  if(widget == NULL) return;
  window = EZ_WidgetWindow(widget);
  if(window != (Window)None)
    {
      int i;
      for(i = 0; i < windowsCnt; i++)
	if(windows[i] == window) return;
      /* window is not currently registered */
      if(windowsCnt < 511)
	{
	  windows[windowsCnt++] = window;
	  XChangeProperty(EZ_Display,
			  EZ_DummyWindow,
			  EZ_WLIST_ATOM,
			  XA_WINDOW, 32,
			  PropModeReplace,
			  (unsigned char *)windows,
			  windowsCnt);
	}
    }
}
     
void EZ_RemoveFromDnDWindowList(widget)
     EZ_Widget *widget;
{
  Window window;
  if(widget == NULL) return;
  window = EZ_WidgetWindow(widget);
  if(window != (Window)None)
    {
      int i, j;

      for(i = 0; i < windowsCnt; i++)
	{
	  if(windows[i] == window)
	    {
	      for(j = i, i++;  i < windowsCnt; i++, j++)
		windows[j] = windows[i];
	      windowsCnt--; if(windowsCnt < 0) windowsCnt = 0;
	      XChangeProperty(EZ_Display,
			      EZ_DummyWindow,
			      EZ_WLIST_ATOM,
			      XA_WINDOW, 32,
			      PropModeReplace,
			      (unsigned char *)windows,
			      windowsCnt);
	      XSync(EZ_Display, False);
	      break;
	    }
	}
    }
}
/****************************************************************************************************/
/* added  3/10/98 */
void EZ_DnDFinishDrag()
{
  EZ_Item   *item = NULL;
  EZ_Widget *widget = NULL;
  if(EZ_DnDInfo.srcItem)
    {
      EZ_DnDDataEncoder  *sencoder, *encoders = NULL;

      if(EZ_DnDInfo.srcType == EZ_DND_OBJ_IS_ITEM)
	{
	  item = (EZ_Item *)EZ_DnDInfo.srcItem;
	  if(item && EZ_LookupItemFromHT(item))	  
	    encoders = EZ_ItemDnDDataEncoders(item);
	  else item = NULL;
	}
      else if(EZ_DnDInfo.srcType == EZ_DND_OBJ_IS_WIDGET)
	{
	  widget = (EZ_Widget *)EZ_DnDInfo.srcItem;
	  if(widget && EZ_LookupWidgetFromAllHT(widget))
	    encoders = EZ_WidgetDnDDataEncoders(widget);
	  else widget = NULL;
	}

      sencoder = EZ_FindEncoderGivenTarget(encoders, EZ_DND_DRAG_FINISH_ATOM);
      if(sencoder)
	{
	  if(sencoder)
	      {
		int length = 0, needFree = 0;
		char *message = NULL;
		
		if(sencoder->encoder)
		  (sencoder->encoder)(sencoder->object, sencoder->data, &message, &length, &needFree);
		if((sencoder->callback).callback)   
		  ((sencoder->callback).callback)(sencoder->object,(sencoder->callback).data);
		/* this is insane !! */
		if(length>0 && needFree && message) (void)my_free((void *)message);
	      }
	}
    }
  if(item)   EZ_ItemBlocked(item) -= 1;
  if(widget) EZ_WidgetBlocked(widget) -= 1;
}
void EZ_DnDFinishDrop()
{
  EZ_Item   *item = NULL;
  EZ_Widget *widget = NULL;
  
  if(EZ_DnDInfo.targetItem)
    {
      EZ_DnDDataDecoder  *sdecoder, *decoders = NULL;

      if(EZ_DnDInfo.targetType == EZ_DND_OBJ_IS_ITEM)
	{
	  item = (EZ_Item *)EZ_DnDInfo.targetItem;
	  if(item && EZ_LookupItemFromHT(item))
	    decoders = EZ_ItemDnDDataDecoders(item);
	  else item = NULL;
	}
      else if(EZ_DnDInfo.targetType == EZ_DND_OBJ_IS_WIDGET)
	{
	  widget = (EZ_Widget *)EZ_DnDInfo.targetItem;
	  if(widget && EZ_LookupWidgetFromAllHT(widget))
	    decoders = EZ_WidgetDnDDataDecoders(widget);
	  else widget = NULL;
	}
      sdecoder = EZ_FindDecoderGivenTarget(decoders, EZ_DND_DROP_FINISH_ATOM);
      if(sdecoder)
	{
	  if(sdecoder)
	      {
		if(sdecoder->decoder)
		  (sdecoder->decoder)(sdecoder->object, sdecoder->data, NULL, 0);
		if((sdecoder->callback).callback)   
		  ((sdecoder->callback).callback)(sdecoder->object,(sdecoder->callback).data);
	      }
	}
    }
  if(item)   EZ_ItemBlocked(item) -= 1;
  if(widget) EZ_WidgetBlocked(widget) -= 1;
}
/****************************************************************************************************/
#undef _EZ_DND_C_
