/*********************************************************************
 *
 *         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 Internal Tree                         ***
 ***                                                           ***
 *****************************************************************/
#define _EZ_WIDGET_ITREE_C_
#include "EZ_Widget.h"

/*********************************************************************
 * 
 *  Functions implemented in this file:
 */
EZ_Widget  *EZ_CreateITree  MY_ANSIARGS((EZ_Widget *parent));
void       EZ_DrawWidgetITree MY_ANSIARGS((EZ_Widget *widget));
void       EZ_ComputeWidgetITreeSize MY_ANSIARGS((EZ_Widget *widget, int *w, int *h));
void       EZ_FreeWidgetITreeData MY_ANSIARGS((EZ_Widget *widget));
void       EZ_ITreeEventHandle MY_ANSIARGS((EZ_Widget *widget, XEvent *event));

EZ_TreeNode *EZ_FindPointerOnTreeNode MY_ANSIARGS((int x, int y, EZ_TreeNode *root));
EZ_TreeNode *EZ_FindTreeAdjBranch MY_ANSIARGS((EZ_TreeNode *root, EZ_TreeNode *node, int dir));
void        EZ_SetITreeWTree MY_ANSIARGS((EZ_Widget *widget, EZ_TreeNode *tree));
void        EZ_FlashItemOutline MY_ANSIARGS((EZ_Widget *widget, EZ_Item *item, 
					     int Rx, int Ry, int Rw, int Rh));
void        EZ_DnDOutlineItem MY_ANSIARGS((EZ_Widget *widget, EZ_Item *item, 
					     int Rx, int Ry, int Rw, int Rh));

void        EZ_ITreeSelectNode MY_ANSIARGS((EZ_Widget *widget, EZ_TreeNode *theNode, int *location));
void        EZ_ITreeSelectNodeUsingItem MY_ANSIARGS((EZ_Widget *widget, EZ_Item *theItem, int *location));
/*********************************************************************/
void tree_scroll_update_selection MY_ANSIARGS((EZ_Widget *widget, EZ_TreeNode *node));
/*********************************************************************
 * 
 *  Local Variables.
 */
static EZ_WidgetHandle EZ_ITreeHandle =
{ 
  EZ_ComputeWidgetITreeSize,
  EZ_DrawWidgetITree,
  EZ_FreeWidgetITreeData,
  EZ_ITreeEventHandle,
};

static void update_itree_scrollbars MY_ANSIARGS((EZ_Widget *widget));
static void ITreeWTimerCallBack MY_ANSIARGS((EZ_Timer *timer, void *data));
/*********************************************************************/

EZ_Widget  *EZ_CreateITree(parent)
     EZ_Widget  *parent;     /* parent widget    */
{
  EZ_Widget  *tmp;
  
  tmp = EZ_CreateNewWidget(parent);
  /*--------------------------------------------------
   * Register the handling functions for label.
   *  has to be done after  EZ_CreateNewWiget.
   *-------------------------------------------------*/
  EZ_WidgetHandlingFunctions[EZ_WIDGET_ITREE] = &EZ_ITreeHandle;

  EZ_SetWidgetTypeAndNames(tmp,EZ_WIDGET_ITREE);
  EZ_WidgetBorderStyle(tmp)  = EZ_BORDER_DOWN;
  EZ_WidgetBorderWidth(tmp)  = 2;
  EZ_WidgetPadX(tmp) = 1;
  EZ_WidgetPadY(tmp) = 1;
  EZ_WidgetPadB(tmp) = 2;

  EZ_ITreeWRoot(tmp) = (EZ_TreeNode *)NULL;
  EZ_ITreeWSelection(tmp) = (EZ_TreeNode *)NULL;

  EZ_ITreeWHighlightBG(tmp) = ~0; 
  EZ_ITreeWHighlightFG(tmp) = ~0;
  EZ_ITreeWTextBG(tmp) = ~0;

  EZ_ITreeWYSize(tmp)  = 1;
  EZ_ITreeWYPageSize(tmp) = 1;
  EZ_ITreeWYStart(tmp)   = 0;
  EZ_ITreeWXSize(tmp) = 1;
  EZ_ITreeWXPageSize(tmp) = 1;
  EZ_ITreeWXStart(tmp) = 0;
  EZ_ITreeWDisplayed(tmp) = 0;

  EZ_ITreeWHScroll(tmp) = (EZ_Widget *)NULL; 
  EZ_ITreeWVScroll(tmp) = (EZ_Widget *)NULL;
  EZ_ITreeWTree(tmp) = (EZ_Widget *)NULL;
  EZ_ITreeWDirty(tmp) = 1;
  /* blinking selection timer */
  (void)EZ_CreateTimer(0,350000,-1,(EZ_CallBack)ITreeWTimerCallBack, tmp, 0);
  
  EZ_SetWidgetFocusableFlag(tmp);  

  EZ_Insert2DnDWidgetList(tmp);
  EZ_WidgetDnDHandler(tmp) = (EZ_DnDMsgHandler)EZ_ITreeDnDHandle;   

  return(tmp);
}

void EZ_FreeWidgetITreeData(widget)
     EZ_Widget *widget;
{
  if(EZ_ITreeWRoot(widget) != (EZ_TreeNode *)NULL)
    EZ_TreeDestroySubtree(EZ_ITreeWRoot(widget));
}
 
/********************************************************************
 *
 *  Figure out the dimension of a label widget.
 */

void EZ_ComputeWidgetITreeSize(widget, w, h)
     EZ_Widget *widget;
     int             *w, *h;
{
  int  cw, ch;

  if(!(EZ_GetWidgetSizeComputedFlag(widget)))
    { 
      EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
      if(rt)
	{
	  EZ_Item *item = EZ_TreeNodeItem(rt);
	  if(EZ_ItemDirty(item)) EZ_ComputeSubtreeSize(rt, &cw, &ch);
	}
      EZ_SetWidgetSizeComputedFlag(widget);
    }
  cw = EZ_WidgetPadX(widget) + EZ_WidgetBorderWidth(widget) + EZ_WidgetPadB(widget);
  ch = EZ_WidgetPadY(widget) + EZ_WidgetBorderWidth(widget) + EZ_WidgetPadB(widget);
  /*
  if(EZ_WidgetMapped(widget) == 0)
    {
      EZ_ITreeWXStart(widget) = 0;
      EZ_ITreeWYStart(widget) = 0;
    }
    */
  EZ_ITreeWYPageSize(widget) = 30;
  EZ_ITreeWXPageSize(widget) = 30;

  cw = cw + cw + 30;
  ch = ch + ch + 30;

  *w = cw;
  *h = ch;
}

/*******************************************************************
 *
 *  Draw A Label Widget.
 */
void  EZ_DrawWidgetITree(wptr)
     EZ_Widget *wptr;
{
  int       w, h,x,y, bw, padb, padb2;
  Pixmap    pixmap;
  Window    win;
  GC        gc;
  unsigned long   bgpv;
  EZ_TreeNode *rt;

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

  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_ITreeWXStart(wptr);
	int yy = EZ_ITreeWYStart(wptr);
	gc = EZ_TILEGC;
	XSetTile(EZ_Display,gc,bitmap->pixmap);
	XSetTSOrigin(EZ_Display, gc, xx, yy);
      }
    else if(EZ_ITreeWTextBG(wptr) != ~0)
      {
	bgpv = EZ_ITreeWTextBG(wptr);
	XSetForeground(EZ_Display, EZ_WRITABLEGC, bgpv);	
	gc = EZ_WRITABLEGC;
      }
    else EZ_GetBackgroundGC(wptr, &gc, &bgpv, 0, 0);
  }
  XFillRectangle(EZ_DisplayForWidgets, pixmap, gc, padb,padb, w -padb2, h-padb2); 
  
  rt = EZ_ITreeWRoot(wptr);
  if(rt)
    {
      EZ_Item *item = EZ_TreeNodeItem(rt);
      int xoff = EZ_WidgetBorderWidth(wptr) + EZ_WidgetPadX(wptr) + padb;
      int yoff = EZ_WidgetBorderWidth(wptr) + EZ_WidgetPadY(wptr) + padb;
      
      if(EZ_ItemDirty(item) || EZ_ITreeWDirty(wptr)) 
	{
	  EZ_ComputeSubtreeSize(rt, &x, &y);

	  /* update the geometry of the tree for scrolling */
	  EZ_ITreeWXSize(wptr) = EZ_TreeNodeTWidth(rt) + (xoff << 1);
	  EZ_ITreeWYSize(wptr) = EZ_TreeNodeTHeight(rt)+ (yoff << 1);
	  EZ_ITreeWXPageSize(wptr) = EZ_WidgetWidth(wptr);
	  EZ_ITreeWYPageSize(wptr) = EZ_WidgetHeight(wptr);

	  if(EZ_ITreeWXStart(wptr) + EZ_ITreeWXSize(wptr) < 0)
	    {
	      int itmp = w - EZ_ITreeWXSize(wptr);
	      EZ_ITreeWXStart(wptr) = itmp > 0? 0: itmp;

	    }
	  if(EZ_ITreeWYStart(wptr) + EZ_ITreeWYSize(wptr) < 0)
	    {
	      int itmp = h - EZ_ITreeWYSize(wptr); 
	      EZ_ITreeWYStart(wptr) = itmp > 0? 0: itmp;
	    }
	  EZ_ITreeWDirty(wptr) = 0;
	}

      x =  EZ_ITreeWXStart(wptr) + xoff;
      y =  EZ_ITreeWYStart(wptr) + yoff;

      EZ_DisplayTreeInDrawable(wptr, pixmap, x,y, rt, bw,bw, w-(bw<<1), h-(bw<<1));
      
      {
	EZ_TreeNode *sel = EZ_ITreeWSelection(wptr);
	if(sel)
	  {
	    EZ_Item *tmp = EZ_TreeNodeItem(sel);
	    unsigned long bg = EZ_ITreeWHighlightBG(wptr);
	    unsigned long fg = EZ_ITreeWHighlightFG(wptr);
	    EZ_HighlightOneItem(wptr, pixmap, tmp, fg, bg, bw,bw, w-(bw<<1), h-(bw<<1));
	  }
      }
    }
  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_Display, pixmap); 
  
  update_itree_scrollbars(wptr);
  if(EZ_ITreeWDisplayed(wptr) == 0)
    {
      EZ_TreeNode *sel = EZ_ITreeWSelection(wptr);
      EZ_ITreeWDisplayed(wptr) = 1;
      if(sel != NULL)
	{
	  tree_scroll_update_selection(wptr, sel);
	  EZ_HandleMotionCallBacks(wptr,EZ_ITreeWMotionCallBack(wptr));
	}
    }
}

/* */
static void update_itree_scrollbars(wptr)
     EZ_Widget *wptr;
{
  if(EZ_ITreeWHScroll(wptr) != (EZ_Widget *)NULL)
    {
      EZ_UpdateScrollbarI(EZ_ITreeWHScroll(wptr),
			  EZ_ITreeWXSize(wptr),
			  EZ_ITreeWXPageSize(wptr),
			  EZ_ITreeWXStart(wptr));
    }
  
  if(EZ_ITreeWVScroll(wptr) != (EZ_Widget *)NULL)
    {
      EZ_UpdateScrollbarI(EZ_ITreeWVScroll(wptr),
			  EZ_ITreeWYSize(wptr),
			  EZ_ITreeWYPageSize(wptr),
			  -EZ_ITreeWYStart(wptr));
    }
}

/****************************************************************************/
void  EZ_ITreeEventHandle(widget, event)
     EZ_Widget *widget;
     XEvent    *event;
{
  XEvent    xevent;
  int       x, y, bw, padb, Rx,Ry,Rw,Rh, xoffset, yoffset, done;
  int       Ox, Oy, oldx, oldy, dx,dy, rootx, rooty,tx,ty,Sx,Sy, moved, grabed;
  int       ix0, iy0, ww,hh,iww, ihh, iarea, snapshot = 0, wox,woy;
  Window    root, child;
  unsigned  int    mask;
  EZ_TreeNode   *node = NULL;
  EZ_Item       *nsitem = NULL;

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

  switch(event->type)
    {
    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_ITreeWRoot(widget) == (EZ_TreeNode *)NULL) break;
      if(event->xbutton.button == EZ_Btn1)  
	{
	  Time  time_stamp = event->xbutton.time;   
	  int   elapsedtime = EZ_LastButton1ClickTime - time_stamp; 

	  EZ_LastButton1ClickTime = time_stamp;
	  padb = EZ_WidgetPadB(widget);
	  bw = EZ_WidgetBorderWidth(widget) + padb;
	  Rx = Ry = bw;
	  ww = (int)(EZ_WidgetWidth(widget));
	  Rw =  ww - (bw<<1);
	  hh = (int)(EZ_WidgetHeight(widget)); 
	  Rh =  hh - (bw<<1);
  
	  x = event->xbutton.x;
	  y = event->xbutton.y;
	  node = EZ_FindPointerOnTreeNode(x,y, EZ_ITreeWRoot(widget));

	  if(node && node != EZ_ITreeWSelection(widget))
	    {
	      unsigned long bg = EZ_ITreeWHighlightBG(widget);
	      unsigned long fg = EZ_ITreeWHighlightFG(widget);

	      EZ_TreeNode   *tnode = EZ_ITreeWSelection(widget);
	      EZ_Item *item = (tnode == NULL? NULL : EZ_TreeNodeItem(tnode));

	      EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),item, Rx, Ry, Rw, Rh);
	      EZ_ITreeWSelection(widget) = node; 

	      item = EZ_TreeNodeItem(node);
	      EZ_HighlightOneItem(widget,EZ_WidgetWindow(widget),item,fg,bg, Rx, Ry, Rw, Rh);
	      
	      /* motion callback */
	      EZ_HandleMotionCallBacks(widget,EZ_ITreeWMotionCallBack(widget));
	    }
	  /*
	   * a double click cause the execuation of the call back function
	   */
	  if(node && ABSV(elapsedtime) < DOUBLE_CLICK_TIME_INTERVAL)
	    { 
	      EZ_TreeNode *snode = EZ_ITreeWSelection(widget);
	      if(snode)
		{
		  EZ_Item *sitem = EZ_TreeNodeItem(snode);
		  if(sitem)
		    {
		      /* EZ_ItemPrivateData(sitem) = snode;*/
		      EZ_HandleItemCallBacks(sitem);
		    }
		}
	      EZ_ExecuteWidgetCallBacks(widget);
	      return;
	    }
	  /* pick the item under the pointer */
	  if(node)
	    {
	      nsitem = EZ_TreeNodeItem(node); /* must not be NULL */
	      
	      xoffset = bw + EZ_WidgetPadX(widget) ;
	      yoffset = bw + EZ_WidgetPadY(widget);

	      /* */ Ox = Oy = oldx = oldy = 0; iww = ihh = 1; 
	      {
		EZ_TreeNode *SaveNode = NULL;
		
		done = 0;
		moved = 0;
		while(!done)
		  {
		    do {
		      XNextEvent(EZ_Display, &xevent); 	      
		      if(EZ_FilterEvent(&xevent))
			EZ_InvokePrivateEventHandler(&xevent);
		      if(xevent.type == MotionNotify)
			{
			  moved = 1; 
			  x = xevent.xmotion.x;
			  y = xevent.xmotion.y;
			}
		      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)
		      {
			if(moved)
			  {
			    EZ_TreeNode *tmp = EZ_FindPointerOnTreeNode(x, y, EZ_ITreeWRoot(widget));
			    if(tmp != SaveNode && tmp != EZ_ITreeWSelection(widget))
			      {
				EZ_Item *item = (SaveNode == NULL? NULL : EZ_TreeNodeItem(SaveNode));
				EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),item, Rx, Ry, Rw, Rh);
				if(tmp)
				  {
				    EZ_Item *item = EZ_TreeNodeItem(tmp);
				    EZ_FlashItemOutline(widget,item, Rx,Ry,Rw,Rh);
				  }
				SaveNode = tmp;
			      }
			  }
		      }
		    else /* we are done */
		      {
			if(SaveNode) /* cannot be the current selection */
			  {
			    EZ_Item *item;
			    EZ_TreeNode *sel = EZ_ITreeWSelection(widget);
			    unsigned long bg = EZ_ITreeWHighlightBG(widget);
			    unsigned long fg = EZ_ITreeWHighlightFG(widget);
			    EZ_ITreeWSelection(widget) = SaveNode; 
			    
			    if(sel)
			      {
				item = EZ_TreeNodeItem(sel);			      
				EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),item, Rx, Ry, Rw, Rh);
			      }
			    item = EZ_TreeNodeItem(SaveNode);			      
			    EZ_HighlightOneItem(widget,EZ_WidgetWindow(widget),item,fg,bg, Rx, Ry, Rw, Rh);
			    
			    /* motion callback */
			    EZ_HandleMotionCallBacks(widget,EZ_ITreeWMotionCallBack(widget));
			  }
		      }
		  } /* while */
	      }
	    } /* if node */
	  else /* we are not on a node */
	    {
	      xoffset = bw + EZ_WidgetPadX(widget) ;
	      yoffset = bw + EZ_WidgetPadY(widget);

	      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; break;}
		  if(  x < xoffset || x > ww - xoffset ||  y < yoffset || y > hh - yoffset)
		    {
		      if(y < yoffset)
			{
			  /* scroll down */
			  if(EZ_ITreeWYStart(widget) < 0)
			    {
			      int tmp = EZ_ITreeWYStart(widget) + 10;
			      EZ_ITreeWYStart(widget) = (tmp < 0 ? tmp: 0);
			      EZ_DrawWidget(widget);
			    }
			}
		      else if( y > EZ_WidgetHeight(widget) - yoffset)
			{
			  /* scroll up */ 
			  EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
			  if(rt)
			    {
			      int  th = EZ_ITreeWYSize(widget);
			      if(th > EZ_WidgetHeight(widget))
				{
				  int dif = EZ_WidgetHeight(widget) - th;
				  int tmp = EZ_ITreeWYStart(widget) - 10;
				  int nOff = (tmp > dif ? tmp: dif);
				  if( EZ_ITreeWYStart(widget) !=  nOff)
				    {
				      EZ_ITreeWYStart(widget) =  nOff;
				      EZ_DrawWidget(widget);		
				    }		  
				}
			    }
			}
		      else if(x < xoffset)
			{
			  /* scroll right */
			  if(EZ_ITreeWXStart(widget) < 0)
			    {
			      int tmp = EZ_ITreeWXStart(widget) + 10;
			      EZ_ITreeWXStart(widget) = (tmp < 0 ? tmp: 0);
			      EZ_DrawWidget(widget);
			    }
			}
		      else if( x > EZ_WidgetWidth(widget) - xoffset)
			{
			  /* scroll left */ 
			  EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
			  if(rt)
			    {
			      int  tw = EZ_ITreeWXSize(widget);
			      if(tw > EZ_WidgetWidth(widget))
				{
				  int dif = EZ_WidgetWidth(widget) - tw;
				  int tmp = EZ_ITreeWXStart(widget) - 10;
				  int nOff =  (tmp > dif ? tmp: dif);
				  if(EZ_ITreeWXStart(widget) != nOff)
				    {
				      EZ_ITreeWXStart(widget) = nOff;
				      EZ_DrawWidget(widget); 
				    }		  
				}
			    }
			}
		    }
		} /* while */
	    }
	} /* button */
      else if(event->xbutton.button == EZ_Btn3)   /* DnD */
	{
	  Window  ptWin = event->xbutton.window;
	  Window oldPtWin = ptWin;
	  Window widgetwin = ptWin;
	  unsigned int actionType = (event->xbutton.state) & (ShiftMask | ControlMask | Mod1Mask);
	  int helpRequested = 0;
	  int dragCancelled = 0;
	  padb = EZ_WidgetPadB(widget);
	  bw = EZ_WidgetBorderWidth(widget) + padb;
	  Rx = Ry = bw;
	  ww = (int)(EZ_WidgetWidth(widget));
	  Rw =  ww - (bw<<1);
	  hh = (int)(EZ_WidgetHeight(widget)); 
	  Rh =  hh - (bw<<1);
  
	  x = event->xbutton.x;
	  y = event->xbutton.y;

	  node = EZ_FindPointerOnTreeNode(x,y, EZ_ITreeWRoot(widget));

	  if(node && node != EZ_ITreeWSelection(widget))
	    {
	      unsigned long bg = EZ_ITreeWHighlightBG(widget);
	      unsigned long fg = EZ_ITreeWHighlightFG(widget);

	      EZ_TreeNode   *tnode = EZ_ITreeWSelection(widget);
	      EZ_Item *item = (tnode == NULL? NULL : EZ_TreeNodeItem(tnode));

	      EZ_UpdateOneItem(widget,widgetwin,item, Rx, Ry, Rw, Rh);
	      EZ_ITreeWSelection(widget) = node; 
	      
	      item = EZ_TreeNodeItem(node);
	      EZ_HighlightOneItem(widget,widgetwin,item,fg,bg, Rx, Ry, Rw, Rh);
	      
	      /* motion callback */
	      EZ_HandleMotionCallBacks(widget,EZ_ITreeWMotionCallBack(widget));
	    }
	  /* pick the item under the pointer */
	  if(node)
	    {
	      nsitem = EZ_TreeNodeItem(node); /* must not be NULL */
	      
	      xoffset = bw + EZ_WidgetPadX(widget) ;
	      yoffset = bw + EZ_WidgetPadY(widget);

	      /* */ Ox = Oy = oldx = oldy = 0; iww = ihh = 1; 

	      if(EZ_ItemIsDnDSrc(nsitem))
		{
		  unsigned long serial = 0L;
		  int newEvent = 0;
		  done = 0;
		  moved = 0;
		  grabed = 0;
		  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;
			    }
			}
		      if(newEvent == 0)
			{
			  EZ_CheckDnDTimer();
			  EZ_SitForALittleBit(1000);
			}
		      
		      if(!done && newEvent) /* not done yet */
			{
			  newEvent = 0;
			  if(moved)
			    {
			      if(grabed == 0)
				{
				  XTranslateCoordinates(EZ_Display, widgetwin,
							RootWindow(EZ_Display, EZ_ScreenNum),
							0, 0,
							&wox,&woy, &child);
				  ix0 = EZ_ItemOriginX(nsitem);
				  iy0 = EZ_ItemOriginY(nsitem);
				  iww = EZ_ItemAWidth(nsitem);
				  ihh = EZ_ItemAHeight(nsitem);
				  iarea  = iww * ihh;
				  oldx = wox + x;
				  oldy = woy + y;
				  
				  tx = ix0 - x;
				  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)
					{ 
					  snapshot = EZ_DND_DRAG_ICON_CURSOR;
					  EZ_DnDInfo.cursor = cursor;
					}
				      else if(iarea < 40000)
					snapshot = EZ_MakeSnapShot(widget,1,ix0,iy0,iww,ihh);
				      else snapshot = EZ_DND_DRAG_ICON_OUTLINE;
				      {
					EZ_DnDInfo.actionType = actionType;
					EZ_DnDInfo.px = oldx;
					EZ_DnDInfo.py = oldy;
					EZ_DnDInfo.dragIconType = snapshot;
					{
					  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(snapshot != EZ_DND_DRAG_ICON_CURSOR)
					EZ_MoveSnapShotOrOutline(snapshot, 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(snapshot != EZ_DND_DRAG_ICON_CURSOR)
					EZ_MoveSnapShotOrOutline(snapshot, Ox, Oy, iww, ihh, 0);
				    }
				} /* grabed */
			    }/* moved */
			} /* not done */			    
		      else if(done) /* we are done */
			{
			  if(EZ_PointerGrabed)
			    {
			      XUngrabPointer(EZ_Display,CurrentTime);
			      EZ_PointerGrabed = 0;
			    }
			  if(grabed) /* dragged */
			    {
			      char *message;
			      int length;
			      Window commWin;
			      
			      if(snapshot != EZ_DND_DRAG_ICON_CURSOR)
				EZ_MoveSnapShotOrOutline(snapshot, 0,0,0,0,1);
			      if(ptWin != 0)  commWin = (Window) EZ_WindowIsDnDTarget(ptWin);
			      else commWin = 0L;
			      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)
				{
				  /* drop to the root window, handle this internally */
				  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 */
		}  /* is a drag src */
	    }
	} /* button */
      break;
      break;
    case MotionNotify:
      break;
    case EnterNotify:
      break;
    case LeaveNotify:
      break;
    case KeyPress:
      if(EZ_ITreeWRoot(widget) == (EZ_TreeNode *)NULL) break;
      if(EZ_ITreeWSelection(widget) == (EZ_TreeNode *)NULL)
	EZ_ITreeWSelection(widget) = EZ_ITreeWRoot(widget);
      {
#define TEMP_BUFFER_SIZE    32
	int               count, move_unit = 1;
	KeySym            keysym;
	XComposeStatus    compose; 
	char              tmpbuffer[TEMP_BUFFER_SIZE];
	int               buffersize = TEMP_BUFFER_SIZE;
#undef TEMP_BUFFER_SIZE
	if(event->xkey.state & ShiftMask) move_unit = 2;
	if(event->xkey.state & ControlMask)     move_unit = 4;
	
	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
	    {
	      EZ_TreeNode *node = EZ_ITreeWSelection(widget);
	      for(count = 0; count < move_unit; count++)
		{
		  EZ_TreeNode *tnode = EZ_FindTreeAdjBranch(EZ_ITreeWRoot(widget), node, -1);
		  if(tnode == (EZ_TreeNode *)NULL) break;
		  else node = tnode;
		}
	      if(node && node != EZ_ITreeWSelection(widget))
		{
		  tree_scroll_update_selection(widget, node);
		  EZ_HandleMotionCallBacks(widget,EZ_ITreeWMotionCallBack(widget));
		}
	    }
	    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
	    {
	      EZ_TreeNode *node = EZ_ITreeWSelection(widget);
	      for(count = 0; count < move_unit; count++)
		{
		  EZ_TreeNode *tnode = EZ_FindTreeAdjBranch(EZ_ITreeWRoot(widget), node, 1);
		  if(tnode == (EZ_TreeNode *)NULL) break;
		  else node = tnode;
		}
	      if(node && node != EZ_ITreeWSelection(widget))
		{
		  tree_scroll_update_selection(widget, node);
		  EZ_HandleMotionCallBacks(widget,EZ_ITreeWMotionCallBack(widget));
		}
	    }
	    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
	    {
	      EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
	      int tw = EZ_TreeNodeTWidth(rt);
	      int xoffset = EZ_WidgetBorderWidth(widget) + EZ_WidgetPadX(widget);
	      int w = EZ_WidgetWidth(widget) - (xoffset <<1);
	      if(w < tw)
		{
		  int aaa = (move_unit << 3);
		  int itmp = EZ_ITreeWXStart(widget) + aaa;
		  if(itmp >= 0) itmp = 0;
		  if(EZ_ITreeWXStart(widget) != itmp)
		    {
		      EZ_ITreeWXStart(widget) = itmp;
		      if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
		    }
		}
	    }
	  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
	    {
	      EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
	      int tw = EZ_TreeNodeTWidth(rt);
	      int xoffset = EZ_WidgetBorderWidth(widget) + EZ_WidgetPadX(widget);
	      int w = EZ_WidgetWidth(widget) - (xoffset <<1);

	      if( w < tw)
		{
		  int aaa = -(move_unit << 3);
		  int itmp = EZ_ITreeWXStart(widget) + aaa;
		  if(itmp < w - tw) itmp = w -tw;
		  
		  if(EZ_ITreeWXStart(widget) != itmp)
		    {
		      EZ_ITreeWXStart(widget) = itmp;
		      if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
		    }
		}
	    }
	    break;
	  case XK_Return: case XK_Linefeed: case XK_space:  
#ifdef XK_KP_Enter
	  case XK_KP_Enter:
#endif
	    { 
	      EZ_TreeNode *snode = EZ_ITreeWSelection(widget);
	      if(snode)
		{
		  EZ_Item *item = EZ_TreeNodeItem(snode);
		  if(item) 
		    {
		      /* EZ_ItemPrivateData(item) = snode;*/
		      EZ_HandleItemCallBacks(item);
		    }
		}
	      EZ_ExecuteWidgetCallBacks(widget);
	    }
	    break;
	  default:
	    break;
	  }
	{ 
	  XEvent tmpevent; 
	  while(XCheckTypedEvent(EZ_Display, KeyPress, &tmpevent))
	    if(EZ_FilterEvent(&tmpevent))
	      EZ_InvokePrivateEventHandler(&tmpevent);
	}
      }      
      break;
    default:
      break;
    }
}
/*****************************************************************************
 * 
 * given (x0,y0), figure out which node the point is in. and return the node.
 */
EZ_TreeNode *EZ_FindPointerOnTreeNode(x0,y0, root)
     int x0,y0;
     EZ_TreeNode *root;
{
  EZ_TreeNode *children;
  EZ_Item *item;
  int  x, y, w, h, tw, th;
  
  if(root == NULL) { return(NULL); }
  item = EZ_TreeNodeItem(root);
  if(item)
    {
      x = EZ_ItemOriginX(item);
      y = EZ_ItemOriginY(item);
      w = EZ_ItemAWidth(item);
      h = EZ_ItemAHeight(item);
      if( x0 >= x && x0 <= x + w && y0 >= y && y0 <= y + h)
	{ return(root); }
	
      if(EZ_TreeNodeType(root) == EZ_TREE_NODE)
	x -= ((int)(EZ_TreeNodeTWidth(root) - EZ_ItemWidth(item)) >> 1);

      tw = EZ_TreeNodeTWidth(root);
      th = EZ_TreeNodeTHeight(root);
      if(x0 >= x && y0 >= y && x0 < x + tw && y0 < y +th)
	{
	  EZ_TreeNode *tmp;
	  children = EZ_TreeNodeChildren(root);
	  while(children)
	    {
	      tmp = EZ_FindPointerOnTreeNode(x0,y0, children);
	      if(tmp) { return(tmp); }
	      children = EZ_TreeNodeSibling(children);
	    }
	}
    }
  return(NULL);
}
  
/****************************************************************
 *
 * Find the adj branch to item on the display.
 */
EZ_TreeNode *EZ_FindTreeAdjBranch(root, node, direction)
     EZ_TreeNode *root, *node;
     int direction;
{
  if(root == (EZ_TreeNode *)NULL || node == (EZ_TreeNode *)NULL)
    return((EZ_TreeNode *)NULL);

  if(direction == -1) /* up */
    {
      if(node == root) return(root);
      {
	EZ_TreeNode *parent = EZ_TreeNodeParent(node);
	EZ_TreeNode *children = EZ_TreeNodeChildren(parent);
	if(children == node) return(parent);
	else
	  {
	    while(children && EZ_TreeNodeSibling(children) != node)
	      children =  EZ_TreeNodeSibling(children);
	    while(1)
	      {
		if(children) 
		  {
		    EZ_TreeNode *tmp = EZ_TreeNodeChildren(children);
		    if(tmp == (EZ_TreeNode *)NULL) return(children);
		    else 
		      {
			while(tmp && EZ_TreeNodeSibling(tmp))
			  tmp =  EZ_TreeNodeSibling(tmp);
		      }
		    children = tmp;
		  }
		else break;
	      }
	    return(children);
	  }
      }
    }
  else /* down */
    {
      EZ_TreeNode *children = EZ_TreeNodeChildren(node);
      if(children) return(children);
      else
	{
	  EZ_TreeNode *sibling = EZ_TreeNodeSibling(node);
	  if(sibling) return(sibling);
	  else
	    {
	      EZ_TreeNode *parent = EZ_TreeNodeParent(node);
	      while(parent && parent != root)
		{
		  if(EZ_TreeNodeSibling(parent))
		    {
		      return(EZ_TreeNodeSibling(parent));
		      break;
		    }
		  else parent = EZ_TreeNodeParent(parent);
		}
	    }
	}
    }
  return(NULL);
}

void tree_scroll_update_selection(widget, node)
     EZ_Widget *widget;
     EZ_TreeNode   *node;
{
  EZ_Item *item = EZ_TreeNodeItem(node);

  if(item)
    {
      int x0 = EZ_ItemOriginX(item);
      int y0 = EZ_ItemOriginY(item);
      int iw = EZ_ItemWidth(item);
      int ih = EZ_ItemHeight(item);
      int padb = EZ_WidgetPadB(widget);
      int bw = EZ_WidgetBorderWidth(widget) + padb;
      int yoffset = bw + EZ_WidgetPadY(widget);
      int xoffset = bw + EZ_WidgetPadX(widget);
      int Rw = EZ_WidgetWidth(widget);
      int Rh = EZ_WidgetHeight(widget);
      int w = Rw - (xoffset <<1);
      int h = Rh - (yoffset <<1);

      if( y0 < 0 || x0 < 0 || x0 + iw > w || y0 + ih >h)
	{ /* part is outside */
	  if(y0 < 0)
	    {
	      int itmp  =  - y0  + EZ_ITreeWYStart(widget);
	      EZ_ITreeWYStart(widget) = (itmp < 0 ? itmp: 0);
	      if(x0 < 0)
		{
		  int itmp  = - x0  + EZ_ITreeWXStart(widget);
		  EZ_ITreeWXStart(widget) = (itmp < 0 ? itmp: 0);
		}
	      else if(x0 + iw > w)
		{
		  EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
		  int  tw = EZ_TreeNodeTWidth(rt) + (xoffset <<1);
		  if(tw > EZ_WidgetWidth(widget))
		    {
		      int itmp = EZ_ITreeWXStart(widget) - iw + w - x0;
		      int dif = EZ_WidgetWidth(widget) - tw;
		      EZ_ITreeWXStart(widget) = (itmp > dif ? itmp:dif);
		    }
		}
	    }
	  else if(y0 + ih >h)
	    {
	      EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
	      int  th = EZ_TreeNodeTHeight(rt) + (yoffset <<1);
	      if(th > EZ_WidgetHeight(widget))
		{
		  int itmp =  EZ_ITreeWYStart(widget) - ih + h - y0; 
		  int dif = EZ_WidgetHeight(widget) - th;
		  EZ_ITreeWYStart(widget) = (itmp > dif ? itmp: dif);
		}
	      if(x0 < 0)
		{
		  int itmp  = - x0  + EZ_ITreeWXStart(widget);
		  EZ_ITreeWXStart(widget) = (itmp < 0 ? itmp: 0);
		}
	      else if(x0 + iw > w)
		{
		  EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
		  int  tw = EZ_TreeNodeTWidth(rt) + (xoffset <<1);
		  if(tw > EZ_WidgetWidth(widget))
		    {
		      int itmp = EZ_ITreeWXStart(widget) - iw + w - x0;
		      int dif = EZ_WidgetWidth(widget) - tw;
		      EZ_ITreeWXStart(widget) = (itmp > dif ? itmp:dif);
		    }
		}
	    }
	  else if(x0 < 0)
	    {
	      int itmp  = - x0  + EZ_ITreeWXStart(widget);
	      EZ_ITreeWXStart(widget) = (itmp < 0 ? itmp: 0);
	    }
	  else if(x0 + iw > w)
	    {
	      EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
	      int  tw = EZ_TreeNodeTWidth(rt) + (xoffset <<1);
	      if(tw > EZ_WidgetWidth(widget))
		{
		  int itmp = EZ_ITreeWXStart(widget) - iw + w - x0;
		  int dif = EZ_WidgetWidth(widget) - tw;
		  EZ_ITreeWXStart(widget) = (itmp > dif ? itmp:dif);
		}
	    }
	  EZ_ITreeWSelection(widget) = node;	
	  EZ_DrawWidget(widget);
	}
      else
	{
	  EZ_TreeNode *snode = EZ_ITreeWSelection(widget);
	  unsigned long bg = EZ_ITreeWHighlightBG(widget);
	  unsigned long fg = EZ_ITreeWHighlightFG(widget);
	  EZ_Item *item = EZ_TreeNodeItem(snode);
	  EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),item,
			   bw,bw, Rw -(bw <<1), Rh - (bw << 1));		       

	  EZ_ITreeWSelection(widget) = node;	
	  item = EZ_TreeNodeItem(node);
	  EZ_HighlightOneItem(widget,EZ_WidgetWindow(widget),item,fg,bg,
			      bw,bw, Rw -(bw <<1), Rh - (bw << 1));
	}
    }
}

/***************************************************************************/
static void ITreeWTimerCallBack(timer, cdata)
     EZ_Timer *timer; 
     void *cdata;
{
  EZ_Widget *widget = (EZ_Widget *)cdata;
  if(widget && EZ_LookupWidgetFromAllHT(widget) == widget &&
     EZ_WidgetType(widget) == EZ_WIDGET_ITREE)
    {
      if(EZ_WidgetWindow(widget) && EZ_WidgetMapped(widget))
	{
	  EZ_TreeNode *rt = EZ_ITreeWRoot(widget);
	  EZ_TreeNode *sel = EZ_ITreeWSelection(widget);
	  if(rt && sel)
	    {
	      EZ_Item *item = EZ_TreeNodeItem(sel);
	      if(item)
		{
		  int padb = EZ_WidgetPadB(widget);
		  int Rx = EZ_WidgetBorderWidth(widget) + padb;
		  int Ry = Rx;
		  int Rw = EZ_WidgetWidth(widget) - (Rx+Rx);
		  int Rh = EZ_WidgetHeight(widget) -(Rx+Rx) ;

		  EZ_FlashItemOutline(widget,item, Rx,Ry,Rw,Rh);
		}
	    }
	}
    }
  else EZ_CancelTimer(timer);
}

/*********************************************************************/
void  EZ_FlashItemOutline(widget,item, Rx,Ry,Rw,Rh)
     EZ_Widget *widget;
     EZ_Item *item;
     int Rx,Ry,Rw,Rh;
{
  if(item != NULL) 
    {
      int y0 = (int)EZ_ItemOriginY(item);
      int x0 = (int)EZ_ItemOriginX(item);  
      int w = (int)EZ_ItemAWidth(item)-1;
      int h = (int)EZ_ItemAHeight(item)-1;  
      
      if(y0 >= Ry && y0 + h <= Ry+Rh && x0 >= Rx && x0 +w <= Rx+Rw)
	{  /* item is completely inside the drawable region */
	  XPoint points[5];
	  Window win = EZ_WidgetWindow(widget);
	  GC gc;
	  EZ_GetMovingDashedLineGC(&gc);
	  points[0].x = x0;     points[0].y = y0;
	  points[1].x = x0+w;   points[1].y = y0;
	  points[2].x = x0+w;   points[2].y = y0+h;
	  points[3].x = x0;     points[3].y = y0+h;
	  points[4].x = x0;     points[4].y = y0;
	  XDrawLines(EZ_Display, win, gc, points, 5, CoordModeOrigin);
	}
      else /* item is partially outside */
	{
	  int x_r,y_r,w_r,h_r;
	  XPoint points[2];
	  Window win = EZ_WidgetWindow(widget);
	  GC gc;
	  EZ_GetMovingDashedLineGC(&gc);
	  EZ_IntersectRectangles(Rx,Ry,Rw,Rh, 
				 x0,y0,w,h,
				 &x_r,&y_r, &w_r, &h_r);
	  if(y_r == y0) /* top edge is inside */
	    {
	      points[0].x = x_r;          points[0].y = y_r;
	      points[1].x = x_r + w_r;    points[1].y = y_r;
	      XDrawLines(EZ_Display, win, gc, points, 2, CoordModeOrigin);
	    }
	  if(w_r + x_r == x0 +w) /* right edge is inside */
	    {
	      points[0].x = x_r + w_r;          points[0].y = y_r;
	      points[1].x = x_r + w_r;    points[1].y = y_r + h_r;
	      XDrawLines(EZ_Display, win, gc, points, 2, CoordModeOrigin);
	    }
	  if(y_r+h_r == h+y0)
	    {
	      points[0].x = x_r + w_r;          points[0].y = y_r + h_r;
	      points[1].x = x_r;    points[1].y = y_r + h_r;
	      XDrawLines(EZ_Display, win, gc, points, 2, CoordModeOrigin);
	    }
	  if(x_r == x0)
	    {
	      points[0].x = x_r;          points[0].y = y_r + h_r;
	      points[1].x = x_r;          points[1].y = y_r;
	      XDrawLines(EZ_Display, win, gc, points, 2, CoordModeOrigin);
	    }
	}
    }
}
/***************************************************************************/
void EZ_DnDOutlineItem(widget,item, Rx,Ry,Rw,Rh)
     EZ_Widget *widget;
     EZ_Item *item;
     int Rx,Ry,Rw,Rh;
{
  if(item != NULL)
    {
      int y0 = (int)EZ_ItemOriginY(item);
      int x0 = (int)EZ_ItemOriginX(item);  
      int w = (int)EZ_ItemAWidth(item)-1;
      int h = (int)EZ_ItemAHeight(item)-1;  
      
      if(y0 >= Ry && y0 + h <= Ry+Rh && x0 >= Rx && x0 +w <= Rx+Rw)
	{  /* item is completely inside the drawable region */
	  XPoint points[5];
	  Window win = EZ_WidgetWindow(widget);
	  GC gc = EZ_DNDXORGC; 
	  
	  points[0].x = x0;     points[0].y = y0;
	  points[1].x = x0+w;   points[1].y = y0;
	  points[2].x = x0+w;   points[2].y = y0+h;
	  points[3].x = x0;     points[3].y = y0+h;
	  points[4].x = x0;     points[4].y = y0;
	  XDrawLines(EZ_Display, win, gc, points, 5, CoordModeOrigin);
	}
      else /* item is partially outside */
	{
	  int x_r,y_r,w_r,h_r;
	  XPoint points[2];
	  Window win = EZ_WidgetWindow(widget);
	  GC gc = EZ_DNDXORGC; 
	   
	  EZ_IntersectRectangles(Rx,Ry,Rw,Rh, 
				 x0,y0,w,h,
				 &x_r,&y_r, &w_r, &h_r);
	  if(y_r == y0) /* top edge is inside */
	    {
	      points[0].x = x_r;          points[0].y = y_r;
	      points[1].x = x_r + w_r;    points[1].y = y_r;
	      XDrawLines(EZ_Display, win, gc, points, 2, CoordModeOrigin);
	    }
	  if(w_r + x_r == x0 +w) /* right edge is inside */
	    {
	      points[0].x = x_r + w_r;          points[0].y = y_r;
	      points[1].x = x_r + w_r;    points[1].y = y_r + h_r;
	      XDrawLines(EZ_Display, win, gc, points, 2, CoordModeOrigin);
	    }
	  if(y_r+h_r == h+y0)
	    {
	      points[0].x = x_r + w_r;          points[0].y = y_r + h_r;
	      points[1].x = x_r;    points[1].y = y_r + h_r;
	      XDrawLines(EZ_Display, win, gc, points, 2, CoordModeOrigin);
	    }
	  if(x_r == x0)
	    {
	      points[0].x = x_r;          points[0].y = y_r + h_r;
	      points[1].x = x_r;          points[1].y = y_r;
	      XDrawLines(EZ_Display, win, gc, points, 2, CoordModeOrigin);
	    }
	}
    }
}


/***************************************************************************
 *
 *  Supporting functions.
 *
 ***************************************************************************/
void EZ_SetITreeWTree(widget, root)
     EZ_Widget *widget;
     EZ_TreeNode  *root;
{
  if(widget)
    {
      EZ_TreeNode *tmp = EZ_ITreeWRoot(widget);
      if(tmp && tmp != root) EZ_TreeDestroySubtree(tmp);
      EZ_ITreeWRoot(widget) = root;
      EZ_ITreeWSelection(widget) = NULL;
      if(root)
	{
	  EZ_Item *item = EZ_TreeNodeItem(root);
	  EZ_ItemDirty(item) = 1;
	}
      EZ_ITreeWXStart(widget) = 0;
      EZ_ITreeWYStart(widget) = 0;
      if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);
    }
}
/************************************************************************/

void EZ_ITreeSelectNode(widget, theNode, location)
     EZ_Widget *widget;
     EZ_TreeNode *theNode; 
     int *location;
{
  EZ_TreeNode *root = EZ_ITreeWRoot(widget);
  if(root)
    {
      EZ_TreeNode *ONode = EZ_ITreeWSelection(widget);
      if(EZ_TreeFindNode(root, theNode) == NULL) theNode = NULL;
      EZ_ITreeWSelection(widget) = theNode;
      if(ONode && ONode != theNode)  /* clear old selection */
	{
	  if(EZ_WidgetMapped(widget))
	    {
	      EZ_Item *item = EZ_TreeNodeItem(ONode);
	      int padb = EZ_WidgetPadB(widget);
	      int bw = EZ_WidgetBorderWidth(widget) + padb;
	      int Rx, Ry, Rw, Rh, ww, hh;
	      Rx = Ry = bw;
	      ww = (int)(EZ_WidgetWidth(widget));
	      Rw =  ww - (bw<<1);
	      hh = (int)(EZ_WidgetHeight(widget)); 
	      Rh =  hh - (bw<<1);
	      EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),item, Rx, Ry, Rw, Rh);
	    }
	}
      EZ_ITreeWSelection(widget) = theNode;
      if(theNode && theNode != ONode) /* highlight new selection */
      	{
	  if(EZ_WidgetMapped(widget))
	    {
	      if(location)
		{
		  EZ_Item *item = EZ_TreeNodeItem(theNode);
		  int x = EZ_ItemOriginX(item);
		  int y = EZ_ItemOriginY(item);
		  EZ_ITreeWXStart(widget) += location[0] - x;
		  EZ_ITreeWYStart(widget) += location[1] - y;
		  EZ_DrawWidget(widget);
		}
	      tree_scroll_update_selection(widget, theNode);
	      EZ_HandleMotionCallBacks(widget,EZ_ITreeWMotionCallBack(widget));
	    }
	}
    }
}


void EZ_ITreeSelectNodeUsingItem(widget, theItem, location)
     EZ_Widget *widget;
     EZ_Item *theItem;
     int *location;
{
  EZ_TreeNode *root = EZ_ITreeWRoot(widget);
  EZ_TreeNode *theNode;
  if(root)
    {
      EZ_TreeNode *ONode = EZ_ITreeWSelection(widget);
      theNode = EZ_TreeFindNodeUsingItem(root, theItem);

      if(ONode && ONode != theNode) /* clear old selection */
	{
	  if(EZ_WidgetMapped(widget))
	    {
	      EZ_Item *item = EZ_TreeNodeItem(ONode);
	      int padb = EZ_WidgetPadB(widget);
	      int bw = EZ_WidgetBorderWidth(widget) + padb;
	      int Rx, Ry, Rw, Rh, ww, hh;
	      Rx = Ry = bw;
	      ww = (int)(EZ_WidgetWidth(widget));
	      Rw =  ww - (bw<<1);
	      hh = (int)(EZ_WidgetHeight(widget)); 
	      Rh =  hh - (bw<<1);
	      EZ_UpdateOneItem(widget,EZ_WidgetWindow(widget),item, Rx, Ry, Rw, Rh);
	    }
	}
      EZ_ITreeWSelection(widget) = theNode;
      if(theNode  && theNode != ONode)
	{
	  if( EZ_WidgetMapped(widget))
	    {
	      if(location)
		{
		  EZ_Item *item = EZ_TreeNodeItem(theNode);
		  int x = EZ_ItemOriginX(item);
		  int y = EZ_ItemOriginY(item);
		  EZ_ITreeWXStart(widget) += location[0] - x;
		  EZ_ITreeWYStart(widget) += location[1] - y;
		  EZ_DrawWidget(widget);
		}
	      tree_scroll_update_selection(widget, theNode);
	      EZ_HandleMotionCallBacks(widget,EZ_ITreeWMotionCallBack(widget));
	    }
	}
    }
}

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

#undef _EZ_WIDGET_ITREE_C_

