/*********************************************************************
 *
 *         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 List Box                     ***
 ***                                                           ***
 *****************************************************************/
#define _EZ_WIDGET_ILIST_BOX_C_

#include "EZ_Widget.h"
#include <X11/Xatom.h>

/*********************************************************************
 * 
 *  Functions implemented in this file:
 */
EZ_Widget        *EZ_CreateIListBox MY_ANSIARGS((EZ_Widget *parent));
void             EZ_DrawWidgetIListBox MY_ANSIARGS((EZ_Widget *widget));
void             EZ_ComputeWidgetIListBoxSize MY_ANSIARGS((EZ_Widget *widget, int *w, int *h));
void             EZ_FreeWidgetIListBoxData MY_ANSIARGS((EZ_Widget *widget));
void             EZ_IListBoxEventHandle MY_ANSIARGS((EZ_Widget *widget, XEvent *event));

void             EZ_SetIListBoxData MY_ANSIARGS((EZ_Widget *widget, char **items, int nitems));
void             EZ_AppendIListBoxItem MY_ANSIARGS((EZ_Widget *widget, char *item));
void             EZ_InsertIListBoxItem MY_ANSIARGS((EZ_Widget *widget, char *item, int where));
void             EZ_DeleteIListBoxItem MY_ANSIARGS((EZ_Widget *widget, int idx));
void             EZ_DeleteIListBoxString MY_ANSIARGS((EZ_Widget *widget, char *str));
void             EZ_ClearIListBox MY_ANSIARGS((EZ_Widget *widget));
char             *EZ_GetIListBoxSelectedItem MY_ANSIARGS((EZ_Widget *widget));
char             *EZ_GetIListBoxSelection MY_ANSIARGS((EZ_Widget *widget));
void             EZ_GetIListBoxData MY_ANSIARGS((EZ_Widget *widget, listboxentry **dat, int *cont));

void             EZ_ModifyIListBoxItem MY_ANSIARGS((EZ_Widget *widget, char *item, int idx));

void             EZ_IListBoxSelectItem MY_ANSIARGS((EZ_Widget *widget, char *str));
void             EZ_IListBoxSelectItemUsingIdx MY_ANSIARGS((EZ_Widget *widget, int idx));
/*********************************************************************
 * 
 *  Global data
 */
extern Time  EZ_LastButton1ClickTime;

/*********************************************************************
 * 
 * Local functions.
 */
static void   EZ_IListBoxSetSelection MY_ANSIARGS((EZ_Widget *widget));
static void   update_il_scrollbars MY_ANSIARGS((EZ_Widget *widget));

/*********************************************************************
 * 
 *  Local Variables.
 */
static EZ_WidgetHandle EZ_IListBoxHandle =
{
  EZ_ComputeWidgetIListBoxSize,
  EZ_DrawWidgetIListBox,
  EZ_FreeWidgetIListBoxData,
  EZ_IListBoxEventHandle,
};

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

EZ_Widget  *EZ_CreateIListBox(parent)
     EZ_Widget  *parent;     /* parent widget    */
{
  EZ_Widget  *wptr;
  int        i;

  wptr = EZ_CreateNewWidget(parent);
  /*--------------------------------------------------
   * Register the handling functions for ...
   *  has to be done after  EZ_CreateNewWiget.
   *-------------------------------------------------*/
  EZ_WidgetHandlingFunctions[EZ_WIDGET_ILIST_BOX] = &EZ_IListBoxHandle;

  EZ_SetWidgetTypeAndNames(wptr,EZ_WIDGET_ILIST_BOX);

  EZ_WidgetBorderStyle(wptr)  = EZ_BORDER_DOWN;
  EZ_WidgetBorderWidth(wptr)  = 2;
  EZ_WidgetPadB(wptr) = 2;

  EZ_IListBoxFont(wptr) = EZ_GetFontFromId(EZ_BOLD_FONT);
  EZ_IListBoxTHeight(wptr)    = 0;

  EZ_IListBoxNLines(wptr)  = 0;
  EZ_IListBoxSLine(wptr) = -1;
  EZ_IListBoxSLineEnd(wptr) = -1;
  EZ_IListBoxFirstLine(wptr)  = 0;
  EZ_IListBoxLastLine(wptr) = 0;
  EZ_IListBoxTextBG(wptr) = ~0;
  EZ_IListBoxSelectionBG(wptr) = ~0;

  EZ_IListBoxListBox(wptr) = (EZ_Widget *)NULL;
  EZ_IListBoxHScroll(wptr) = (EZ_Widget *)NULL;
  EZ_IListBoxVScroll(wptr) = (EZ_Widget *)NULL;

  EZ_IListBoxMaxHSize(wptr) = 1;
  EZ_IListBoxHPageSize(wptr) = 1;
  EZ_IListBoxHStart(wptr) = 0;
  EZ_IListBoxTakeMotion(wptr) = 0;
  EZ_IListBoxCallBackOnRelease(wptr) = 0;

  EZ_IListBoxData(wptr) = ( listboxentry *)my_malloc(128 * sizeof( listboxentry), _ILIST_BOX_D_);
  if(!(EZ_IListBoxData(wptr))) EZ_OutOfMemory("EZ_CreateIListBox");
  EZ_IListBoxSpace(wptr) = 128;
  for(i = 0; i < EZ_IListBoxSpace(wptr); i++)
    EZ_IListBoxData(wptr)[i].data = (char *)NULL;  

  EZ_IListBoxPageSize(wptr) = 1;

  EZ_SetWidgetFocusableFlag(wptr);  

  EZ_IListBoxOptEntry(wptr)  = NULL;
  EZ_IListBoxFSL(wptr)  = NULL;
  
  return(wptr);
}


void  EZ_FreeWidgetIListBoxData(widget)
     EZ_Widget *widget;
{
  int          i;
  listboxentry *tmp = EZ_IListBoxData(widget);
  for(i = 0; i < EZ_IListBoxSpace(widget); i++)
    {
      if(tmp[i].data != (char *)NULL)
	(void)my_free((char *) tmp[i].data);
    }
  (void) my_free((char *)tmp);
}
 
/********************************************************************
 *
 *  Figure out the dimension of an internal list box.
 */
void EZ_ComputeWidgetIListBoxSize(widget, w, h)
     EZ_Widget *widget;
     int             *w, *h;
{
  int   height, cw, ch, padb;

  if(!(EZ_GetWidgetSizeComputedFlag(widget)))
    { 
      EZ_IListBoxTHeight(widget) = EZ_IListBoxFont(widget)->max_bounds.ascent +
	EZ_IListBoxFont(widget)->max_bounds.descent;
      EZ_IListBoxFontAscent(widget) = EZ_IListBoxFont(widget)->max_bounds.ascent;

      EZ_SetWidgetSizeComputedFlag(widget);
    }
  height = EZ_IListBoxTHeight(widget);
  cw = EZ_WidgetPadX(widget) + EZ_WidgetBorderWidth(widget);
  ch = EZ_WidgetPadY(widget) + EZ_WidgetBorderWidth(widget);
  cw = cw + cw + height * 2; /* default size to fit 2 lines, */
  ch = ch + ch + height * 2; /* each line can hold 2 chars   */

  padb = EZ_WidgetPadB(widget)<<1;
  *w = cw + padb;
  *h = ch + padb;
}

/****************************************************************************
 *
 *  Draw a IListBox widget
 */

void  EZ_DrawWidgetIListBox(wptr)
     EZ_Widget *wptr;
{
  int             w, h,x,y,xx,yy,hinc, hinc1, bw, padb,padb2;
  int             itmp, hsize = 1;
  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;
  {
    int tmp, tmp1, height;

    tmp1 = EZ_WidgetHeight(wptr) - EZ_WidgetPadY(wptr) - padb2 -
      ((EZ_WidgetBorderWidth(wptr))<<1);
    height = EZ_IListBoxTHeight(wptr);
    tmp = tmp1/height;
    EZ_IListBoxPageSize(wptr) = tmp;    /* page size */
    if(EZ_IListBoxFirstLine(wptr)< 0) EZ_IListBoxFirstLine(wptr) = 0;
    tmp1 = EZ_IListBoxFirstLine(wptr) + tmp -1;
    if(tmp1 >= EZ_IListBoxNLines(wptr)) 
      {
	int tmp2 = EZ_IListBoxNLines(wptr) - tmp;
	EZ_IListBoxFirstLine(wptr) = tmp2 < 0? 0: tmp2;
      }
    if(tmp1 < EZ_IListBoxNLines(wptr)) 	EZ_IListBoxLastLine(wptr) = tmp1;
    else  EZ_IListBoxLastLine(wptr) = EZ_IListBoxNLines(wptr) - 1;

    EZ_IListBoxHPageSize(wptr) = EZ_WidgetWidth(wptr) - ((bw+EZ_WidgetPadX(wptr))<<1) - padb2;

    if(EZ_IListBoxHStart(wptr) > 0) EZ_IListBoxHStart(wptr) = 0;
    tmp1 =  EZ_IListBoxHPageSize(wptr) - EZ_IListBoxMaxHSize(wptr);
    if(tmp1 < 0 && EZ_IListBoxHStart(wptr) < tmp1) EZ_IListBoxHStart(wptr) = tmp1;
  }
  /*-----------------------------------------------------------
   *  Create a pixmap, draw into this pixmap in background and
   *  copy to the button window when finished.
   *----------------------------------------------------------*/
  pixmap = XCreatePixmap(EZ_DisplayForWidgets, win, w, h, EZ_DepthForWidgets);    
  {
    EZ_Bitmap *bitmap = EZ_WidgetPixmap(wptr);
    if(bitmap && EZ_BitmapType(bitmap) == EZ_BITMAP_IS_PIXMAP)
      {
	int fl = EZ_IListBoxFirstLine(wptr);
	int lw = EZ_IListBoxTHeight(wptr);
	int yy = fl * lw;
	gc = EZ_TILEGC;
	XSetTile(EZ_Display,gc,bitmap->pixmap);
	XSetTSOrigin(EZ_Display, gc, (int)(EZ_IListBoxHStart(wptr)), -yy);
      }
    else if(EZ_IListBoxTextBG(wptr) != ~0)
      {
	XSetForeground(EZ_Display, EZ_WRITABLEGC, EZ_IListBoxTextBG(wptr));	
	gc = EZ_WRITABLEGC;
      }
    else EZ_GetBackgroundGC(wptr, &gc, &bgpv, 0, 0);
  }
  XFillRectangle(EZ_DisplayForWidgets, pixmap, gc, padb,padb, w -padb2, h-padb2);   
  /*-------------------
   *  Render the text
   *------------------*/
  if( EZ_GetWidgetDisabledFlag(wptr) ) 
    {
      EZ_GetBrightBDGC(wptr, &gc);
      XSetFont(EZ_DisplayForWidgets, gc, EZ_IListBoxFont(wptr)->fid);
    }
  else
    {
      if(EZ_IListBoxForeground(wptr) != EZ_DefaultForeground || 
	 EZ_IListBoxFont(wptr) != EZ_GetFontFromId(EZ_BOLD_FONT))
	{
	  XSetFont(EZ_DisplayForWidgets, EZ_WRITABLEGC, EZ_IListBoxFont(wptr)->fid);
	  XSetForeground(EZ_DisplayForWidgets, EZ_WRITABLEGC, EZ_IListBoxForeground(wptr));	
	  gc = EZ_WRITABLEGC;
	}
      else gc = EZ_BUTTON_TEXTGC;
    }

  xx = EZ_IListBoxHStart(wptr);
  yy = 0;

  x = xx  + EZ_WidgetPadX(wptr) + bw + padb;
  y = yy + EZ_WidgetBorderWidth(wptr) + EZ_WidgetPadY(wptr) + padb +
    EZ_IListBoxFontAscent(wptr);
  hinc = EZ_IListBoxTHeight(wptr);
  hinc1 = EZ_IListBoxFontAscent(wptr);

  if(EZ_IListBoxNLines(wptr) != 0)
    {
      int  len, i, theY = y;
      char *str ;
      register  listboxentry *item = EZ_IListBoxData(wptr) + EZ_IListBoxFirstLine(wptr);

      for(i = EZ_IListBoxFirstLine(wptr); i <= EZ_IListBoxLastLine(wptr); i++, item++, theY += hinc)
	{
	  if(i >= EZ_IListBoxSLine(wptr) && i <= EZ_IListBoxSLineEnd(wptr))
	    {
	      /* do not use EZ_WRITABLEGC ! */ 
	      GC tmpgc = EZ_BD_DARKGC; 
	      unsigned long bg = EZ_IListBoxSelectionBG(wptr);
	      if(bg == ~0) EZ_GetBrightBDpv(wptr, &bg);
	      XSetForeground(EZ_Display, tmpgc, bg);
	      XFillRectangle(EZ_Display, pixmap, tmpgc, bw,theY-hinc1, w-(bw+bw), hinc); 
	    }
	  str = item->data;
	  len =  item->length;
	  itmp = XTextWidth(EZ_IListBoxFont(wptr),str, len);
	  if(itmp > hsize) hsize = itmp;
	  XDrawString(EZ_DisplayForWidgets, pixmap, gc, x,theY, str, len);
	}
    }
  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_DisplayForWidgets,pixmap,win, EZ_WRITABLEGC,0,0,w,h,0,0); 
  XFreePixmap(EZ_DisplayForWidgets, pixmap); 

  EZ_IListBoxMaxHSize(wptr) = hsize;
  update_il_scrollbars(wptr);
}
/*************************************************************************/
static void update_il_scrollbars(wptr)
     EZ_Widget *wptr;
{
  if(EZ_IListBoxHScroll(wptr) != (EZ_Widget *)NULL)
    {
      EZ_UpdateScrollbarI(EZ_IListBoxHScroll(wptr),
			  EZ_IListBoxMaxHSize(wptr),
			  EZ_IListBoxHPageSize(wptr),
			  EZ_IListBoxHStart(wptr));
    }

  if(EZ_IListBoxVScroll(wptr) != (EZ_Widget *)NULL)
    {
      EZ_UpdateScrollbarI(EZ_IListBoxVScroll(wptr),
			 EZ_IListBoxNLines(wptr),
			 EZ_IListBoxPageSize(wptr),
			 EZ_IListBoxFirstLine(wptr));
    }
}
/*************************************************************************
 *
 *  Event handling
 */
void  EZ_IListBoxEventHandle(widget, event)
     EZ_Widget *widget;
     XEvent          *event;
{
  XEvent    xevent;
  int       x, y, ysave, ww, hh, line, offset, xoffset, done, xyset, button;
  int       bw, padb;

  if(widget == (EZ_Widget *)NULL) return;
  if( EZ_GetWidgetDisabledFlag(widget) )
    {
      if(event->type == Expose)	EZ_DrawWidget(widget);      
      return;
    }
  bw = EZ_WidgetBorderWidth(widget);
  padb = EZ_WidgetPadB(widget);
  offset = EZ_WidgetPadY(widget) + bw + padb;
  xoffset = EZ_WidgetPadX(widget) + bw + padb;
  ww = EZ_WidgetWidth(widget);
  hh = EZ_WidgetHeight(widget);

  switch(event->type)
    {
    case Expose:
      EZ_DrawWidget(widget);      
      break;
    case MappingNotify:
      XRefreshKeyboardMapping((XMappingEvent *)event);
      break;
    case SelectionRequest:
      EZ_HandleSelectionRequest(widget,event);
      break;
    case SelectionClear:
      {
	int line =  EZ_IListBoxSLine(widget);
	if(line != EZ_IListBoxSLineEnd(widget))
	  {
	    EZ_IListBoxSLineEnd(widget) = line;
	    EZ_DrawWidget(widget);
	  }
      }
    break;
    case ButtonPress:
      if(EZ_IListBoxNLines(widget) <= 0) break;
      {
	/* first, get focus if we don;t have it */
	{ 
	  Window fw; int rt;
	  EZ_Widget *tmp;
	  XGetInputFocus(EZ_Display, &fw, &rt);
	  tmp = EZ_LookupWidgetFromMappedHT(fw);
	  if(tmp != widget) EZ_SetFocusTo(widget);
	}
	button = event->xbutton.button;
	if(button == EZ_Btn1) 
	  {
	    Time  time_stamp = event->xbutton.time;   
	    int   elapsedtime = EZ_LastButton1ClickTime - time_stamp;
	    
	    EZ_LastButton1ClickTime = time_stamp;
	    y = event->xbutton.y - offset;
	    line = y/EZ_IListBoxTHeight(widget) + EZ_IListBoxFirstLine(widget);
	    if(line != EZ_IListBoxSLine(widget) &&
	       line <= EZ_IListBoxLastLine(widget))
	      {
		EZ_IListBoxSLine(widget) = line;
		EZ_IListBoxSLineEnd(widget) = line;
		/* EZ_IListBoxSetSelection(widget);*/
		EZ_DrawWidget(widget);
		EZ_HandleMotionCallBacks(widget,EZ_IListBoxMotionCallBack(widget));
	      }
	    /*
	     * a double click cause the execuation of the call back function
	     */
	    if(ABSV(elapsedtime) < DOUBLE_CLICK_TIME_INTERVAL)
	      { 
		/* EZ_Widget *twidget = EZ_IListBoxListBox(widget);*/
		EZ_ExecuteWidgetCallBacks(widget);
		/* if(twidget) {EZ_ExecuteWidgetCallBacks(twidget);}*/
		return;
	      }
	    /*
	     * wait for a matching ButtonRelease. In the mean time, process
	     * MotionNotify and SelectionRequest events.
	     */
	  waitForButtonRelease:
	    xyset = 0; 
	    x = y = 0; /* to please gcc */
	    done = 0;
	    xevent.type = 0;
	    while(!done)
	      {
		if(XPending(EZ_Display)) 
		  {
		    while(XPending(EZ_Display)) 
		      {
			if(xevent.type == ButtonRelease && xevent.xbutton.button == EZ_Btn1) break;
			else if(xevent.type == MotionNotify)
			  {
			    x = xevent.xmotion.x - xoffset;
			    y = xevent.xmotion.y - offset;			
			    xyset = 1;
			  }
			else if(xevent.type == Expose || xevent.type == SelectionClear || 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(!done && xyset)  /* do scroll if necessary */
		  {
		    if(y < 0) line = -1;
		    else line  = y/EZ_IListBoxTHeight(widget) +  EZ_IListBoxFirstLine(widget);
		    if(line >= EZ_IListBoxFirstLine(widget) &&
		       line <= EZ_IListBoxLastLine(widget))
		      {
			if(line != EZ_IListBoxSLine(widget))
			  {
			    EZ_IListBoxSLine(widget) = line;
			    EZ_IListBoxSLineEnd(widget) = line;
			    /*EZ_IListBoxSetSelection(widget);*/
			    EZ_DrawWidget(widget);    
			    EZ_HandleMotionCallBacks(widget,EZ_IListBoxMotionCallBack(widget));  		  
			  }
		      }
		    else if(line > EZ_IListBoxLastLine(widget))
		      {
			if(EZ_IListBoxLastLine(widget) < EZ_IListBoxNLines(widget) - 1)
			  {
			    EZ_IListBoxFirstLine(widget) += 1;
			    EZ_IListBoxLastLine(widget) += 1;
			    EZ_IListBoxSLine(widget) = EZ_IListBoxLastLine(widget);
			    EZ_IListBoxSLineEnd(widget) = EZ_IListBoxLastLine(widget);
			    /*EZ_IListBoxSetSelection(widget); */
			    EZ_DrawWidget(widget);    
			    EZ_HandleMotionCallBacks(widget,EZ_IListBoxMotionCallBack(widget));  		  
			  }
		      }
		    else  if(line < EZ_IListBoxFirstLine(widget))
		      {
			if(EZ_IListBoxFirstLine(widget) > 0)
			  {
			    EZ_IListBoxFirstLine(widget) -= 1;
			    EZ_IListBoxLastLine(widget) -= 1;
			    EZ_IListBoxSLine(widget) =EZ_IListBoxFirstLine(widget);
			    EZ_IListBoxSLineEnd(widget) =EZ_IListBoxFirstLine(widget);
			    /* EZ_IListBoxSetSelection(widget);*/
			    EZ_DrawWidget(widget);    
			    EZ_HandleMotionCallBacks(widget,EZ_IListBoxMotionCallBack(widget)); 
			  }
		      }
		    /*
		     * scroll horizontally
		     */
		    if(x < 0 )
		      {
			if(EZ_IListBoxHStart(widget) < 0)
			  {
			    int iitmp = EZ_IListBoxHStart(widget) + 10;
			    if(iitmp > 0) iitmp = 0;
			    EZ_IListBoxHStart(widget) = iitmp;
			    EZ_DrawWidget(widget);  
			  }
		      }
		    else if( x > EZ_WidgetWidth(widget) - (xoffset<<1))
		      {
			int jjtmp = EZ_IListBoxHPageSize(widget) - EZ_IListBoxMaxHSize(widget);
			if(EZ_IListBoxHStart(widget) > jjtmp)
			  {
			    int iitmp = EZ_IListBoxHStart(widget) - 10;
			    if(iitmp < jjtmp) iitmp = jjtmp;
			    EZ_IListBoxHStart(widget) = iitmp;
			    EZ_DrawWidget(widget);  
			  }
		      }
		  }
	      }
	    /* for special listbox ... popup in optionalEntry  */
	    if(EZ_IListBoxCallBackOnRelease(widget))
	      {
		/* EZ_Widget *twidget = EZ_IListBoxListBox(widget); */
		EZ_ExecuteWidgetCallBacks(widget);
		/*if(twidget) {EZ_ExecuteWidgetCallBacks(twidget);} */
	      }
	    EZ_IListBoxSetSelection(widget);
	  }
	else if(button == EZ_Btn2)  /* set the selection only */
	  {
	    ysave = y = event->xbutton.y - offset;
	    line = y/EZ_IListBoxTHeight(widget) + EZ_IListBoxFirstLine(widget);
	    if(line != EZ_IListBoxSLine(widget) &&
	       line <= EZ_IListBoxLastLine(widget))
	      {
		EZ_IListBoxSLine(widget) = line;
		EZ_IListBoxSLineEnd(widget) = line;
		/* EZ_IListBoxSetSelection(widget);*/
		EZ_DrawWidget(widget);
		EZ_HandleMotionCallBacks(widget,EZ_IListBoxMotionCallBack(widget));
	      }
	    /*
	     * wait for a matching ButtonRelease. In the mean time, process
	     * MotionNotify and SelectionRequest events.
	     */
	    xyset = 0;
	    done = 0;
	    xevent.type = 0;
	    x = y = 0; /* to make gcc happy */
	    while(!done)
	      {
		if(XPending(EZ_Display)) 
		  {
		    while(XPending(EZ_Display)) 
		      {
			if(xevent.type == ButtonRelease && xevent.xbutton.button == EZ_Btn2)
			  {
			    done = 1;
			    break;
			  }
			else if(xevent.type == MotionNotify)
			  {
			    x = xevent.xmotion.x - xoffset;
			    y = xevent.xmotion.y - offset;			
			    xyset = 1;
			  }
			else if(xevent.type == Expose || xevent.type == SelectionClear || 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(!done && xyset)  /* do scroll if necessary */
		  {
		    if(y < 0) line = -1;
		    else line  = y/EZ_IListBoxTHeight(widget) +  EZ_IListBoxFirstLine(widget);
		    if(line >= EZ_IListBoxFirstLine(widget) &&
		       line <= EZ_IListBoxLastLine(widget))
		      {
			int bgn = EZ_IListBoxSLine(widget);
			int end = EZ_IListBoxSLineEnd(widget);
			if(line != bgn && line != end)
			  {
			    if(line < bgn) { EZ_IListBoxSLine(widget) = line; }
			    else if(line > end) { EZ_IListBoxSLineEnd(widget) = line;}
			    else  /* (line >= bgn && line <= end) */
			      {
				if(y < ysave) EZ_IListBoxSLineEnd(widget) = line;
				else EZ_IListBoxSLine(widget) = line;
			      }
			    ysave = y;
			    EZ_DrawWidget(widget);  
			    /* EZ_IListBoxSetSelection(widget); */
			  }
		      }
		    else if(line > EZ_IListBoxLastLine(widget))
		      {
			if(EZ_IListBoxLastLine(widget) < EZ_IListBoxNLines(widget) - 1)
			  {
			    EZ_IListBoxFirstLine(widget) += 1;
			    EZ_IListBoxLastLine(widget) += 1;
			    EZ_IListBoxSLineEnd(widget) = EZ_IListBoxLastLine(widget);
			    EZ_DrawWidget(widget);  
			    /* EZ_IListBoxSetSelection(widget);*/
			  }
		      }
		    else  if(line < EZ_IListBoxFirstLine(widget))
		      {
			if(EZ_IListBoxFirstLine(widget) > 0)
			  {
			    EZ_IListBoxFirstLine(widget) -= 1;
			    EZ_IListBoxLastLine(widget) -= 1;
			    EZ_IListBoxSLine(widget) =EZ_IListBoxFirstLine(widget);
			    EZ_DrawWidget(widget);  
			    /* EZ_IListBoxSetSelection(widget); */
			  }
		      }
		    /*
		     * scroll horizontally
		     */
		    if(x < 0 )
		      {
			if(EZ_IListBoxHStart(widget) < 0)
			  {
			    int iitmp = EZ_IListBoxHStart(widget) + 10;
			    if(iitmp > 0) iitmp = 0;
			    EZ_IListBoxHStart(widget) = iitmp;
			    EZ_DrawWidget(widget);  
			  }
		      }
		    else if( x > EZ_WidgetWidth(widget) - (xoffset<<1))
		      {
			int jjtmp = EZ_IListBoxHPageSize(widget) - EZ_IListBoxMaxHSize(widget);
			if(EZ_IListBoxHStart(widget) > jjtmp)
			  {
			    int iitmp = EZ_IListBoxHStart(widget) - 10;
			    if(iitmp < jjtmp) iitmp = jjtmp;
			    EZ_IListBoxHStart(widget) = iitmp;
			    EZ_DrawWidget(widget);  
			  }
		      }
		  }
	      }
	    EZ_IListBoxSetSelection(widget);	  
	  }
      }
      break;
    case MotionNotify:
      {
	unsigned int mask;
	if(EZ_Btn1 == Button1) mask = Button1Mask;
	else if(EZ_Btn1 == Button2) mask = Button2Mask;
	else mask = Button3Mask;
	if(EZ_IListBoxTakeMotion(widget) &&
	   EZ_IListBoxNLines(widget) > 0 && (event->xmotion.state & mask))
	  goto waitForButtonRelease;
      }
    break;
    case KeyPress:
      if(EZ_IListBoxNLines(widget) <= 0) break;
      if(EZ_IListBoxSLine(widget) == -1) 
	{
	  EZ_IListBoxSLine(widget) = EZ_IListBoxFirstLine(widget);
	}
      EZ_IListBoxSLineEnd(widget) = EZ_IListBoxSLine(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
	    line = EZ_IListBoxSLine(widget) - move_unit;
	    if(line < 0) line = 0;
	    if(EZ_IListBoxFirstLine(widget) > line) EZ_IListBoxFirstLine(widget) = line;
	    else if(EZ_IListBoxLastLine(widget) < line)
	      {
		int d = line - EZ_IListBoxLastLine(widget);
		EZ_IListBoxFirstLine(widget) += d;
	      }
	    EZ_IListBoxSLine(widget) = line;
	    EZ_IListBoxSLineEnd(widget) = line;
	    EZ_IListBoxSetSelection(widget);
	    EZ_DrawWidget(widget);
	    EZ_HandleMotionCallBacks(widget,EZ_IListBoxMotionCallBack(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
	    line = EZ_IListBoxSLine(widget) + move_unit;
	    if(line >= EZ_IListBoxNLines(widget)) line = EZ_IListBoxNLines(widget) -1;
	    if(EZ_IListBoxLastLine(widget) < line)
	      {
		int d = line - EZ_IListBoxLastLine(widget);
		EZ_IListBoxFirstLine(widget) += d;
	      }
	    else if(EZ_IListBoxFirstLine(widget) > line)
	      EZ_IListBoxFirstLine(widget) = line;
	    
	    EZ_IListBoxSLine(widget) = line;
	    EZ_IListBoxSLineEnd(widget) = line;
	    EZ_IListBoxSetSelection(widget);
	    EZ_DrawWidget(widget);
	    EZ_HandleMotionCallBacks(widget,EZ_IListBoxMotionCallBack(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
	    {
	      int aaa = (move_unit << 3);
	      int itmp = EZ_IListBoxHStart(widget) + aaa;
	      if(itmp >= 0) itmp = 0;
	      else if(itmp < EZ_IListBoxHPageSize(widget)
		      - EZ_IListBoxMaxHSize(widget))
		itmp =  EZ_IListBoxHPageSize(widget) - 
		  EZ_IListBoxMaxHSize(widget);

	      if(EZ_IListBoxHStart(widget) != itmp)
		{
		  EZ_IListBoxHStart(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
	    {
	      int aaa = -(move_unit << 3);
	      int itmp = EZ_IListBoxHStart(widget) + aaa;
	      if(itmp >= 0) itmp = 0;
	      else if(itmp < EZ_IListBoxHPageSize(widget)
		      - EZ_IListBoxMaxHSize(widget))
		itmp =  EZ_IListBoxHPageSize(widget) - 
		  EZ_IListBoxMaxHSize(widget);

	      if(EZ_IListBoxHStart(widget) != itmp)
		{
		  EZ_IListBoxHStart(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_Widget *twidget = EZ_IListBoxListBox(widget); */
	      EZ_ExecuteWidgetCallBacks(widget);
	      /* if(twidget) {EZ_ExecuteWidgetCallBacks(twidget);} */
	    }
	    break;
	  default:
	    break;
	  }
	{
	  XEvent tmpevent; 
	  while(XCheckTypedEvent(EZ_Display, KeyPress, &tmpevent))
	    if(EZ_FilterEvent(&tmpevent))
	      EZ_InvokePrivateEventHandler(&tmpevent);
	}
      }      
      break;
    case EnterNotify:
      break;
    case LeaveNotify:
      break;
    default:
      break;
    }
}
/***********************************************************************************/

void  EZ_AppendIListBoxItem(widget, str)
     EZ_Widget *widget;
     char      *str; 
{
  EZ_InsertIListBoxItem(widget, str, -1);
}
/***********************************************************************************/
void  EZ_InsertIListBoxItem(widget, str, where)
     EZ_Widget *widget;
     char      *str; 
     int       where;
{
  int space, nlines, i, length;
  listboxentry *item;

  if(widget == (EZ_Widget *)NULL || EZ_WidgetType(widget) != EZ_WIDGET_ILIST_BOX)
    return;  
  if(str == (char *)NULL) return;
  
  space = EZ_IListBoxSpace(widget);
  nlines = EZ_IListBoxNLines(widget);

  if(space <= nlines +1)
    {
      EZ_IListBoxData(widget) = (listboxentry *)
	my_realloc(EZ_IListBoxData(widget), (space + 128) * sizeof(listboxentry),  _ILIST_BOX_D_);      
      if(!EZ_IListBoxData(widget)) EZ_OutOfMemory("EZ_InsertIListBoxItem");
      for(i = space; i < space + 128; i++)
	EZ_IListBoxData(widget)[i].data = (char *)NULL;
      EZ_IListBoxSpace(widget) += 128;
    }
  /* insert at the end */
  if(where < 0 || where >= nlines)
    item = EZ_IListBoxData(widget) + nlines;
  else /* insert at the begining */ 
    { 
      listboxentry *table = EZ_IListBoxData(widget);
      for(i = nlines -1; i >= where; i--)
	{
	  table[i+1].data = table[i].data;
	  table[i+1].length = table[i].length;
	}
      item = table + where;
      item->data = (char *)NULL;
      { /* 5-19-97, */
	int itmp = EZ_IListBoxSLine(widget);
	if(itmp >= 0)
	  {
	    if(where <= itmp)
	      {
		EZ_IListBoxSLine(widget) += 1;
		EZ_IListBoxSLineEnd(widget) += 1;
	      }
	  }
      }
    }
  
  /* allocate a new item */
  length = strlen(str);
  if(item->data != (char *)NULL)
    item->data = (char *)my_realloc(item->data, (length+1)*sizeof(char), _ILIST_BOX_D_);
  else
    item->data = (char *)my_malloc((length+1)*sizeof(char),  _ILIST_BOX_D_);
  if(!item->data) EZ_OutOfMemory("EZ_InsertIListBoxItem");
  (void)strcpy(item->data,str);
  item->length = length;
  
  EZ_IListBoxNLines(widget) += 1;

  if(EZ_WidgetMapped(widget))
    {
      EZ_DrawWidget(widget); 
      update_il_scrollbars(widget);
    }
}
/**************************************************************************/

void  EZ_SetIListBoxData(widget, items, nitems)
     EZ_Widget *widget;
     char      **items;
     int       nitems;
{
  int          i;

  if(widget == (EZ_Widget *)NULL || EZ_WidgetType(widget) != EZ_WIDGET_ILIST_BOX)
    return;
  if(nitems > 0 &&  items != (char **)NULL)
    {
      EZ_FreezeWidget(widget);
      EZ_ClearIListBox(widget);

      for(i = 0; i < nitems && items[i]; i++)
	EZ_InsertIListBoxItem(widget, items[i], -1);
      EZ_UnFreezeWidget(widget);
      if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget); 
    }

}
/******************************************************************************/
void  EZ_ClearIListBox(widget)
     EZ_Widget *widget;
{
  if(widget == (EZ_Widget *)NULL || EZ_WidgetType(widget) != EZ_WIDGET_ILIST_BOX)
    return;
  /*
   * reset listbox items to nothing.
   */
  EZ_IListBoxNLines(widget) = 0;
  EZ_IListBoxFirstLine(widget) = 0;
  EZ_IListBoxMaxHSize(widget) = 0;
  EZ_IListBoxHStart(widget) = 0;
  EZ_IListBoxSLine(widget) = -1;
  EZ_IListBoxSLineEnd(widget) = -1;
  if(EZ_WidgetMapped(widget)) EZ_DrawWidget(widget);  
}

/*************************************************************************/
char  *EZ_GetIListBoxSelectedItem(widget)
     EZ_Widget *widget;
{
  if(widget != (EZ_Widget *)NULL && EZ_WidgetType(widget) == EZ_WIDGET_ILIST_BOX)
    {
      int tmp = EZ_IListBoxSLine(widget);
      if(tmp >= 0) return(EZ_IListBoxData(widget)[tmp].data);
    }
  return( (char *)NULL);
}

void  EZ_DeleteIListBoxItem(widget, idx)
     EZ_Widget *widget; int idx;
{
  if(widget != (EZ_Widget *)NULL && EZ_WidgetType(widget) == EZ_WIDGET_ILIST_BOX)
    {
      if(idx >= 0 && idx < EZ_IListBoxNLines(widget))
	{
	  int i;
	  listboxentry *data =  EZ_IListBoxData(widget);
	  (void)my_free( (char *)data[idx].data);
	  for(i = idx; i < EZ_IListBoxNLines(widget) -1; i++)
	    {
	      data[i].data = data[i+1].data;
	      data[i].length = data[i+1].length;
	    }
	  data[i].data = NULL;
	  data[i].length = 0;
	  EZ_IListBoxNLines(widget) -= 1;
	  if(idx == EZ_IListBoxSLine(widget) ||
	     idx == EZ_IListBoxSLineEnd(widget))
	    EZ_IListBoxSLineEnd(widget) = EZ_IListBoxSLine(widget) = -1;
	  else if(idx < EZ_IListBoxSLine(widget) && EZ_IListBoxSLine(widget) > 0)
	    {
	      EZ_IListBoxSLineEnd(widget) -= 1;
	      EZ_IListBoxSLine(widget) -= 1;
	    }
	  if(EZ_WidgetMapped(widget))
	    EZ_DrawWidget(widget);
	}
    }
}
void  EZ_DeleteIListBoxString(widget, str)
     EZ_Widget *widget; char *str;
{
  if(widget != (EZ_Widget *)NULL && EZ_WidgetType(widget) == EZ_WIDGET_ILIST_BOX && str)
    {
      int i, nlines = EZ_IListBoxNLines(widget);
      listboxentry *data =  EZ_IListBoxData(widget);
      for(i = 0; i < nlines; i++)
	{
	  if(!strcmp(data[i].data, str))
	    {
	      EZ_DeleteIListBoxItem(widget, i);
	      break;
	    }
	}
    }
}
/*************************************************************************/
void EZ_GetIListBoxData(widget, data_ret, count_ret)
     EZ_Widget *widget;
     listboxentry **data_ret;
     int *count_ret;
{
  if(widget && EZ_WidgetType(widget) == EZ_WIDGET_ILIST_BOX)
    {
      *data_ret = EZ_IListBoxData(widget);
      *count_ret = (int) EZ_IListBoxNLines(widget);
      return;
    }
  *data_ret = NULL;
  *count_ret = 0;
}
/*************************************************************************/
extern ITextLine TextWidgetKillBuffer;
/*************************************************************************/
char *EZ_GetIListBoxSelection(widget)
     EZ_Widget *widget;
{
  if(widget != (EZ_Widget *)NULL && EZ_WidgetType(widget) == EZ_WIDGET_ILIST_BOX)
    {
      int bgn = EZ_IListBoxSLine(widget);
      if(bgn >= 0)
	{
	  int end = EZ_IListBoxSLineEnd(widget);
	  int length, i;
	  if(end < bgn) end = bgn;
	  length = 0;
	  EZ_GrowITextKillBuffer(&TextWidgetKillBuffer,length+8);
	  (TextWidgetKillBuffer.data)[length] = 0;
	  for(i = bgn; i <= end; i++)
	    {
	      char *str = EZ_IListBoxData(widget)[i].data;
	      if(str)
		{
		  int len = strlen(str);
		  if(i < end)   length += len + 1;
		  else   length += len;
		  EZ_GrowITextKillBuffer(&TextWidgetKillBuffer,length+8);
		  (void) strncat(TextWidgetKillBuffer.data, str, len);
		  if(i < end) (void) strcat(TextWidgetKillBuffer.data, "\n");
		}
	    }
	  (TextWidgetKillBuffer.data)[length] = 0;
	  TextWidgetKillBuffer.length = length;
	  TextWidgetKillBuffer.width = 0;
	  return((TextWidgetKillBuffer.data));
	}
    }
  return(NULL);
}
/*************************************************************************/

static void  EZ_IListBoxSetSelection(widget)
     EZ_Widget *widget;
{
  int bgn = EZ_IListBoxSLine(widget);
  if(bgn >= 0)
    {
      int end = EZ_IListBoxSLineEnd(widget);
      int length, i;
      if(end < bgn) end = bgn;
      length = 0;
      EZ_GrowITextKillBuffer(&TextWidgetKillBuffer,length+8);
      (TextWidgetKillBuffer.data)[length] = 0;
      for(i = bgn; i <= end; i++)
	{
	  char *str = EZ_IListBoxData(widget)[i].data;
	  if(str)
	    {
	      int len = strlen(str);
	      if(i < end)   length += len + 1;
	      else   length += len;
	      EZ_GrowITextKillBuffer(&TextWidgetKillBuffer,length+8);
	      (void) strncat(TextWidgetKillBuffer.data, str, len);
	      if(i < end) (void) strcat(TextWidgetKillBuffer.data, "\n");
	    }
	}
      (TextWidgetKillBuffer.data)[length] = 0;
      TextWidgetKillBuffer.length = length;
      TextWidgetKillBuffer.width = 0;
      EZ_SetSelectionOwner(widget);
    }
}
/*************************************************************************/
/* contributed by  Antti H{yrynen <hydra@pato.vaala.fi> */
void  EZ_ModifyIListBoxItem(widget, str, where)
     EZ_Widget *widget;
     char      *str; 
     int       where;
{
  int space, nlines, length;
  listboxentry *item;

  if(widget == (EZ_Widget *)NULL || EZ_WidgetType(widget) != EZ_WIDGET_ILIST_BOX)
    return;  
  if(str == (char *)NULL) return;
  
  space = EZ_IListBoxSpace(widget);
  nlines = EZ_IListBoxNLines(widget);

  if (where > nlines-1) return ;
  item = EZ_IListBoxData(widget) + where;

  /* allocate a new item */
  length = strlen(str);
  if(item->data != (char *)NULL)
    item->data = (char *)my_realloc(item->data, (length+1)*sizeof(char), _ILIST_BOX_D_);
  else
    item->data = (char *)my_malloc((length+1)*sizeof(char),  _ILIST_BOX_D_);
  (void)strcpy(item->data,str);
  item->length = length;
  
  if(EZ_WidgetMapped(widget))
    {
      if(where <= EZ_IListBoxFirstLine(widget) + EZ_IListBoxPageSize(widget) &&
         where >= EZ_IListBoxFirstLine(widget))
	EZ_DrawWidget(widget); 
      update_il_scrollbars(widget);
    }
}
/**************************************************************************/
void  EZ_IListBoxSelectItem(widget, str)
     EZ_Widget *widget;
     char      *str; 
{
  int i, nlines;
  listboxentry *data, *item;

  if( widget == (EZ_Widget *)NULL || EZ_WidgetType(widget) != EZ_WIDGET_ILIST_BOX)
    return;  

  nlines = EZ_IListBoxNLines(widget);
  if(nlines <= 0) return;

  data = EZ_IListBoxData(widget);
  if(str == (char *)NULL) i = nlines + 2;
  else
    {
      for(i = 0; i < nlines; i++)
	{
	  item = data + i;
	  if(!strcmp(str, item->data)) break;
	}
    }
  if(i < nlines)
    {
      int osline =  EZ_IListBoxSLine(widget);
      if(i != osline)
	{
	  int fline = EZ_IListBoxFirstLine(widget);
	  int lline = EZ_IListBoxLastLine(widget);
	  
	  if(i > lline)
	    {
	      int  tmp = i - lline;
	      EZ_IListBoxFirstLine(widget) += tmp;
	      EZ_IListBoxLastLine(widget) += tmp;
	    }
	  else  if(i < fline) 
	    {
	      int tmp = fline - i;
	      EZ_IListBoxFirstLine(widget) -= tmp;
	      EZ_IListBoxLastLine(widget) -= tmp;
	    }
	  EZ_IListBoxSLine(widget) = i;
	  EZ_IListBoxSLineEnd(widget) = i;
	  /* EZ_IListBoxSetSelection(widget);*/
	  EZ_DrawWidget(widget);    
	  EZ_HandleMotionCallBacks(widget,EZ_IListBoxMotionCallBack(widget));   
	}
    }
  else if(i > nlines)
    {
      int osline =  EZ_IListBoxSLine(widget); 
      EZ_IListBoxSLine(widget) = -1;
      EZ_IListBoxSLineEnd(widget) = -1;
      if(osline != -1 && EZ_WidgetMapped(widget))   EZ_DrawWidget(widget);    
    }
}

void  EZ_IListBoxSelectItemUsingIdx(widget, idx)
     EZ_Widget *widget;
     int idx;
{
  int nlines;

  if( widget == (EZ_Widget *)NULL || EZ_WidgetType(widget) != EZ_WIDGET_ILIST_BOX)
    return;  

  nlines = EZ_IListBoxNLines(widget);
  
  if(idx < 0 || idx >= nlines) 
    {
      int osline =  EZ_IListBoxSLine(widget); 
      EZ_IListBoxSLine(widget) = -1;
      EZ_IListBoxSLineEnd(widget) = -1;
      if(osline != -1 && EZ_WidgetMapped(widget))   EZ_DrawWidget(widget);    
      return;
    }

  {
    int osline =  EZ_IListBoxSLine(widget);
    int i = idx;
    if(i != osline)
      {
	int fline = EZ_IListBoxFirstLine(widget);
	int lline = EZ_IListBoxLastLine(widget);
	  
	if(i > lline)
	  {
	    int  tmp = i - lline;
	    EZ_IListBoxFirstLine(widget) += tmp;
	    EZ_IListBoxLastLine(widget) += tmp;
	  }
	else  if(i < fline) 
	  {
	    int tmp = fline - i;
	    EZ_IListBoxFirstLine(widget) -= tmp;
	    EZ_IListBoxLastLine(widget) -= tmp;
	  }
	EZ_IListBoxSLine(widget) = i;
	EZ_IListBoxSLineEnd(widget) = i;
	if(EZ_WidgetMapped(widget))
	  {
	    /* EZ_IListBoxSetSelection(widget);*/
	    EZ_DrawWidget(widget);    
	    EZ_HandleMotionCallBacks(widget,EZ_IListBoxMotionCallBack(widget));
	  }
      }
  }
}
/*************************************************************************/
#undef _EZ_WIDGET_ILIST_BOX_C_
