/*********************************************************************
 *
 *         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 File Selector                         ***
 ***                                                           ***
 *****************************************************************/
#define _EZ_WIDGET_FILE_SELECT_C_

#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include "EZ_Widget.h"

/*********************************************************************
 * 
 *  Functions implemented in this file:
 */
EZ_Widget        *EZ_CreateFileSelector  MY_ANSIARGS((EZ_Widget *parent, char *pattern));
void             EZ_GetFileSelectorWidgetComponents  MY_ANSIARGS((EZ_Widget *widget,
							    EZ_Widget **pe, EZ_Widget **se,
							    EZ_Widget **dl, EZ_Widget **fl,
							    EZ_Widget **ok, EZ_Widget **ft,
							    EZ_Widget **cancel));
void             EZ_SetFileSelectorInitialPattern MY_ANSIARGS((EZ_Widget *widget, char *pattern));
char             *EZ_GetFileSelectorSelection MY_ANSIARGS((EZ_Widget *widget));

int              EZ_ProcessFileSelectorPattern MY_ANSIARGS((char **dirName, char *pattern));
int              EZ_CmpStr MY_ANSIARGS((CONST void *ptr1, CONST void *ptr2));
int              EZ_CountSetFilePatterns MY_ANSIARGS((char *pat, char **patterns));
int              EZ_IsGlobPattern MY_ANSIARGS((char *pat));
int              position_after_last_slash_before_glob_chars MY_ANSIARGS((char *str));
/*********************************************************************
 * 
 * Local functions.
 */
static void CancelCallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void OkCallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void FilterCallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void ListboxAMotionCallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void ListboxBMotionCallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void ListboxACallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void ListboxBCallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void EntryACallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void EntryBCallBack MY_ANSIARGS((EZ_Widget *widget, void *data));
static void EZ_FreeFileSelectData MY_ANSIARGS((EZ_Widget *widget));

static EZ_WidgetHandle EZ_FileSelectHandle =
{
  EZ_ComputeWidgetUnknownSize,
  EZ_DrawWidgetUnknown,
  EZ_FreeFileSelectData,
  EZ_UnkownEventHandle,
};

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

EZ_Widget  *EZ_CreateFileSelector(parent, ini_pattern)
     EZ_Widget  *parent;     /* parent widget    */
     char       *ini_pattern;
{
  EZ_Widget  *frame, *frameA, *frameB,*frameB1, *frameB2,*frameC, *frameD;
  EZ_Widget  *entryA, *entryB, *label, *tmp;
  EZ_Widget  *listboxA1, *listboxA2;
  EZ_Widget  *ok, *filter, *cancel;
  
  frame = EZ_CreateNewWidget(parent);
  EZ_WidgetHandlingFunctions[EZ_WIDGET_FILE_SELECTOR] = &EZ_FileSelectHandle;

  EZ_SetWidgetTypeAndNames(frame,EZ_WIDGET_FILE_SELECTOR);
  EZ_FileSelectorCurrentDirectory(frame) = (char *)NULL;
  EZ_FileSelectorCurrentSelection(frame) = (char **)NULL;

  EZ_WidgetStacking(frame) = EZ_VERTICAL;
  EZ_WidgetFillMode(frame) = EZ_FILL_BOTH;
  EZ_WidgetWidthHint(frame) = 300;
  EZ_WidgetHeightHint(frame) = 400;
  EZ_SetWidgetWidthHintFlag(frame);
  EZ_SetWidgetHeightHintFlag(frame);
  EZ_WidgetPadX(frame) = 2;
  EZ_WidgetPadY(frame) = 4;
  EZ_WidgetSepY(frame) = 4;

  frameA = EZ_CreateFrame(frame, NULL);
  EZ_WidgetStacking(frameA) = EZ_VERTICAL;
  EZ_WidgetFillMode(frameA) = EZ_FILL_HORIZONTALLY;
  EZ_WidgetHeight(frameA) = 0;    /* will not strectched in the V dir */
  EZ_SetWidgetHeightSetFlag(frameA);
  EZ_WidgetPadX(frameA) = 0;
  EZ_WidgetPadY(frameA) = 3;
  EZ_WidgetSepY(frameA) = 3;
  EZ_SetWidgetCINames(frameA, NULL, "frameA");

  label = EZ_CreateLabel(frameA, "Filter:");
  EZ_WidgetLabelPosition(label) = EZ_LEFT;
  EZ_WidgetPadY(label) = 0;
  EZ_WidgetPadX(label) = 0;
  EZ_SetWidgetCINames(label, NULL, "filterLabel");

  entryA = EZ_CreateOptionalEntry(frameA, ini_pattern == NULL ? "*" : ini_pattern, 1, 1);
  EZ_SetWidgetCINames(entryA, NULL, "filterEntry");
  {
    EZ_Widget *tmp = EZ_OptEntryPopup(entryA);
    EZ_SetWidgetCINames(tmp, NULL, "popupA");
  }

  frameB = EZ_CreateFrame(frame, NULL);
  EZ_WidgetFillMode(frameB) = EZ_FILL_BOTH;
  EZ_WidgetPadX(frameB) = 0;
  EZ_WidgetPadY(frameB) = 0;
  EZ_WidgetSepX(frameB) = 3;
  EZ_WidgetSepY(frameB) = 0;
  EZ_SetWidgetCINames(frameB, NULL, "frameB");
  
  frameB1 = EZ_CreateFrame(frameB, NULL);
  EZ_WidgetStacking(frameB1) = EZ_VERTICAL;
  EZ_WidgetFillMode(frameB1) = EZ_FILL_BOTH;
  EZ_WidgetPadX(frameB1) = 0;
  EZ_WidgetPadY(frameB1) = 0;
  EZ_WidgetSepX(frameB1) = 0;
  EZ_WidgetSepY(frameB1) = 3;
  EZ_SetWidgetCINames(frameB1, NULL, "frameB1");

  tmp = EZ_CreatePaneHandle(frameB);
  frameB2 = EZ_CreateFrame(frameB, NULL);
  EZ_WidgetStacking(frameB2) = EZ_VERTICAL;
  EZ_WidgetFillMode(frameB2) = EZ_FILL_BOTH;
  EZ_WidgetPadX(frameB2) = 0;
  EZ_WidgetPadY(frameB2) = 0;
  EZ_WidgetSepX(frameB2) = 0;
  EZ_WidgetSepY(frameB2) = 3;

  label = EZ_CreateLabel(frameB1, "Directories:");
  EZ_WidgetLabelPosition(label) = EZ_LEFT;
  EZ_WidgetHeight(label) = 0;    /* will not strectched in the V dir */
  EZ_SetWidgetHeightSetFlag(label);
  EZ_WidgetPadX(label) = 0;
  EZ_WidgetPadY(label) = 0;
  EZ_SetWidgetCINames(label, NULL, "directoryLabel");

  listboxA1 = EZ_CreateListBox(frameB1, 1, 1);
  EZ_SetWidgetCINames(listboxA1, NULL, "directoryListBox");

  label = EZ_CreateLabel(frameB2, "Files:");
  EZ_WidgetLabelPosition(label) = EZ_LEFT;
  EZ_WidgetHeight(label) = 0;    /* will not strectched in the V dir */
  EZ_SetWidgetHeightSetFlag(label);
  EZ_WidgetPadY(label) = 0;
  EZ_SetWidgetCINames(label, NULL, "fileLabel");

  listboxA2 = EZ_CreateListBox(frameB2, 1, 1);
  EZ_SetWidgetCINames(listboxA2, NULL, "fileListBox");
  
  frameC = EZ_CreateFrame(frame, NULL);  
  EZ_WidgetStacking(frameC) = EZ_VERTICAL;
  EZ_WidgetFillMode(frameC) = EZ_FILL_HORIZONTALLY;
  EZ_WidgetPadX(frameC) = 0;
  EZ_WidgetSepY(frameC) = 3;
  EZ_WidgetHeight(frameC) = 0;    /* will not strectched in the V dir */
  EZ_SetWidgetHeightSetFlag(frameC);
  EZ_SetWidgetCINames(frameC, NULL, "frameC");

  label = EZ_CreateLabel(frameC, "Selection:");
  EZ_WidgetLabelPosition(label) = EZ_LEFT;
  EZ_WidgetPadY(label) = 0;
  EZ_WidgetPadX(label) = 0;
  EZ_SetWidgetCINames(label, NULL, "selectionLabel");

  entryB = EZ_CreateOptionalEntry(frameC, NULL, 1, 1);
  EZ_SetWidgetCINames(entryB, NULL, "selectionEntry");
  {
    EZ_Widget *tmp = EZ_OptEntryPopup(entryB);
    EZ_SetWidgetCINames(tmp, NULL, "popupB");
  }

  frameD =  EZ_CreateFrame(frame, NULL);
  EZ_WidgetFillMode(frameD) = EZ_FILL_HORIZONTALLY;
  EZ_WidgetSepX(frameD) = 10;
  EZ_WidgetHeight(frameD) = 0;    /* will not strectched in the V dir */
  EZ_SetWidgetHeightSetFlag(frameD);
  EZ_SetWidgetCINames(frameD, NULL, "frameD");

  ok = EZ_CreateButton(frameD, "Ok", 0);
  EZ_SetWidgetCINames(ok, NULL, "okButton");
  filter = EZ_CreateButton(frameD, "Filter", 0);  
  EZ_SetWidgetCINames(filter, NULL, "filterButton");
  cancel = EZ_CreateButton(frameD, "Hide", 0);  
  EZ_SetWidgetCINames(cancel, NULL, "hideButton");

  EZ_ButtonFSB(ok)     = frame;
  EZ_ButtonFSB(cancel) = frame;
  EZ_ButtonFSB(filter) = frame;
  EZ_AddWidgetCallBackInternal(ok,  EZ_CALLBACK, (EZ_CallBack)OkCallBack, NULL,0);
  EZ_AddWidgetCallBackInternal(cancel, EZ_CALLBACK, (EZ_CallBack)CancelCallBack,NULL,0);
  EZ_AddWidgetCallBackInternal(filter, EZ_CALLBACK, (EZ_CallBack)FilterCallBack,NULL,0);
  {
    EZ_Widget *rentryA, *rentryB;
    EZ_Widget *ilistboxA1, *ilistboxA2;

    rentryA =  EZ_OptEntryEntry(entryA);
    rentryB =  EZ_OptEntryEntry(entryB);
    ilistboxA1 = EZ_ListBoxIListBox(listboxA1);
    ilistboxA2 = EZ_ListBoxIListBox(listboxA2);
    
    EZ_AddWidgetCallBackInternal(ilistboxA1, EZ_MOTION_CALLBACK,(EZ_CallBack)ListboxAMotionCallBack,NULL,0);
    EZ_AddWidgetCallBackInternal(ilistboxA2, EZ_MOTION_CALLBACK,(EZ_CallBack)ListboxBMotionCallBack,NULL,0);
    EZ_AddWidgetCallBackInternal(ilistboxA1, EZ_CALLBACK, (EZ_CallBack) ListboxACallBack, NULL,0);
    EZ_AddWidgetCallBackInternal(ilistboxA2, EZ_CALLBACK, (EZ_CallBack) ListboxBCallBack, NULL,0);
    EZ_AddWidgetCallBackInternal(rentryA,  EZ_CALLBACK,(EZ_CallBack) EntryACallBack, NULL,0);
    EZ_AddWidgetCallBackInternal(rentryB,  EZ_CALLBACK,(EZ_CallBack) EntryBCallBack, NULL,0);

    EZ_FileSelectorPatternEntry(frame)  = entryA;
    EZ_FileSelectorSelectionEntry(frame) = entryB;
    EZ_FileSelectorDirListbox(frame)     = listboxA1;
    EZ_FileSelectorFileListbox(frame)    = listboxA2;
    EZ_FileSelectorOkButton(frame)       = ok;
    EZ_FileSelectorFilterButton(frame)   = filter;
    EZ_FileSelectorCancelButton(frame)   = cancel;

    /* ptr to the current selection */
    EZ_FileSelectorCurrentSelection(frame) = (char **) &(EZ_EntryData(rentryB));
    
    EZ_IListBoxFSL(ilistboxA1) = frame;
    EZ_IListBoxFSL(ilistboxA2) = frame;
    EZ_EntryFSE(rentryA) = frame;
    EZ_EntryFSE(rentryB) = frame;

    /* initialize the file selector */
    EntryACallBack(rentryA, NULL);
  }
  return(frame);
}

/*************************************************************
 *
 *  This is really ugly !!
 *  EZ_FileSelectorCurrentDirectory(fileSelect) always holds 
 *  the current absolute path !!!
 */

static void EntryACallBack(widget, notused)
     EZ_Widget *widget;
     void *notused;
{
  EZ_Widget *fileSelect, *ilistboxA, *listboxA, *ilistboxB, *listboxB;
  char pathname[1024], pattern[1024], cpy[1024], dir_pat[1024], tdir_pat[1024];
  char *patterns[16], **cdir;
  char *pat = EZ_GetEntryString(widget);
  int ii, npatterns, position, gposition = -1;
  char **dirItems = NULL, **fileItems = NULL;
  int   dirItemCnt = 0, fileItemCnt = 0;
  
  if(pat == NULL) return;
  (void)strcpy(cpy, pat);

  fileSelect = EZ_EntryFSE(widget);
  listboxA =  EZ_FileSelectorDirListbox(fileSelect);
  ilistboxA = EZ_ListBoxIListBox(listboxA);
  listboxB =  EZ_FileSelectorFileListbox(fileSelect);
  ilistboxB = EZ_ListBoxIListBox(listboxB);

  if( (npatterns = EZ_CountSetFilePatterns(cpy, patterns)) == 0) return;
  
  EZ_FreezeWidget(listboxA);  
  EZ_FreezeWidget(listboxB);  

  /* pointer grabing is removed. 6-1-98 */
  EZ_WaitCursor(fileSelect, EZ_GetCursor(EZ_C_WATCH));
  
  /* if more than one patterns are specified, find common root. 6-1-98 */
  if(npatterns > 1)
    {
      int i, j, k;
      char **cdir;

      for(i = 0; i < npatterns && i < 16; i++)
	{
	  (void) strcpy(pattern, patterns[i]);
	  cdir = (char **)&EZ_FileSelectorCurrentDirectory(fileSelect);
	  position = EZ_ProcessFileSelectorPattern(cdir, pattern);
	  if(i == 0) { strcpy(tdir_pat, *cdir); gposition = position;}
	  else
	    {
	      char *p, *q;
	      p = *cdir;
	      q = tdir_pat;
	      j = 0; k = 0;
	      while(*p && *q && *p == *q)
		{
		  j++;
		  if(*p == '/') k = j;
		  p++; q++;
		}
	      *q = 0;
	      gposition = k;
	    }
	}
    }

  for(ii = 0; ii < npatterns && ii < 16; ii++)
    {
      (void) strcpy(pattern, patterns[ii]);
      cdir = (char **)&EZ_FileSelectorCurrentDirectory(fileSelect);
      position = EZ_ProcessFileSelectorPattern(cdir, pattern);
      strcpy(tdir_pat, *cdir);
      if(gposition < 0) gposition = position;
      if(gposition > 0) (*cdir)[gposition] = 0; /* ????? */
      if(ii == 0) EZ_SetEntryString(widget, pattern);
      else 
	{
	  EZ_EntryEditEndOfInput(widget);
	  EZ_EntryEditInsertString(widget,"  ");
	  EZ_EntryEditInsertString(widget,pattern);
	  if(EZ_WidgetMapped(widget))  EZ_DrawWidget(widget);  
	}
      
      /* contents of dir listbox */
      {
	char *tmp = tdir_pat;
	char **dirlist;
	int  do_it; 
	(void)strcpy(pathname, tmp);
	(void)strcat(pathname, "*/");
	
	if(ii == 0) { do_it  = 1; (void)strcpy(dir_pat,pathname);}
	else if(strcmp(dir_pat, pathname)) do_it = 1;
	else do_it =0;
	
	if(do_it)
	  {
	    dirlist = (char **) EZ_GlobFileName(pathname);    
	    if(dirlist)
	      {
		int i, j = 0, k; 
		while(dirlist[j] != NULL) j++;
		k = dirItemCnt;
		dirItemCnt += j;
		if(dirItems == NULL)
		  dirItems = (char **)my_malloc((dirItemCnt+1)*sizeof(char *),_MISC_DATA_);
		else dirItems = (char **)my_realloc(dirItems, (dirItemCnt+1)*sizeof(char *),_MISC_DATA_);
		for(i = 0; i < j; i++)   dirItems[i+k] = dirlist[i];
		(void)free((char *)dirlist);  /* not allocated by my_malloc */
	      }
	  }
      }
      /* content of the files listbox */
      {
	char **dirlist = (char **) EZ_GlobFileName(pattern);
	if(dirlist)
	  {
	    int len = strlen(pattern);
	    int dir_only = (pattern[len-1] == '/') ? 1 : 0;
	    if(dirlist)
	      {
		struct stat finfo;
		int i, j = 0, ll; 
		while(dirlist[j] != NULL) j++;
		if(fileItems == NULL)
		  fileItems = (char **)my_malloc((fileItemCnt+j+2)*sizeof(char *),_MISC_DATA_);
		else fileItems=(char **)my_realloc(fileItems,(fileItemCnt+j+2)*sizeof(char *),_MISC_DATA_);
		
		for(ll = 0, i = 0; i < j; i++)
		  {
		    if( dir_only == 1 || ((stat (dirlist[i], &finfo) >= 0) && !S_ISDIR (finfo.st_mode)))
		      {
			fileItems[fileItemCnt + ll] = dirlist[i];
			ll++;
		      }
		    else (void)free((char *)dirlist[i]);  /* not allocated by my_malloc */
		  }
		fileItemCnt += ll;
		(void)free((char *)dirlist);  /* not allocated by my_malloc */
	      }
	  }
      }
    } /** end of loop */

  /* update the two listboxes */
  EZ_ClearListBox(ilistboxB);  /* file listbox */
  EZ_ClearIListBox(ilistboxA); /* dir listbox  */
  EZ_InsertIListBoxItem(ilistboxA, "..", 0);
  if(dirItems != NULL)
    {
      int i;
      if(dirItemCnt > 0)
	{
	  qsort(dirItems, dirItemCnt, sizeof(char *), EZ_CmpStr);
	  EZ_InsertIListBoxItem(ilistboxA, dirItems[0]+gposition, -1);
	  for(i = 1; i < dirItemCnt; i++)
	    {
	      if(strcmp(dirItems[i], dirItems[i-1]))
		EZ_InsertIListBoxItem(ilistboxA, dirItems[i]+gposition, -1);
	    }
	  for(i = 0; i < dirItemCnt; i++)
	    my_free(dirItems[i]);
	}
      my_free(dirItems);
    }
  if(fileItems != NULL)
    {
      int i;
      if(fileItemCnt > 0)
	{
	  qsort(fileItems, fileItemCnt, sizeof(char *), EZ_CmpStr);
	  EZ_InsertIListBoxItem(ilistboxB, fileItems[0]+gposition, -1);
	  for(i = 1; i < fileItemCnt; i++)
	    {
	      if(strcmp(fileItems[i], fileItems[i-1]))
		EZ_InsertIListBoxItem(ilistboxB, fileItems[i]+gposition, -1);
	    }
	  for(i = 0; i < fileItemCnt; i++)
	    my_free(fileItems[i]);
	}
      my_free(fileItems);
    }
  EZ_NormalCursor(fileSelect);
  EZ_UnFreezeWidget(listboxA);  
  EZ_UnFreezeWidget(listboxB);  
}
/*************************************************************/
static void ListboxACallBack(widget, notused)
     EZ_Widget *widget;
     void *notused;
{
  EZ_Widget *fileSelect = EZ_IListBoxFSL(widget);
  EZ_Widget *oEntry = EZ_FileSelectorPatternEntry(fileSelect); /* pattern entry */
  EZ_Widget *entry = EZ_OptEntryEntry(oEntry);

  if(EZ_IListBoxSLine(widget) >= 0)
    {
      EZ_ClearWidgetHighlightFlag(entry); /* remove highlighting on entry */
      EZ_DrawWidget(entry);	  
      /* call the entry call backs */
      EZ_ExecuteWidgetCallBacks(entry);
      if(oEntry) {EZ_ExecuteWidgetCallBacks(oEntry);}
    }
}
/*************************************************************/
static void ListboxBCallBack(widget, notused)
     EZ_Widget *widget;
     void *notused;
{
  EZ_Widget *fileSelect = EZ_IListBoxFSL(widget);
  EZ_Widget *oEntry = EZ_FileSelectorSelectionEntry(fileSelect);  /* selection entry */
  EZ_Widget *entry = EZ_OptEntryEntry(oEntry);
  
  if(EZ_IListBoxSLine(widget) >= 0)  
    {
      EZ_ClearWidgetHighlightFlag(entry); /* remove highlighting on entry */
      EZ_DrawWidget(entry);	  
      /* call the entry call backs */
      EZ_ExecuteWidgetCallBacks(entry);
      if(oEntry) {EZ_ExecuteWidgetCallBacks(oEntry);}
    }
}
/*************************************************************/
static void ListboxAMotionCallBack(widget, notused)
     EZ_Widget *widget;
     void *notused;
{
  char str[1024];
  EZ_Widget *fileSelect = EZ_IListBoxFSL(widget);
  EZ_Widget *oEntry = EZ_FileSelectorPatternEntry(fileSelect);  /* pattern entry */
  EZ_Widget *entry = EZ_OptEntryEntry(oEntry);
  char *item = EZ_GetListBoxSelectedItem(widget);
  if(item) 
    {
      int len = strlen(item);
      (void)strcpy(str, item);
      if(str[len-1] == '/') { str[len] = '*';  str[len+1] = 0; }
      else { str[len] = '/'; str[len+1] = '*'; str[len+2] = 0; }

      EZ_SetEntryString(entry, str);
    }
}
/*************************************************************/
static void ListboxBMotionCallBack(widget, notused)
     EZ_Widget *widget;
     void *notused;
{
  EZ_Widget *fileSelect = EZ_IListBoxFSL(widget);
  EZ_Widget *oEntry = EZ_FileSelectorSelectionEntry(fileSelect);  /* selection entry */
  EZ_Widget *entry = EZ_OptEntryEntry(oEntry);
  char *item = EZ_GetListBoxSelectedItem(widget);

  if(item)
    {
      char str[1024], *dir;
      dir = EZ_FileSelectorCurrentDirectory(fileSelect);
      (void)strcpy(str, dir);
      (void)strcat(str, item);
      EZ_SetEntryString(entry, str);  
    }
}
/*************************************************************/
static void EntryBCallBack(widget,notused)
     EZ_Widget *widget;
     void *notused;
{
  EZ_Widget *fileSelect =  EZ_EntryFSE(widget);
  if(fileSelect) 
    {
      EZ_Widget *listbox = EZ_FileSelectorFileListbox(fileSelect);
      char *item = EZ_GetListBoxSelectedItem(listbox);
      if(item)
	{
	  char str[1024], *dir;
	  char *estr = EZ_GetEntryString(widget);
	  dir = EZ_FileSelectorCurrentDirectory(fileSelect);
	  (void)strcpy(str, dir);
	  (void)strcat(str, item);
	  if(strcmp(str,estr))
	    {
	      EZ_Widget *ilistbox = EZ_ListBoxIListBox(listbox);
	      EZ_IListBoxSLine(ilistbox) = -1;
	      EZ_IListBoxSLineEnd(ilistbox) = -1;
	      EZ_DrawWidget(ilistbox);
	    }
	}
      EZ_ExecuteWidgetCallBacks(fileSelect);
    }      
}

/*************************************************************/
static void CancelCallBack(widget, notused)
     EZ_Widget *widget;
     void *notused;
{
  EZ_Widget *fileSelect = EZ_ButtonFSB(widget);  /* the file selector */
  if(fileSelect) EZ_HideWidget(fileSelect);
}
/*************************************************************/
static void OkCallBack(widget, notused)
     EZ_Widget *widget;
     void *notused;
{
  EZ_Widget *fileSelect = EZ_ButtonFSB(widget);  /* the file selector */
  if(fileSelect)
    {
      EZ_Widget *listbox = EZ_FileSelectorFileListbox(fileSelect);
      char *item = EZ_GetListBoxSelectedItem(listbox);
      if(item)
	{
	  char str[1024], *dir, *estr;
	  EZ_Widget *entry = EZ_FileSelectorSelectionEntry(fileSelect);

	  entry = EZ_OptEntryEntry(entry);
	  estr = EZ_GetEntryString(entry);
	  dir = EZ_FileSelectorCurrentDirectory(fileSelect);
	  (void)strcpy(str, dir);
	  (void)strcat(str, item);
	  if(strcmp(str,estr))
	    {
	      EZ_Widget *ilistbox = EZ_ListBoxIListBox(listbox);
	      EZ_IListBoxSLine(ilistbox) = -1;
	      EZ_IListBoxSLineEnd(ilistbox) = -1;
	      EZ_DrawWidget(ilistbox);
	    }
	}
      EZ_ExecuteWidgetCallBacks(fileSelect);
    }      
}
/*************************************************************/
static void FilterCallBack(widget, notused)
     EZ_Widget *widget;
     void *notused;
{
  EZ_Widget *fileSelect = EZ_ButtonFSB(widget);  /* the file selector */
  if(fileSelect)
    {
      EZ_Widget *oEntry = EZ_FileSelectorPatternEntry(fileSelect);  /* pattern entry */
      EZ_Widget *entry = EZ_OptEntryEntry(oEntry);

      if(entry) { EZ_ExecuteWidgetCallBacks(entry);}
      {EZ_ExecuteWidgetCallBacks(oEntry);}
    }
}  
/*************************************************************/
static void EZ_FreeFileSelectData(widget)
     EZ_Widget *widget;
{
  if(EZ_FileSelectorCurrentDirectory(widget))
    (void)my_free((char *)EZ_FileSelectorCurrentDirectory(widget));
}
/*************************************************************/
void EZ_GetFileSelectorWidgetComponents(widget, pattern_entry, selection_entry,
				  dir_listbox, file_listbox, ok_button,
				  filter_button, cancel_button)
     EZ_Widget *widget;
     EZ_Widget **pattern_entry, **selection_entry, **dir_listbox,
               **file_listbox, **ok_button, **filter_button,
               **cancel_button;
{
  if(widget && EZ_WidgetType(widget) == EZ_WIDGET_FILE_SELECTOR)
    {
      if(pattern_entry)
	*pattern_entry = EZ_FileSelectorPatternEntry(widget);
      if(selection_entry)
	*selection_entry = EZ_FileSelectorSelectionEntry(widget);
      if(dir_listbox)
	*dir_listbox = EZ_FileSelectorDirListbox(widget);
      if(file_listbox)
	*file_listbox = EZ_FileSelectorFileListbox(widget);
      if(ok_button)
	*ok_button = EZ_FileSelectorOkButton(widget);
      if(filter_button)
	*filter_button = EZ_FileSelectorFilterButton(widget);
      if(cancel_button)
	*cancel_button = EZ_FileSelectorCancelButton(widget);
    }
}
/*************************************************************/
void EZ_SetFileSelectorInitialPattern(widget, pattern)
     EZ_Widget *widget; char *pattern;
{
  if(widget && EZ_WidgetType(widget) == EZ_WIDGET_FILE_SELECTOR)  
    {
      EZ_Widget *rentry, *entry = EZ_FileSelectorPatternEntry(widget);
      EZ_SetOptionalEntryString(entry, pattern);
      rentry = EZ_OptEntryEntry(entry);
      EntryACallBack(rentry, NULL);
    }
}
/*************************************************************/
char *EZ_GetFileSelectorSelection(widget)
     EZ_Widget *widget;
{
  if(widget && EZ_WidgetType(widget) == EZ_WIDGET_FILE_SELECTOR)
    {
      return( *(EZ_FileSelectorCurrentSelection(widget)));
    }
  return(NULL);
}
/*************************************************************/
int EZ_CmpStr(vaptr,vbptr)
     CONST void *vaptr;
     CONST void *vbptr;
{
  char **aptr = (char **)vaptr;
  char **bptr = (char **)vbptr;
  char *a = *aptr, *b = *bptr;
  if(a == NULL) return(1);
  else if( b == NULL) return(-1);
  while(*a && *b && *a == *b) {a++; b++;}
  if(*a == 0) return(-1);
  else if(*b == 0) return(1);
  return( (*a > *b)? 1: -1);
}
/*************************************************************
 *
 *  given the a pattern and a place or storing the current
 *  directory. Process opattern, and update cdir.
 *     cdir is a malloced array!
 *     opattern is overwritten !!!
 *  Potential problem: assume opattern is a big-enough 
 *  array and working directly on it.
 */
int EZ_ProcessFileSelectorPattern(cdir, opattern)
     char **cdir, *opattern;
{
  char *pattern = opattern, *epat;
  char pathname[1024];

  if((epat = EZ_TildeExpand(pattern)) != NULL) 
    {
      (void)strcpy(pattern, epat);
      (void)free((char *)epat);  /* not allocated by my_malloc */
    }

  /* set the current-directory if *cdir = NULL */
  {
    /* cdir end with a / */
    char **tmp = cdir;
    
    if(*tmp == NULL)
      {
	char *ptr;

	if(pattern[0] == '/')
	  {
	    *tmp = EZ_AllocCopyString(pattern);
	    ptr = (char *)strrchr(*tmp, '/');
	    if(ptr) *(ptr+1) = 0;
	  }
	else  /* path is not absolute, use cwd */
	  {
	    int  len;
	    char cwd[1024];
	    if(getcwd(cwd,1024) == NULL)  (void)strcpy(cwd, getenv("HOME"));
	    len = strlen(cwd);
	    if(len > 1 && cwd[len-1] != '/') { cwd[len] = '/'; cwd[len+1] = 0; }

	    *tmp = EZ_AllocCopyString(cwd);
	  }
      }
  }
  
  /* collapsing .. and ./  Result are saved in pattern */
  if(pattern[0] == '.' && pattern[1] == '/')
    {
      int i, j = 2;

      (void)strcpy(pathname, *cdir);
      i = strlen(pathname);
      while(pattern[j] != 0) pathname[i++] = pattern[j++];
      pathname[i] = 0;
      (void)strcpy(pattern, pathname);
    }
  else if(pattern[0] == '.' && pattern[1] == '.' && (pattern[2] == 0 || pattern[2] == '/')) 
    {
      int i, j;
      char *slash;
      (void)strcpy(pathname, *cdir);
      i = strlen(pathname);
      if(pathname[i-1] == '/') pathname[i-1] = 0;
      slash = (char *)strrchr(pathname, '/');
      if(slash) *(slash+1) = 0;
      else { pathname[0] = '/';  pathname[1] = 0;}

      i = strlen(pathname);      
      if(pattern[2] == '/')
	{
	  j = 3;
	  while(pattern[j] != 0) pathname[i++] = pattern[j++];
	  pathname[i] = 0;
	}
      else
	{
	  pathname[i++] = '*'; pathname[i] = 0; 
	}
      (void) strcpy(pattern, pathname);
    }
  else if(pattern[0] != '/')
    {
      /* relative to the current path */
      int pos = position_after_last_slash_before_glob_chars( *cdir);
      (void)strncpy(pathname, *cdir, pos);
      pathname[pos] = 0;
      (void)strcat(pathname, pattern);
      (void)strcpy(pattern, pathname);
    }

  /* update cdir */
  {
    int len, dot = 0, changed = 0;
    char *s, *p = pattern, *q = pathname;
   
    while(*p)
      {
	switch(*p)
	  {
	  case '.':
	    dot++;
	    break;
	  case '/':
	    if(dot == 1)
	      {
		q -= 2;
		if(q < pathname) q = pathname;
		changed = 1;
	      }
	    else if(dot == 2) 
	      {
		q -= 3;
		*q = 0;
		s = strrchr(pathname, '/');
		if(s) q = s;
		else q = pathname;
		changed = 1;		
	      }
	    dot = 0;
	    break;
	  default:
	    dot = 0;
	    break;
	  }
	*q = *p;
	p++; q++;
      }
    *q = 0;

    if(changed) strcpy(pattern, pathname);
    len = strlen(pathname);
    if(pathname[len -1] == '/')   pathname[ (--len)] = 0;
    if(len > 0)
      {
	char *slash = (char *)strrchr(pathname, '/');
	if(slash)  *(slash+1) = 0;
	else len = 0;
      }
    if(len <= 0) *cdir = EZ_ReallocCopyString(*cdir, "/");
    else *cdir = EZ_ReallocCopyString(*cdir, pathname);
  }
  return(position_after_last_slash_before_glob_chars( *cdir));
}

/*****************************************************************/
int position_after_last_slash_before_glob_chars(path)
     char *path;
{
  register char c;
  register char *ptr = path;
  int  done = 0;
  int  count = 0;
  while((c = *ptr++) != '\0' && done == 0)
    switch (c)
      {
      case '?':
      case '*':
      case '[':
	done = 1;
	break;
      case '/':
	count = (ptr - path);
	break;
      default:
	break;
      }
  return(count);
}

int EZ_IsGlobPattern(path)
     char *path;
{
  if(path)
    {
      register char c;
      register char *ptr = path;
      while((c = *ptr++) != '\0')
	switch (c)
	  {
	  case '?':
	  case '*':
	  case '[':
	    return(1);
	    break;
	  default:
	    break;
	  }
    }
  return(0);
}

/***********************************************************************/
int EZ_CountSetFilePatterns(pat, patterns)
     char *pat, *patterns[];
{
  int space = 1, count = 0;
  char *ptr = pat;
  
  while(*ptr)
    {
      switch(*ptr)
	{
	case ' ':
	case '\t':
	  if(!space) { space = 1; *ptr = 0; }
	  break;
	case 0:
	  break;
	default:
	  if(space) { patterns[count++] = ptr; space = 0;}
	  break;
	}
      ptr++;
    }
  return(count);
}
/***********************************************************************/
#undef _EZ_WIDGET_FILE_SELECT_C_
