/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                           F I L E S E L E C T . C                            *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: fileselect.c,v 1.6 2003/11/07 11:02:00 jrh Exp $
* $Log: fileselect.c,v $
* Revision 1.6  2003/11/07 11:02:00  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:07:14  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:25:28  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:49:15  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:47:43  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:40:52  jrh
* Initial revision
*
*/
#include<sys/types.h>
#include<dirent.h>
#include<fnmatch.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<X11/Xlib.h>
#include<X11/Intrinsic.h>
#include<Xm/Xm.h>
#include<Xm/DialogS.h>
#include<Xm/FileSB.h>
#include<Xm/MwmUtil.h>
#include "viewmol.h"

extern void *getmem(size_t, size_t);
#ifdef AIX
extern int alphasort(struct dirent **, struct dirent **);
#endif

static void GetFilename(Widget, char **, XmFileSelectionBoxCallbackStruct *);
static void Cancel(Widget, char **, caddr_t);
extern void MapBox(Widget, caddr_t, XmAnyCallbackStruct *);
#if defined LINUX || defined DARWIN
static int matchFileNames(const struct dirent *);
static int matchDirNames(const struct dirent *);
#else
static int matchFileNames(struct dirent *);
static int matchDirNames(struct dirent *);
#endif
static void searchFile(Widget, XtPointer);
static void searchDir(Widget, XtPointer);

extern XtAppContext app;
extern Widget topShell, fileBox;
extern int rgbMode;

static int cont;
static char *string, *directory, *mask;

void createFileselect(void)
{
  Visual *vi;
  Pixel bg;
  Widget helpbutton;
  Arg args[7];

  /* This function creates the file selection box */

  XtVaGetValues(topShell, XmNvisual, &vi, XmNbackground, &bg, NULL);
  XtSetArg(args[0], XmNautoUnmanage, False);
  XtSetArg(args[1], XmNdefaultPosition, False);
  XtSetArg(args[2], XmNmwmDecorations, MWM_DECOR_RESIZEH | MWM_DECOR_TITLE);
  XtSetArg(args[3], XmNfileTypeMask, XmFILE_REGULAR);
  XtSetArg(args[4], XmNfileSearchProc, searchFile);
  XtSetArg(args[5], XmNdirSearchProc, searchDir);
  XtSetArg(args[6], XmNvisual, vi);

  fileBox=XmCreateFileSelectionDialog(topShell, "fileSelectionBox", args,
                                      XtNumber(args));

  XtAddCallback(fileBox, XmNokCallback, (XtCallbackProc)GetFilename, &string);
  XtAddCallback(fileBox, XmNcancelCallback, (XtCallbackProc)Cancel, &string);
  XtAddCallback(fileBox, XmNmapCallback, (XtCallbackProc)MapBox, NULL);

  helpbutton=XmFileSelectionBoxGetChild(fileBox, XmDIALOG_HELP_BUTTON);
  XtUnmanageChild(helpbutton);
}

char *selectFile(char *filter, char *deflt, int apply)
{
  XEvent event;
  XmString text;

  if (apply)
  {
    text=XmStringCreateSimple(filter);
    XtVaSetValues(fileBox, XmNpattern, text, NULL);
    XmStringFree(text);
    text=XmStringCreateSimple(deflt);
    XtVaSetValues(fileBox, XmNtextString, text, NULL);
    XmStringFree(text);
  }
  XtManageChild(fileBox);

  cont=TRUE;
  while (cont)
  {
    XtAppNextEvent(app, &event);
    XtDispatchEvent(&event);
  }
  XtUnmanageChild(fileBox);
  return(string);
}

static void GetFilename(Widget button, char **client_data,
                        XmFileSelectionBoxCallbackStruct *call_data)
{
  char *text;

  XmStringGetLtoR(call_data->value, XmFONTLIST_DEFAULT_TAG, &text);
  *client_data=text;
  cont=FALSE;
}

static void Cancel(Widget button, char **client_data, caddr_t call_data)
{
  *client_data=(char *)NULL;
  cont=FALSE;
}

#if defined LINUX || defined DARWIN
int matchFileNames(const struct dirent *d)
#else
int matchFileNames(struct dirent *d)
#endif
{
  struct stat buffer;
  char filename[FILENAME_MAX];

  strcpy(filename, directory);
  if (filename[strlen(filename)-1] != '/') strcat(filename, "/");
  strcat(filename, d->d_name);
  if (fnmatch(mask, filename, FNM_PATHNAME | FNM_PERIOD))
    return(0);
  if (stat(filename, &buffer) == -1)
    return(0);
  if ((buffer.st_mode & S_IFMT) == S_IFDIR)
    return(0);
  return(1);
}

#if defined LINUX || defined DARWIN
int matchDirNames(const struct dirent *d)
#else
int matchDirNames(struct dirent *d)
#endif
{
  struct stat buffer;
  char filename[FILENAME_MAX];

  strcpy(filename, directory);
  if (filename[strlen(filename)-1] != '/') strcat(filename, "/");
  strcat(filename, d->d_name);
  if (strcmp(d->d_name, ".."))
  {
    if (fnmatch(mask, filename, FNM_PATHNAME | FNM_PERIOD))
      return(0);
  }
  if (stat(filename, &buffer) == -1)
    return(0);
  if ((buffer.st_mode & S_IFMT) == S_IFDIR)
    return(1);
  return(0);
}

static void searchFile(Widget w, XtPointer s)
{
  XmFileSelectionBoxCallbackStruct *search=(XmFileSelectionBoxCallbackStruct *)s;
  struct dirent **namelist;
  XmString *names;
  int n=0;
  char filename[FILENAME_MAX];
  register int i;

  if (!XmStringGetLtoR(search->dir, XmFONTLIST_DEFAULT_TAG, &directory)) return;
  if (!XmStringGetLtoR(search->mask, XmFONTLIST_DEFAULT_TAG, &mask)) return;

  if ((n=scandir(directory, &namelist, matchFileNames, alphasort)) == -1)
    return;

  if (n)
  {
    names=(XmString *)getmem((size_t)n, sizeof(XmString));
    for (i=0; i<n; i++)
    {
      strcpy(filename, directory);
      if (filename[strlen(filename)-1] != '/') strcat(filename, "/");
      strcat(filename, namelist[i]->d_name);
      names[i]=XmStringCreateLocalized(filename);
      free(namelist[i]);
    }
    free(namelist);
    XtVaSetValues(w, XmNfileListItems, names,
                     XmNfileListItemCount, n,
                     XmNdirSpec, search->dir,
                     XmNlistUpdated, True,
                     NULL);
    while (i > 0)
      XmStringFree(names[--i]);
    free(names);
  }
  else
  {
    XtVaSetValues(w, XmNfileListItems, NULL,
                     XmNfileListItemCount, 0,
                     XmNlistUpdated, True,
                     NULL);
  }
  XtFree(mask);
  XtFree(directory);
}

static void searchDir(Widget w, XtPointer s)
{
  XmFileSelectionBoxCallbackStruct *search=(XmFileSelectionBoxCallbackStruct *)s;
  struct dirent **namelist;
  XmString *names;
  int n=0;
  char filename[FILENAME_MAX];
  register int i;

  if (!XmStringGetLtoR(search->dir, XmFONTLIST_DEFAULT_TAG, &directory)) return;
  if (!XmStringGetLtoR(search->mask, XmFONTLIST_DEFAULT_TAG, &mask)) return;

  if ((n=scandir(directory, &namelist, matchDirNames, alphasort)) == -1)
  {
    XtVaSetValues(w, XmNdirectoryValid, False, NULL);
    return;
  }

  names=(XmString *)getmem((size_t)n, sizeof(XmString));
  for (i=0; i<n; i++)
  {
    strcpy(filename, directory);
    if (filename[strlen(filename)-1] != '/') strcat(filename, "/");
    strcat(filename, namelist[i]->d_name);
    names[i]=XmStringCreateLocalized(filename);
    free(namelist[i]);
  }
  free(namelist);
  if (n)
  {
    XtVaSetValues(w, XmNdirListItems, names,
                     XmNdirListItemCount, n,
                     XmNlistUpdated, True,
                     XmNdirectoryValid, True,
                     XmNdirSpec, search->dir,
                     NULL);
    while (i > 0)
      XmStringFree(names[--i]);
    free(names);
  }
  else
  {
    XtVaSetValues(w, XmNdirListItems, NULL,
                     XmNdirListItemCount, 0,
                     XmNlistUpdated, True,
                     XmNdirectoryValid, True,
                     NULL);
  }
  XtFree(mask);
  XtFree(directory);
}
