/*********************************************************************
 *
 *         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 Optional Entry                        ***
 ***                                                           ***
 ***                                                           ***
 *****************************************************************/
#define _EZ_WIDGET_OPT_INPUT_C_

#include "EZ_Widget.h"

/*********************************************************************
 * 
 *  Functions implemented in this file:
 */
EZ_Widget        *EZ_CreateOptionalEntry MY_ANSIARGS((EZ_Widget *parent, char *str, int hist, int editable));
void              EZ_SetOptionalEntryString MY_ANSIARGS((EZ_Widget *widget, char *str));
char             *EZ_GetOptionalEntryString MY_ANSIARGS((EZ_Widget *widget));
void              EZ_GetOptionalEntryWidgetComponents MY_ANSIARGS((EZ_Widget *widget, 
							     EZ_Widget **entry, EZ_Widget **listbox, 
							     EZ_Widget **btn ));
void  EZ_OptionalEntryClearOptions     MY_ANSIARGS((EZ_Widget *widget));
void  EZ_SetOptionalEntryOptionalItems MY_ANSIARGS((EZ_Widget *widget, char **items, int count));
void  EZ_OptionalEntryAppendOption     MY_ANSIARGS((EZ_Widget *widget, char *item));
void  EZ_OptionalEntryInsertListOption MY_ANSIARGS((EZ_Widget *widget, char *str, int idx));
void  EZ_OptionalEntryDeleteOption     MY_ANSIARGS((EZ_Widget *widget, char *str));

/*********************************************************************
 * 
 * Local functions.
 */
static void EZ_OptEntryBtnEventHandle MY_ANSIARGS((void *widget,XEvent *event));
static void EZ_OptEntryBtnRedraw MY_ANSIARGS((void *widget));
static void EZ_OptEntryListboxMotionCallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void EZ_OptEntryListboxDone MY_ANSIARGS((EZ_Widget *widget, void *data));
static void EZ_OptEntryEntryCallBack MY_ANSIARGS((EZ_Widget *widget, void *data));

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

EZ_Widget  *EZ_CreateOptionalEntry(parent, init_str, remhist, editable)
     EZ_Widget  *parent;     /* parent widget    */
     char       *init_str;
     int        remhist;
     int        editable;
{
  EZ_Widget  *frame, *btn, *listbox, *entry, *icon;

  frame = EZ_CreateNewWidget(parent);
  /* use the default widget handle */
  
  EZ_SetWidgetTypeAndNames(frame,EZ_WIDGET_OPTIONAL_ENTRY);
  EZ_WidgetPadX(frame) = 0;
  EZ_WidgetPadY(frame) = 0;
  EZ_WidgetSepX(frame) = 2;
  EZ_WidgetSepY(frame) = 0;
  EZ_WidgetBorderWidth(frame) = 0;
  EZ_WidgetStacking(frame) = EZ_HORIZONTAL;
  EZ_WidgetFillMode(frame) = EZ_FILL_HORIZONTALLY;
  
  entry = EZ_CreateEntry(frame, init_str);
  EZ_WidgetPadX(entry) = 2;
  EZ_WidgetPadY(entry) = 0;
  EZ_EntryLineLength(entry) = 15;
  EZ_EntryRemberHistory(entry) = (remhist != 0 ? 1 : 0);
  if(editable == 0) EZ_EntryEditable(entry) = 0;
  
  btn = EZ_CreateSpecialWidget(frame, EZ_DOWN_TRIANGLE_BAR);
  EZ_ClearWidgetFocusableFlag(btn);
  EZ_WidgetBorderWidth(btn) = 2;
  EZ_WidgetPadB(btn) = 0;
  EZ_WidgetBorderStyle(btn) = EZ_BORDER_UP;
  EZ_WidgetPadX(btn) = 2;
  EZ_WidgetPadY(btn) = 2;
  EZ_SetWidgetWidthSetFlag(btn);
  EZ_SpecialWidgetEventHandle(btn) = EZ_OptEntryBtnEventHandle;
  EZ_SpecialWidgetRedrawFunc(btn) = EZ_OptEntryBtnRedraw;
  EZ_SetWidgetCINames(btn, "Button",  "arrowButton");

  icon = EZ_CreateIcon();
  EZ_SetWidgetCINames(icon, "Popup", "popup");
  EZ_LabelMovable(icon) = 0; 
  EZ_WidgetPadX(icon) = 3;
  EZ_WidgetPadY(icon) = 3;
  EZ_WidgetFillMode(icon) = EZ_FILL_BOTH;
  EZ_WidgetBorderWidth(icon) = 2;
  EZ_WidgetBorderStyle(icon) = EZ_BORDER_UP;
  EZ_LabelDontTakeFocus(icon) = 1;

  listbox = EZ_CreateListBox(icon,1,1);
  EZ_ListBoxCallBackOnRelease(listbox) = 1;
  EZ_AddWidgetCallBackInternal(listbox, EZ_MOTION_CALLBACK,
			       (EZ_CallBack) EZ_OptEntryListboxMotionCallBack, 
			       frame, 0);
  EZ_AddWidgetCallBackInternal(listbox, EZ_CALLBACK, (EZ_CallBack)EZ_OptEntryListboxDone, listbox,0);

  EZ_AddWidgetCallBackInternal(entry,  EZ_CALLBACK, (EZ_CallBack)EZ_OptEntryEntryCallBack, 
			       EZ_ListBoxIListBox(listbox), 0);

  /*
   * link widgets together
   */
  EZ_OptEntryEntry(frame) = entry;
  EZ_OptEntryButton(frame) = btn;
  EZ_OptEntryListbox(frame) = listbox;
  EZ_OptEntryPopup(frame) = icon;

  EZ_EntryOptEntry(entry) = frame;
  EZ_ListBoxOptEntry(listbox) = frame;
  EZ_SpecialWidgetPI(btn,0) = frame;

  return(frame);
}
/*************************************************************/
static void EZ_OptEntryListboxDone(confused, data)
     EZ_Widget *confused;  /* data is a LISTBOX */
     void *data;
{
  EZ_Widget *widget = (EZ_Widget *) data;

  EZ_Widget *optEntry = EZ_ListBoxOptEntry(widget);
  EZ_Widget *entry = EZ_OptEntryEntry(optEntry);
  EZ_Widget *parent = EZ_OptEntryPopup(optEntry);
  EZ_HideWidget(parent);

  /* remove highlighting on entry */
  EZ_ClearWidgetHighlightFlag(entry);
  EZ_DrawWidget(entry);	  
  /* call the entry call back */
  if(EZ_IListBoxSLine(widget) >= 0)
    {
       EZ_ExecuteWidgetCallBacks(entry);
       EZ_ExecuteWidgetCallBacks(optEntry);
     }
}
/*************************************************************/
static void EZ_OptEntryBtnRedraw(vwptr)
     void *vwptr;
{
  int             w, h,x,y, ww,hh;
  Pixmap          pixmap;
  Window          win;
  GC              gc;
  int             offset = 0;
  unsigned long   bgpv;
  EZ_Widget       *wptr = (EZ_Widget *)vwptr;

  win = EZ_WidgetWindow(wptr);
  w   = EZ_WidgetWidth(wptr);
  h   = EZ_WidgetHeight(wptr);
  
  /*-----------------------------------------------------------
   *  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_GetBackgroundGC(wptr, &gc,&bgpv,1,0);
  XFillRectangle(EZ_DisplayForWidgets, pixmap, gc, 0,0, w, h); 

  /*--------------------------------------------------------
   *  Draw the label.
   *-------------------------------------------------------*/

  if(EZ_OffsetEnabled && EZ_WidgetBorderStyle(wptr) == EZ_BORDER_DOWN)   offset = 1;
  
  x =  EZ_WidgetPadX(wptr) + EZ_WidgetBorderWidth(wptr) + offset;
  y =  EZ_WidgetBorderWidth(wptr) + EZ_WidgetPadY(wptr) + offset;
  
  ww = w - (x + x);
  hh = h - (y + y);

  EZ_DrawSpecial(wptr,pixmap, NULL, EZ_DOWN_ARROW_BAR, x,y, ww, hh);

  EZ_DrawRectBorder(wptr, pixmap);
  XCopyArea(EZ_DisplayForWidgets,pixmap,win, EZ_WRITABLEGC,0,0,w,h,0,0);  
  XFreePixmap(EZ_DisplayForWidgets, pixmap);                               
}
/*************************************************************/
static void EZ_OptEntryListboxMotionCallBack(widget, data)
     EZ_Widget *widget;
     void *data;
{
  char *item = EZ_GetListBoxSelectedItem(widget);
  if(item)
    {
      EZ_Widget *optEntry = /*EZ_IListBoxOptEntry(widget); */ (EZ_Widget *)data;
      EZ_Widget *entry = EZ_OptEntryEntry(optEntry);
      EZ_SetEntryString(entry, item);
    }
}
/*************************************************************/
static void EZ_OptEntryEntryCallBack(widget, data)
     EZ_Widget *widget;
     void *data;
{
  if(widget && data)
    {
      char    *str = EZ_GetEntryString(widget);
      if(str && *str ) /* ignore NULL */
	{
	  listboxentry *item;
	  int     i, insert = 1,  nitems;
	  EZ_Widget *ilistbox = (EZ_Widget *)data;
	  EZ_GetIListBoxData(ilistbox, &item, &nitems);
	  for(i = 0; i < nitems; i++)
	    {
	      if(!strcmp(str, (item +i)->data)) 
		{
		  int k, len;
		  char *data;
		  listboxentry *tmp; 
		  insert = 0; 
		  tmp = item + i;
		  len = tmp->length;
		  data = tmp->data;
		  for(k = i; k > 0; k--)
		    {
		      item[k].data = item[k-1].data;
		      item[k].length = item[k-1].length;
		    }
		  item[0].data = data;
		  item[0].length = len;
		  break;
		}
	    }
	  if(insert && EZ_EntryRemberHistory(widget))
	    EZ_InsertIListBoxItem(ilistbox, str, 0);
	  EZ_IListBoxSLine(ilistbox) = EZ_IListBoxFirstLine(ilistbox) = 0;  
	  EZ_IListBoxSLineEnd(ilistbox) = 0;
	}
    }  
}
/*************************************************************
 *
 * This is really crazy.
 */
static void EZ_OptEntryBtnEventHandle(vwidget, event)
     void *vwidget;
     XEvent    *event;
{
  EZ_Widget *widget = (EZ_Widget *)vwidget;
  EZ_Widget *popup;
  if(widget == (EZ_Widget *)NULL) return;

  if(event->type == Expose) EZ_DrawWidget(widget);      
  if(event->type == ButtonPress && event->xbutton.button != EZ_Btn1)
    return;

  switch(event->type)
    {
    case EnterNotify:
      EZ_SetWidgetHighlightFlag(widget);
      EZ_DrawWidget(widget);
      break;
    case LeaveNotify:
      EZ_ClearWidgetHighlightFlag(widget);
      EZ_DrawWidget(widget);
      break;
    case KeyPress:
      {
	char              tmpbuffer[8];
	int               buffersize = 8;
	KeySym            keysym;
	XComposeStatus    compose; 
	int count = XLookupString(&(event->xkey), tmpbuffer, buffersize, &keysym, &compose);
	tmpbuffer[count] = '\0';
	if(keysym != XK_Down && keysym != XK_Up  
#ifdef XK_KP_Down
	   && keysym != XK_KP_Down && keysym != XK_KP_Up
#endif
	   )
	  break;
      }
    case ButtonPress:
      {
	int          rx,ry,x,y;
	unsigned int mask;
	Window       root,win;
	Window          w1,w2,w3, w4;
	EZ_Widget       *tmp, *tmp1, *listbox, *entry;
	EZ_Widget *optEntry = (EZ_Widget *)EZ_SpecialWidgetPI(widget,0);
	EZ_Widget *parent = optEntry;
	popup = EZ_OptEntryPopup(optEntry);

	/*-----------------------------------------------------------
	 * first display the  popup listbox.
	 *----------------------------------------------------------*/
	XQueryPointer(EZ_Display, EZ_WidgetWindow(parent),
		      &root,                                    /* root return win */
		      &win,                                     /* child ret win   */
		      &rx, &ry,                                 /* x, y in root    */
		      &x,&y,                                    /* x, y in win     */
		      &mask
		      );
	rx -= x;
	ry += (EZ_WidgetHeight(parent) -y);
	
	EZ_ClearWidgetSizeComputedFlag(popup);
	EZ_WidgetOriginX(popup) = rx;
	EZ_WidgetOriginY(popup) = ry;
	EZ_SetWidgetXYSetFlag(popup);
	EZ_WidgetWidthHint(popup) = EZ_WidgetWidth(popup) = EZ_WidgetWidth(parent) +36;
	EZ_SetWidgetWidthSetFlag(popup);
	if(EZ_WidgetBackground(widget)) EZ_WidgetParentBG(popup) = &(EZ_WidgetBackground(widget));
	else EZ_WidgetParentBG(popup) = EZ_WidgetParentBG(widget);
	
	EZ_WidgetHeightHint(popup) = EZ_WidgetHeight(popup) = 200;
	EZ_SetWidgetHeightSetFlag(popup);
	EZ_DisplayWidget(popup);

	tmp = EZ_OptEntryListbox(optEntry);  /* the listbox */
	listbox = EZ_ListBoxIListBox(tmp); /* the ilistbox */
	EZ_IListBoxTakeMotion(listbox) = 1;
	w1 = EZ_WidgetWindow(listbox);
	tmp1 = EZ_ListBoxHScroll(tmp);
	w2 = EZ_WidgetWindow(tmp1);
	tmp1 = EZ_ListBoxVScroll(tmp);
	w3 = EZ_WidgetWindow(tmp1);	
	entry = EZ_OptEntryEntry(optEntry); /* the entry */ 
	w4 = EZ_WidgetWindow(entry);	
	
	/* and grab the keyboard */
	XGrabPointer(EZ_Display,
		     EZ_WidgetWindow(popup),
		     True,
		     ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|PointerMotionMask,
		     GrabModeAsync,
		     GrabModeAsync, 
		     None,
		     EZ_GetCursor(EZ_C_RIGHT_PTR),
		     CurrentTime);

	/* grab the keyboard */
	EZ_SetFocusTo(entry);
	EZ_SetWidgetHighlightFlag(entry);
	EZ_DrawWidget(entry);	  

	/*
	 * Now wait until a selection is made or ...
	 */
	{
	  XEvent          xevent;
	  Window          event_window;
	  int             done = 0;

	  while(1)
	    {
	      EZ_TFEvents();
	      XNextEvent(EZ_Display, &xevent);
	      EZ_FilterEvent(&xevent);
	      event_window = xevent.xany.window;
	      switch(xevent.type)	      
		{
		case EnterNotify:
		case LeaveNotify:
		case MotionNotify:
		case Expose:
		case FocusIn:
		case FocusOut:
		  tmp = EZ_LookupWidgetFromMappedHT(event_window); 
		  EZ_HandleWidgetWindowEvent(tmp, &xevent);
		  break; 
		case ButtonPress:
		  if(event_window == EZ_WidgetWindow(widget))
		    {
		      XUngrabPointer(EZ_Display, CurrentTime); 
		      XUngrabKeyboard(EZ_Display,CurrentTime); 	
		      done = 1;
		    }
		  else if(event_window == w1 || event_window == w2 || 
			  event_window == w3 || event_window == w4)
		    {
		      tmp = EZ_LookupWidgetFromMappedHT(event_window); 
		      EZ_HandleWidgetWindowEvent(tmp, &xevent);
		    }
		  else done = 1;
		  break;
		case KeyPress:
		case ButtonRelease:
		  EZ_IListBoxTakeMotion(listbox) = 0;
		  if(event_window == w4 || event_window == w2 || event_window == w3 ||
		     event_window == w1)
		    {
		      tmp = EZ_LookupWidgetFromMappedHT(event_window); 
		      EZ_HandleWidgetWindowEvent(tmp, &xevent);
		    }
		  else if(event_window != EZ_WidgetWindow(widget)) done  = 1;
		default:
		  break;
		}
	      if(done || EZ_WidgetMapped(popup) == 0) 
		{ 
		  EZ_HideWidget(popup); 
		  XUngrabPointer(EZ_Display, CurrentTime); 
		  EZ_ClearWidgetHighlightFlag(entry);
		  EZ_DrawWidget(entry);		  
		  break;
		}
	    }
	  EZ_IListBoxTakeMotion(listbox) = 0;
	}
	break;
      default:
	break;
      }
    }
}
/**************************************************************************************/
void   EZ_SetOptionalEntryString(widget, str)
     EZ_Widget *widget;
     char      *str;
{
  if(widget && EZ_WidgetType(widget) == EZ_WIDGET_OPTIONAL_ENTRY)
    {
      EZ_Widget *entry = EZ_OptEntryEntry(widget);
      EZ_Widget *listbox = EZ_OptEntryListbox(widget);
      EZ_SetEntryString(entry, str);
      EZ_OptEntryEntryCallBack(entry, EZ_ListBoxIListBox(listbox));
    }
}
/**************************************************************************************/
char *EZ_GetOptionalEntryString(widget)
     EZ_Widget *widget;
{
  return (EZ_GetEntryString(widget));
}
/**************************************************************************************/
void EZ_SetOptionalEntryOptionalItems(widget, items, nitems)
     EZ_Widget *widget;
     char      **items;
     int       nitems;
{
  if(widget)
    {
      EZ_Widget *optEntry;
      switch(EZ_WidgetType(widget))
	{
	case EZ_WIDGET_OPTIONAL_ENTRY:
	  optEntry = widget;
	  break;
	case EZ_WIDGET_ENTRY:
	  optEntry = EZ_EntryOptEntry(widget);
	  break;
	default:
	  optEntry = NULL;
	  break;
	}
      if(optEntry)
	{
	  EZ_Widget *listbox = EZ_OptEntryListbox(optEntry);
	  if(listbox)
	    {
	      EZ_Widget *ilistbox = EZ_ListBoxIListBox(listbox);
	      if(items && nitems > 0)
		EZ_SetIListBoxData(ilistbox, items, nitems);
	      else EZ_ClearIListBox(ilistbox);
	    }
	}
    }
}
/**************************************************************************************/
void  EZ_GetOptionalEntryWidgetComponents(widget, entry_ret, listbox_ret, btn_ret)
     EZ_Widget *widget;
     EZ_Widget **entry_ret, **listbox_ret, **btn_ret;
{
  if(widget && EZ_WidgetType(widget) == EZ_WIDGET_OPTIONAL_ENTRY)
    {
      *entry_ret = EZ_OptEntryEntry(widget);
      *listbox_ret = EZ_OptEntryListbox(widget);
      *btn_ret = EZ_OptEntryButton(widget);
    }
}
/**************************************************************************************/

void  EZ_OptionalEntryClearOptions(widget) EZ_Widget *widget;
{ 
  EZ_Widget *optEntry = NULL;
  if(widget)
    {
      switch(EZ_WidgetType(widget))
	{
	case EZ_WIDGET_OPTIONAL_ENTRY:	  optEntry = widget; 	  break;
	case EZ_WIDGET_ENTRY:	  optEntry = EZ_EntryOptEntry(widget);	  break;
	default:        break;
	}
    }
  if(optEntry)
    {
      EZ_Widget *listbox = EZ_OptEntryListbox(optEntry);
      if(listbox)
	{
	  EZ_Widget *ilistbox = EZ_ListBoxIListBox(listbox);
	  EZ_ClearIListBox(ilistbox);
	}
    }
}

void  EZ_OptionalEntryAppendOption(widget,item)
     EZ_Widget *widget; char *item;
{
  EZ_Widget *optEntry = NULL;
  if(widget)
    {
      switch(EZ_WidgetType(widget))
	{
	case EZ_WIDGET_OPTIONAL_ENTRY:	  optEntry = widget; 	  break;
	case EZ_WIDGET_ENTRY:	  optEntry = EZ_EntryOptEntry(widget);	  break;
	default:        break;
	}
    }
  if(optEntry)
    {
      EZ_Widget *listbox = EZ_OptEntryListbox(optEntry);
      if(listbox)
	{
	  EZ_Widget *ilistbox = EZ_ListBoxIListBox(listbox);
	  EZ_AppendIListBoxItem(ilistbox, item);
	}
    }
}

void  EZ_OptionalEntryInsertListOption(widget,item, idx)
     EZ_Widget *widget; char *item; int idx;
{
  EZ_Widget *optEntry = NULL;
  if(widget)
    {
      switch(EZ_WidgetType(widget))
	{
	case EZ_WIDGET_OPTIONAL_ENTRY:	  optEntry = widget; 	  break;
	case EZ_WIDGET_ENTRY:	  optEntry = EZ_EntryOptEntry(widget);	  break;
	default:        break;
	}
    }
  if(optEntry)
    {
      EZ_Widget *listbox = EZ_OptEntryListbox(optEntry);
      if(listbox)
	{
	  EZ_Widget *ilistbox = EZ_ListBoxIListBox(listbox);
	  EZ_InsertIListBoxItem(ilistbox, item, idx);
	}
    }
}
void  EZ_OptionalEntryDeleteOption(widget,item)
     EZ_Widget *widget; char *item;
{
  EZ_Widget *optEntry = NULL;
  if(widget)
    {
      switch(EZ_WidgetType(widget))
	{
	case EZ_WIDGET_OPTIONAL_ENTRY:	  optEntry = widget; 	  break;
	case EZ_WIDGET_ENTRY:	  optEntry = EZ_EntryOptEntry(widget);	  break;
	default:        break;
	}
    }
  if(optEntry)
    {
      EZ_Widget *listbox = EZ_OptEntryListbox(optEntry);
      if(listbox)
	{
	  EZ_Widget *ilistbox = EZ_ListBoxIListBox(listbox);
	  EZ_DeleteIListBoxString(ilistbox, item);
	}
    }
}
/**************************************************************************************/
#undef _EZ_WIDGET_OPT_INPUT_C_
