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

    Copyright (C) 1994,1997 Ivan A. Curtis.  All rights reserved.

This code must not be re-distributed without these copyright notices intact.

*******************************************************************************
*******************************************************************************

Filename:	~icurtis/src/mx/filesel.c

Description:	

Update History:   (most recent first)
   I. Goldberg  25-Sep-97 16:09 -- .PDB and .PRC are also databases
   I. Goldberg  27-Jun-97 18:02 -- fix memory allocation, add "database only"
                                     selection option
   B. Long      10-May-97 03:12 -- port to SunOS, AIX
   I. Curtis     9-Apr-97 12:02 -- Updated
   I. Curtis    22-Mar-94 23:11 -- Created.

******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef SUNOS414
#  include <sys/types.h>
#  include <sys/dir.h>
#  define dirent direct
#else
#  include <dirent.h>
#endif
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include "basic.h"
#include "menu.h"
#include "select.h"

#ifdef SUNOS414
extern int scandir(char *dirname, struct direct **namelist, int (*select)(),
		    int (*compar)());
extern int alphasort(struct direct **d1, struct direct **d2);
#endif

/*
 * This function used to filter entries for
 * the file selector. We want all files, so
 * we always return 1
 */
int mx_dir_sel(const struct dirent *ent)
{
  return 1;
}

/*
 * This function is used by qsort to sort
 * entries in the file selection. We make
 * a directory always less than an ordinary
 * file, that way all the directories are
 * listed at the head of the list
 *
 * 5-10-97 blong
 *    qsort only takes two args, also, directories are denoted by a
 *    trailing /
 */
int mx_dir_sort(const void *a, const void *b)
{
  mx_menu_item *pa = (mx_menu_item *) a;
  mx_menu_item *pb = (mx_menu_item *) b;

  if (pa->text[strlen(pa->text)-1] == '/') {
    if (pb->text[strlen(pb->text)-1] == '/') 
      return (strcmp(pa->text, pb->text));
    else
      return -1;
  } else if (pb->text[strlen(pb->text)-1] == '/') {
    return 1;
  } else return (strcmp(pa->text, pb->text));
}

/* 
 * Given a directory, return a list of items and the number of items 
 * 5-10-97 blong
 *       Don't use scandir, its a BSDism which doesn't work on Solaris
 */
mx_menu_item *mx_dir_items(const char *dir, int *sel_nitems, int allP)
{
  DIR *dp;
  int nitems = 0;
  int max = 256;
  struct dirent *de;
  struct stat s;
  char buffer[_POSIX_PATH_MAX];
  mx_menu_item *items;
  int isdb;

  if ((dp = opendir(dir)) == NULL) {
    *sel_nitems = 0;
    return NULL;
  }

  items = (mx_menu_item *)malloc((max+1) * sizeof(mx_menu_item));
  items[0].flag = MXItemFlag_Center;
  items[0].text = strdup("Select File");
  items[0].length = 0;
  while ((de = readdir (dp)) != NULL) {
    if (strcmp(de->d_name, ".") == 0) continue;    /* we don't need . */
    sprintf(buffer, "%s/%s", dir, de->d_name);
    /* Is this a database file? */
    isdb = 0;
    if (strlen(de->d_name) >= 4) {
	char *suffix = de->d_name + strlen(de->d_name) - 4;
	if (!strcmp(suffix, ".prc") || !strcmp(suffix, ".pdb")
	 || !strcmp(suffix, ".PRC") || !strcmp(suffix, ".PDB")) {
	    isdb = 1;
	}
    }
    if (stat(buffer, &s) == 0) {
      if (!allP && !S_ISDIR(s.st_mode) && !isdb) continue;
      items[nitems+1].flag = MXItemFlag_Left;
      if (S_ISDIR(s.st_mode)) {
        items[nitems + 1].text = (char *)malloc(strlen(de->d_name) + 2);
        strcpy(items[nitems + 1].text, de->d_name);
        strcat(items[nitems + 1].text, "/");
      } else {
        items[nitems + 1].text = strdup(de->d_name);
      } 
    } else {
      if (!allP) continue;
      items[nitems + 1].flag = MXItemFlag_Left | MXItemFlag_Disabled;
      items[nitems + 1].text = strdup(de->d_name);
    }
    items[nitems + 1].length = 0;
    nitems++;
    if (nitems >= max) {
      max += 256;
      items = realloc(items, sizeof(mx_menu_item) * (max+1));
    }
    items[nitems + 1].text = NULL;
  }
  closedir(dp);
  /* The first entry is the title, so don't sort it */
  ++nitems;
  qsort(&items[1], nitems-1, sizeof(mx_menu_item), mx_dir_sort); 
  *sel_nitems = nitems;
  return items;
}

/* 
 * 5-10-97 blong
 *   free a mx_menu_item array
 */
void mx_free_menuitems(mx_menu_item *items, int nitems)
{
  int x;

  for (x = 0; x < nitems; x++) {
    if (items[x].text != NULL) free(items[x].text);
  }
  free (items);
}

/*
 * 5-10-97 blong
 *   this routine now deletes the item list generated in mx_dir_items()
 */
char *mx_popup_filesel(Display *display, int screen,
		       mx_appearance *app,
		       int *x, int *y, int allP)
{
  int choice, looping;
  int sel_nitems = 0;
  int rx, ry;
  mx_menu_item *sel_items = NULL;
  mx_panel *panel_select = NULL;

  choice = -1;
  looping = 1;
  while (looping) {
    int idx;
    rx = *x;
    ry = *y;
    if (panel_select != NULL) {
      free(panel_select);
    }
    if (sel_items != NULL) {
      mx_free_menuitems(sel_items, sel_nitems);
      sel_nitems = 0;
    }

    sel_items = mx_dir_items(".", &sel_nitems, allP);
    if (sel_nitems <= 0) {
      break;
    }

    panel_select = mx_panel_create(display, app, sel_nitems, sel_items);
    choice = mx_popup_select(display, screen, panel_select,
			     &rx, &ry, mx_min(sel_nitems - 1, 8));
    if (choice < 0) {
      break;
    }

    idx = strlen(panel_select->item[choice].text) - 1;
    if (panel_select->item[choice].text[idx] == '/') {
      chdir(panel_select->item[choice].text);
    } else {
      looping = 0;
    }
  }

  *x = rx;
  *y = ry;
  if (!looping) {
    return panel_select->item[choice].text;
  } else {
    return NULL;
  }
}
