/* iconlist.c
 * Copyright (C) 2002-2004 Pascal Eberhard <pascal.ebo@netcourrier.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */


#include "common.h"

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>

#define _GNU_SOURCE 1
#include <fnmatch.h>

//#ifndef FNM_CASEFOLD            // case insensitive but is not defined... ?
//#define FNM_CASEFOLD  (1 << 4)
//#endif

typedef struct scandir_context_t {
  icon_list_t *icons;
} scandir_context_t;
static scandir_context_t scandir_context = { .icons = NULL };
static int icon_list_scandir_icon(const struct dirent *de);
static int icon_list_scandir_path(const struct dirent *de);

// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
icodir_t* icodir_new_child(gchar *dirname, gboolean sel,
                           gboolean rec, icodir_t *parent)
{
  icodir_t *dir;
  g_assert(dirname != NULL);
  dir = (icodir_t*) malloc(sizeof(icodir_t));
  g_assert(dir != NULL);
  dir->path = dirname;
  dir->count_total = 0;
  dir->count_selected = 0;
  dir->recursive = rec;
  dir->selected = sel;
  dir->parent = parent;
  dir->children = NULL;
  if (parent != NULL)
    parent->children = g_list_append(parent->children, dir);
  return dir;
}
// ----------------------------------------------------------------------------
icodir_t* icodir_new(gchar *dirname, gboolean sel, gboolean rec)
{
  return icodir_new_child(dirname, sel, rec, NULL);
}
// ----------------------------------------------------------------------------
void icodir_free(icodir_t *icodir)
{
  g_assert(icodir != NULL);
  g_assert(icodir->children == NULL);
  if (icodir->count_total != 0)
    ERR("path:%s, count_total:%d", icodir->path, icodir->count_total);
  
  if (icodir->path != NULL)
    g_free(icodir->path);
  if (icodir->parent != NULL)
    icodir->parent->children = g_list_remove(icodir->parent->children, icodir);
  g_free(icodir);
}
// ----------------------------------------------------------------------------
gint icodir_compare(icodir_t *a, icodir_t *b)
{
  g_assert(a != NULL && b != NULL);
  if (strcmp("", a->path) == 0)
    return 1;
  if (strcmp("", b->path) == 0)
    return -1;
  return strcmp(a->path, b->path);
}
// ----------------------------------------------------------------------------
gint icodir_count_total_rec(icodir_t *dir)
{
  GList    *hamster;
  icodir_t *childdir;
  gint      count;
  g_assert(dir != NULL);
  count = dir->count_total;
  hamster = g_list_first(dir->children);
  while(hamster)
  {
    childdir = hamster->data;
    count += icodir_count_total_rec(childdir);
    hamster = g_list_next(hamster);
  }
  return count;
}
// ----------------------------------------------------------------------------
gint icodir_count_selected_rec(icodir_t *dir)
{
  GList    *hamster;
  icodir_t *childdir;
  gint      count;
  g_assert(dir != NULL);
  count = dir->count_selected;
  hamster = g_list_first(dir->children);
  while(hamster)
  {
    childdir = hamster->data;
    count += icodir_count_selected_rec(childdir);
    hamster = g_list_next(hamster);
  }
  return count;
}
// ----------------------------------------------------------------------------
icodir_t * icodir_list_find(GList *dirs, char *dirname)
{
  icodir_t *dir;
  GList    *hamster;
  g_assert(dirname != NULL);
  TRACE("dirname:%s", dirname);
  hamster = g_list_first(dirs);
  while (hamster != NULL)
  {
    dir = hamster->data;
    if (strcmp(dir->path, dirname) == 0)
      break;
    hamster = g_list_next(hamster);
  }
  if (hamster == NULL)
    return NULL;
  return dir;
}
// ----------------------------------------------------------------------------
GList * icodir_list_find_with_parent(GList *dirs, icodir_t *parentdir)
{
  icodir_t *dir;
  GList    *hamster;
  g_assert(parentdir != NULL);
  TRACE("dir:%p", parentdir);
  hamster = g_list_first(dirs);
  while (hamster != NULL)
  {
    dir = hamster->data;
    if (dir == parentdir)
      break;
    hamster = g_list_next(hamster);
  }
  return hamster;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
ico_t* ico_new(icodir_t *dir, gchar *name)
{
  ico_t *ico;
  char   filename[FILENAME_MAX];

  g_assert(dir != NULL && name != NULL);
  g_assert((strlen(dir->path) + strlen(name)) < FILENAME_MAX);
  ico = (ico_t *) malloc(sizeof(ico_t));
  g_assert(ico != NULL);
  
  ico->dir = dir;
  ico->name = name;

  strcpy(filename, dir->path);
  strcat(filename, "/");
  strcat(filename, ico->name);
  ico->pixbuf = gdk_pixbuf_new_from_apwal(filename, &ico->width, &ico->height);
  if (!ico->pixbuf)
  {
    TRACE("failed loading pixbuf, return NULL, %s", filename);
    g_free(name);
    g_free(ico);
    return NULL;
  }
  ico->selected = FALSE;
  ico->dir->count_total++;
  return ico;
}
// ----------------------------------------------------------------------------
void ico_free(ico_t *ico)
{
  g_assert(ico != NULL);
  if (ico->name != NULL)
    g_free(ico->name);
  
  if (ico->pixbuf != NULL)
    g_object_unref(ico->pixbuf);
  
  if (ico->dir != NULL)
    ico->dir->count_total--;

  g_free(ico);
}
// ----------------------------------------------------------------------------
gint ico_compare_by_name(ico_t *a, ico_t *b)
{
  g_assert(a != NULL && b != NULL);
  return strcmp(a->name, b->name);
}
// ----------------------------------------------------------------------------
gint ico_compare_by_path(ico_t *a, ico_t *b)
{
  gint res;
  g_assert(a != NULL && b != NULL);
  g_assert(a->dir != NULL && b->dir != NULL);
  res = strcmp(a->dir->path, b->dir->path);
  if (res == 0)
    res = strcmp(a->name, b->name);
  return res;
}
// ----------------------------------------------------------------------------
char *ico_get_full_filename(ico_t *ico)
{
  static char file[FILENAME_MAX];
  g_assert(ico != NULL);
  g_assert(ico->dir != NULL);
  strcpy(file, ico->dir->path);
  strcat(file, "/");
  strcat(file, ico->name);
  return file;
}
// ----------------------------------------------------------------------------
ico_t *ico_list_find_icon(GList *icos, icodir_t *dir, char *ico_name)
{
  GList *hamster;
  ico_t  ico;
  //icos could be empty (so icos==NULL).
  g_assert(dir != NULL && ico_name != NULL);
  //create a fake icon with only the dir and name set for the search
  ico.dir = dir;
  ico.name = ico_name;
  hamster = g_list_find_custom(icos, &ico, (GCompareFunc)ico_compare_by_path);
  if (hamster == NULL)
    return NULL;
  return ((ico_t *)(hamster->data));
}

// ----------------------------------------------------------------------------
ico_t *ico_list_try_select_icon(icodir_t *dir, char *ico_name,
                                icon_list_t *icons)
{
  ico_t      *ico;
  GList      *ext_hamster;
  file_ext_t *ext;
  int         cc;
  gboolean   selected;
  g_assert(dir != NULL && ico_name != NULL && icons != NULL);
   
  // check if the name is matching one of the patterns
  // if name matches pattern then go out the loop with cc=0
  // if name don't match any pattern then cc!=0 at the end of the loop
  // so after the loop, if cc!=0 then don't select the name
  ext_hamster = g_list_first(icons->iconsel->apwal->iconsel_pref->file_exts);
  selected = FALSE;
  while(ext_hamster != NULL && selected == FALSE)
  {
    ext = ext_hamster->data;
    if (ext->selected == TRUE)
    {
      cc = fnmatch(ext->extension, ico_name, FNM_CASEFOLD);
      //TRACE("ext:%s, name:%s, cc:%d", ext->extension, ico_name, cc);
      if (cc == 0)        // success
      {
        selected = TRUE;
        break;
      }
      if ((cc != 0) && (cc != FNM_NOMATCH))      // error during match
        WARN("fnmatch, ext:%s, name:%s, E:%d", ext->extension, ico_name, cc);
    }
    ext_hamster = g_list_next(ext_hamster);
  } //end while ext
  if (selected == FALSE)      // not match
    return NULL;

  // the next tests need the icon to be created. create the icon now
  ico = ico_new(dir, g_strdup(ico_name));
  if (ico == NULL)
    return NULL;

  // check if the size of the icon match the selected size
  selected = FALSE;
  if ((icons->iconsel->apwal->iconsel_pref->select_48 == TRUE) &&
      (ico->width == 48) && (ico->height == 48))
    selected = TRUE;
  else if ((icons->iconsel->apwal->iconsel_pref->select_gt48 == TRUE) &&
           ((ico->width > 48) || (ico->height > 48)))
    selected = TRUE;
  else if ((icons->iconsel->apwal->iconsel_pref->select_lt48 == TRUE) &&
           ((ico->width < 48) || (ico->height < 48)))
    selected = TRUE;
  if (selected == FALSE)
  {
    ico_free(ico);
    return NULL;
  }
  return ico;
}
// ----------------------------------------------------------------------------
void ico_list_append(GList **icos, icodir_t *dir, icon_list_t *icons)
{
  struct dirent **namelist;
  int       n;
  ico_t    *ico;
  ico_t    *found;
  g_assert(icos != NULL && dir != NULL && icons != NULL);

  n = scandir(dir->path, &namelist, icon_list_scandir_icon, alphasort);
  if (n < 0)
  { 
    TRACE("scandir dir:%s, cc:%d, E:%d", dir->path, n, errno);
    return;
  }
  while(n--)
  {
    //TRACE("*icos:%p, dir:%p, namelist[n]->d_name:%p", 
    //      *icos, dir, namelist[n]->d_name);
    ico = ico_list_try_select_icon(dir, namelist[n]->d_name, icons);
    found = ico_list_find_icon(*icos, dir, namelist[n]->d_name);
    if (ico != NULL && found == NULL)
      *icos = g_list_append(*icos, ico);
    else if (ico != NULL && found != NULL)
      ico_free(ico);
    else if (ico == NULL && found != NULL)
    {
      *icos = g_list_remove(*icos, found);
      if (found == icon_list_get_selected_icon(icons))
        icon_list_set_selected_icon(icons, NULL);
      ico_free(found);
    }
    free(namelist[n]);
  }
  free(namelist);
  
  TRACE("path:%s, count_total:%d", dir->path, dir->count_total);
}
// ----------------------------------------------------------------------------
void ico_list_remove(GList **icos, icodir_t *dir, icon_list_t *icons)
{
  GList *hamster;
  ico_t *ico;
  gint  i, count, dircount, dircount2;
  g_assert(icos != NULL && dir != NULL && icons != NULL);

  i = 0;
  dircount = dir->count_total;
  dircount2 = 0;
  hamster = g_list_first(*icos);
  while(hamster)
  {
    ico = hamster->data;
    if (ico->dir == dir)
      dircount2++;
    hamster = g_list_next(hamster);
  }
  count = g_list_length(*icos);
  hamster = g_list_first(*icos);
  while (hamster != NULL)
  {
    ico = hamster->data;
    if (ico->dir == dir)
    {
      if (hamster == g_list_first(*icos))
      {
        *icos = g_list_remove(*icos, ico);
        if (ico == icon_list_get_selected_icon(icons))
          icon_list_set_selected_icon(icons, NULL);
        hamster = g_list_first(*icos);
      }
      else
      {
        hamster = g_list_remove(hamster, ico);
        if (ico == icon_list_get_selected_icon(icons))
          icon_list_set_selected_icon(icons, NULL);
      }
      i++;
      ico_free(ico);
    }
    else
      hamster = g_list_next(hamster);
  }
  if (dircount != i)
    ERR("icon list:%d, path:%s, dir->count_total:%d !=  i:%d",
        count, dir->path, dircount, i);

  TRACE("icon list:%d, path:%s, dir->count_total:%d, dircount2:%d, i:%d",
         count, dir->path, dircount, dircount2, i);
}
// ----------------------------------------------------------------------------
void ico_list_sort(GList **icos, gint sortmode)
{
  g_assert(icos != NULL);
  switch(sortmode)
  {
    case ICONSEL_SORT_NAME:
      *icos = g_list_sort(*icos, (GCompareFunc)ico_compare_by_name);
      break;
    case ICONSEL_SORT_PATH:
      *icos = g_list_sort(*icos, (GCompareFunc)ico_compare_by_path);
      break;
    default:
      ERR("sortmode:%d invalid", sortmode);
  }
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
icon_list_t * icon_list_new(icon_selection_t *iconsel)
{
  icon_list_t *icons;
  TRACE("%s", "");
  g_assert(iconsel != NULL);
  icons = (icon_list_t *) malloc(sizeof(icon_list_t));
  g_assert(icons != NULL);
  icons->iconsel = iconsel;
  icons->dirs = NULL;
  icons->icos = NULL;
  icons->sortmode = ICONSEL_SORT_NAME;
  icons->pixbuf = NULL;
  icons->selected_icon = NULL;
  icons->icon_per_row = 10;
  scandir_context.icons = icons;
  return icons;
}
// ----------------------------------------------------------------------------
void icon_list_free(icon_list_t *icons)
{
  GList    *hamster;
  icodir_t *dir;
  ico_t    *ico;
  g_assert(icons != NULL);
  TRACE("%s", "");

  // free icon list
  hamster = g_list_first(icons->icos);
  while (hamster != NULL)
  {
    ico = hamster->data;
    ico_free(ico);
    hamster = g_list_next(hamster);
  }
  g_list_free(icons->icos);

  // free dir list
  hamster = g_list_first(icons->dirs);
  while (hamster != NULL)
  {
    dir = hamster->data;
    icodir_free(dir);
    hamster = g_list_next(hamster);
  }
  g_list_free(icons->dirs);

  if (icons->pixbuf)
    g_object_unref(icons->pixbuf);

  g_free(icons);
}
// ----------------------------------------------------------------------------
void icon_list_set_width(icon_list_t *icons, int icon_per_row)
{
  g_assert((icons != NULL) || (icon_per_row <= 0));
  icons->icon_per_row = icon_per_row;
} 
// ----------------------------------------------------------------------------
void icon_list_append_children(icon_list_t *icons, icodir_t *dir)
{
  struct dirent **namelist;
  char            dirname[FILENAME_MAX];
  int             n;
  icodir_t       *childdir;
  g_assert(icons != NULL && dir != NULL);
  g_assert(dir->recursive == TRUE);

  TRACE("'%s'", dir->path);
  splash_update(icons->iconsel->apwal->splash);

  n = scandir(dir->path, &namelist, icon_list_scandir_path, alphasort);
  if (n < 0)
  {
    WARN("scandir:%d, errno:%d", n, errno);
    return;
  }

  while(n--)
  {
    // buid the abolute path of the child directory
    strcpy(dirname, dir->path);
    strcat(dirname, "/");
    strcat(dirname, namelist[n]->d_name);
    
    // check if the child directory already exist
    // if the child do not already exist then create it and follow the child,
    // if the child already exist then do nothing end of the branch here.
    childdir = icodir_list_find(icons->dirs, dirname);
    if (childdir == NULL)
    {
      childdir = icodir_new_child(g_strdup(dirname), dir->selected,
                                  dir->recursive, dir);
      icons->dirs = g_list_insert_sorted(icons->dirs, childdir,
                                         (GCompareFunc) icodir_compare);
      ico_list_append(&icons->icos, childdir, icons);
      icon_list_append_children(icons, childdir);
    }
    else
    {
      if (childdir->parent == dir)
      {
        TRACE("'%s' exist and it is our child, reload it", dirname);
        ico_list_append(&icons->icos, childdir, icons);
        icon_list_append_children(icons, childdir);
      }
      else
        TRACE("'%s' exist and we are not his parent, stop this branch here",
              dirname);
    }
    free(namelist[n]);
  }// end while n--
  free(namelist);
}
// ----------------------------------------------------------------------------
void icon_list_append(icon_list_t *icons, char *dirname,
                      gboolean selected, gboolean recursive)
{
  icodir_t *dir;
  g_assert(icons != NULL && dirname != NULL);

  TRACE("'%s' selected:%d recursive:%d", dirname, selected, recursive);
  splash_update(icons->iconsel->apwal->splash);

  dir = icodir_list_find(icons->dirs, dirname);
  if (dir != NULL)
  {
    if (dir->parent != NULL)
      icon_list_fork(icons, dir, selected, recursive);
    else
      ERR("dirname:%s already exist", dirname);
  }
  else
  {
    dir = icodir_new(g_strdup(dirname), selected, recursive);
    icons->dirs = g_list_insert_sorted(icons->dirs, dir,
                                       (GCompareFunc) icodir_compare);
    ico_list_append(&icons->icos, dir, icons);

    if (dir->recursive == TRUE)
      icon_list_append_children(icons, dir);
  }
  ico_list_sort(&icons->icos, icons->sortmode);
}
// ----------------------------------------------------------------------------
void icon_list_reload(icon_list_t *icons, char *dirname)
{
  icodir_t *dir;
  g_assert(icons != NULL && dirname != NULL);

  TRACE("reload '%s'", dirname);

  dir = icodir_list_find(icons->dirs, dirname);
  if (dir == NULL)
    ERR("dirname:%s not found", dirname);
  if (dir->parent != NULL)
    ERR("dirname:%s is not a root of a dir tree", dirname);

  ico_list_append(&icons->icos, dir, icons);

  if (dir->recursive == TRUE)
    icon_list_append_children(icons, dir);
  ico_list_sort(&icons->icos, icons->sortmode);
}
// ----------------------------------------------------------------------------
void icon_list_reload_all_with_splash(icon_list_t *icons)
{
  g_assert(icons != NULL);
  splash_show(icons->iconsel->apwal->splash);
  icon_list_reload_all(icons);
  splash_hide(icons->iconsel->apwal->splash);
}
// ----------------------------------------------------------------------------
void icon_list_reload_all(icon_list_t *icons)
{
  GList *hamster;
  icon_dir_t *icon_dir;
  g_assert(icons != NULL);
  // use a little trick here. As the dir list is suspectible to change during
  // the loop, use the list of dirs owned by iconsel for the loop (which will
  // not change).
  hamster = g_list_first(icons->iconsel->apwal->iconsel_pref->icon_dirs);
  while (hamster != NULL)
  {
    icon_dir = (icon_dir_t*) hamster->data;
    icon_list_reload(icons, icon_dir->path);
    hamster = g_list_next(hamster);
  }
}
// ----------------------------------------------------------------------------
void icon_list_remove_children(icon_list_t *icons, icodir_t *dir)
{
  GList *hamster;
  icodir_t *childdir;
  g_assert(icons != NULL && dir != NULL);
  g_assert(dir->recursive == TRUE);
  
  if (dir->children == NULL)
    return;

  TRACE("%s", dir->path);
  //hamster = icodir_list_find_with_parent(dir->children, dir);
  hamster = g_list_first(dir->children);
  while (hamster != NULL)
  {
    childdir = hamster->data;
    TRACE("parent:%p, childdir:%p", dir, childdir);
    icon_list_remove_children(icons, childdir);
    ico_list_remove(&icons->icos, childdir, icons);
    icons->dirs = g_list_remove(icons->dirs, childdir);
    hamster = g_list_next(hamster);
    icodir_free(childdir);
    //hamster = icodir_list_find_with_parent(dir->children, dir);
  }
  g_list_free(dir->children);
  dir->children = NULL;
}
// ----------------------------------------------------------------------------
void icon_list_remove(icon_list_t *icons, char *dirname)
{
  icodir_t *dir;
  g_assert(icons != NULL && dirname != NULL);

  TRACE("'%s'", dirname);

  dir = icodir_list_find(icons->dirs, dirname);
  if (dir == NULL)
    ERR("dirname:%s not exist", dirname);

  if (dir->recursive == TRUE)
    icon_list_remove_children(icons, dir);
  ico_list_remove(&icons->icos, dir, icons);
  icons->dirs = g_list_remove(icons->dirs, dir);
  icodir_free(dir);
}
// ----------------------------------------------------------------------------
void icon_list_modify_with_splash(icon_list_t *icons, char *dirname, char *newdirname)
{
  g_assert(icons != NULL);
  splash_show(icons->iconsel->apwal->splash);
  icon_list_modify(icons, dirname, newdirname);
  splash_hide(icons->iconsel->apwal->splash);
}
// ----------------------------------------------------------------------------
void icon_list_modify(icon_list_t *icons, char *dirname, char *newdirname)
{
  icodir_t *dir;
  gboolean  selected, recursive;
  g_assert(icons != NULL && dirname != NULL && newdirname != NULL);

  TRACE("old:'%s', new:'%s'", dirname, newdirname);
  dir = icodir_list_find(icons->dirs, dirname);
  if (dir == NULL)
    ERR("dirname:%s not exist, newdirname:%s",
            dirname, newdirname);

  recursive = dir->recursive;
  selected = dir->selected;

  icon_list_remove(icons, dirname);
  icon_list_append(icons, newdirname, selected, recursive);
}
// ----------------------------------------------------------------------------
void icon_list_fork(icon_list_t *icons, icodir_t *dir,
                    gboolean selected, gboolean recursive)
{
  gint len1, len2;
  g_assert(icons != NULL && dir != NULL);
  g_assert(dir->parent != NULL);
  TRACE("'%s'", dir->path);
  
  // remove link with parent. you are now a grown up people my child...
  len1 = g_list_length(dir->parent->children);
  dir->parent->children = g_list_remove(dir->parent->children, dir);
  len2 = g_list_length(dir->parent->children);
  if (len2 != len1 - 1)
    ERR("remove from dir->parent->children, dir:%s, parent:%s, "
        "len1:%d len2:%d", dir->path, dir->parent->path, len1, len2);
  dir->parent = NULL;
  
  // set recursive and selected properties
  if (dir->recursive != recursive)
    icon_list_set_recursive(icons, dir->path, recursive);
  if (dir->selected != selected)
    icon_list_set_selected(icons, dir->path, selected);
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void icon_list_set_selected_children(icon_list_t *icons, icodir_t *dir,
                                     gboolean selected)
{
  GList    *hamster;
  icodir_t *childdir;
  g_assert(icons != NULL && dir != NULL);
  TRACE("dir->selected:%d, selected:%d", dir->selected, selected);

  hamster = g_list_first(dir->children);
  while (hamster)
  {
    childdir = hamster->data;
    childdir->selected = selected;
    icon_list_set_selected_children(icons, childdir, selected);
    hamster = g_list_next(hamster);
  }
}
// ----------------------------------------------------------------------------
void icon_list_set_selected(icon_list_t *icons, char *dirname,
                            gboolean selected)
{
  icodir_t *dir;
  g_assert(icons != NULL && dirname != NULL);

  TRACE("%s", "");
  dir = icodir_list_find(icons->dirs, dirname);
  if (dir == NULL)
    ERR("dirname:%s not exist", dirname);
  if (dir->selected == selected)
    ERR("dirname:%s, dir->selected:%d == selected:%d",
        dirname, dir->selected, selected);
  
  dir->selected = selected;
  if (dir->recursive == TRUE)
    icon_list_set_selected_children(icons, dir, selected);

}
// ----------------------------------------------------------------------------
void icon_list_set_recursive_with_splash(icon_list_t *icons, char *dirname,
                                         gboolean recursive)
{
  g_assert(icons != NULL);
  splash_show(icons->iconsel->apwal->splash);
  icon_list_set_recursive(icons, dirname, recursive);
  splash_hide(icons->iconsel->apwal->splash);
}
// ----------------------------------------------------------------------------
void icon_list_set_recursive(icon_list_t *icons, char *dirname,
                             gboolean recursive)
{
  icodir_t *dir;
  g_assert(icons != NULL && dirname != NULL);

  dir = icodir_list_find(icons->dirs, dirname);
  if (dir == NULL)
    ERR("dirname:%s not exist", dirname);
  if (dir->recursive == recursive)
    ERR("dirname:%s, dir->recursive:%d == recursive:%d",
            dirname, dir->recursive, recursive);

  if (recursive == TRUE)
  {
    dir->recursive = recursive;
    icon_list_append_children(icons, dir);
  }
  else
  {
    icon_list_remove_children(icons, dir);
    dir->recursive = recursive;
  }
}
// ----------------------------------------------------------------------------
void icon_list_select (icon_list_t *icons,
                       gboolean (*select_func)(ico_t *ico, void *data),
                       void *data)
{
  GList    *hamster;
  ico_t    *ico;
  icodir_t *dir;
  g_assert(icons != NULL && select_func != NULL);

  hamster = g_list_first(icons->dirs);
  while (hamster != NULL)
  {
    dir = hamster->data;
    dir->count_selected = 0;
    hamster = g_list_next(hamster);
  }

  hamster = g_list_first(icons->icos);
  while (hamster != NULL)
  {
    ico = hamster->data;
    if (ico->dir->selected == FALSE)
      ico->selected = FALSE;
    else
      ico->selected = select_func(ico, data);
    if (ico->selected == TRUE)
      ico->dir->count_selected++;
    hamster = g_list_next(hamster);
  }
}
// ----------------------------------------------------------------------------
ico_t *icon_list_find(icon_list_t *icons, char *path, char *name)
{
  GList    *hamster;
  ico_t    *ico;
  icodir_t *dir;
  g_assert(icons != NULL && path != NULL && name != NULL);

  dir = icodir_list_find(icons->dirs, path);
  if (dir == NULL)
    return NULL;

  hamster = g_list_first(icons->icos);
  while (hamster != NULL)
  {
    ico = hamster->data;
    if (ico->dir == dir)
      if (strcmp(ico->name, name) == 0)
        break;
    hamster = g_list_next(hamster);
  }
  if (hamster == NULL)
    return NULL;
  return ico;
}
// ----------------------------------------------------------------------------
icodir_t *icon_list_find_path(icon_list_t *icons, char *path)
{
  g_assert(icons != NULL && path != NULL);
  return icodir_list_find(icons->dirs, path);
}
// ----------------------------------------------------------------------------
void icon_list_set_sortmode(icon_list_t *icons, int sortmode)
{
  g_assert(icons != NULL);
  icons->sortmode = sortmode;
  ico_list_sort(&icons->icos, icons->sortmode);
}
// ----------------------------------------------------------------------------
gint icon_list_selected_count(icon_list_t *icons)
{
  ico_t *ico;
  GList *hamster;
  int    count;
  g_assert(icons != NULL);
  count = 0;
  hamster = g_list_first(icons->icos);
  while (hamster != NULL)
  {
    ico = hamster->data;
    if (ico->selected)
      count++;
    hamster = g_list_next(hamster);
  }
  return count;
}
// ----------------------------------------------------------------------------
ico_t *icon_list_selected_nth(icon_list_t *icons, int nth)
{
  ico_t *ico;
  GList *hamster;
  int    i;
  g_assert(icons != NULL);
  i = 0;
  hamster = g_list_first(icons->icos);
  while (hamster != NULL && i != nth)
  {
    ico = hamster->data;
    if (ico->selected == TRUE)
      i++;
    hamster = g_list_next(hamster);
  }
  if (hamster == NULL)
    return NULL;
  return ico;
}
// ----------------------------------------------------------------------------
GdkPixbuf *icon_list_pixbuf(icon_list_t *icons)
{
  GList  *hamster;
  gint    i, count;
  gint    width;
  gint    height;
  ico_t  *ico;
  gint    index_of_selected;
  g_assert(icons != NULL);

  count = icon_list_selected_count(icons);
  width = icons->icon_per_row  * 48;
  height = ((count / icons->icon_per_row) + 1) * 48;
  
  if (icons->pixbuf != NULL)
  {
    if ( ! (gdk_pixbuf_get_width(icons->pixbuf) == width &&
            gdk_pixbuf_get_height(icons->pixbuf) == height))
    {
      g_object_unref(icons->pixbuf);
      icons->pixbuf = NULL;
    }
  }
  if (icons->pixbuf == NULL)
    icons->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB/*colorspace*/,
                                   TRUE/*has_alpha*/, 8/*bits_per_sample*/,
                                   width, height);
  gdk_pixbuf_fill(icons->pixbuf, 0x00000000);

  i = 0;
  index_of_selected = -1;
  hamster = g_list_first(icons->icos);
  while (hamster)
  {
    ico =  hamster->data;
    if (ico->selected == TRUE)
    {
      // be carful here, width and height are use but for icon pixbuf now
      width = gdk_pixbuf_get_width(ico->pixbuf);
      height = gdk_pixbuf_get_height(ico->pixbuf);
      width = width < 48  ? width : 48;
      height= height< 48 ? height: 48;
      gdk_pixbuf_copy_area(ico->pixbuf/*src_pixbuf*/,
                           0/*src_x*/, 0/*src_y*/, width, height, icons->pixbuf,
                           (i % icons->icon_per_row) * 48/*x*/,
                           (i / icons->icon_per_row) * 48/*y*/);
      if (icons->selected_icon == ico)
        index_of_selected = i;
      i++;
    }
    hamster = g_list_next(hamster);
  } // end while hamster
  
  if (i != count)
    ERR("i:%d, count:%d", i, count);
  TRACE("i:%d, count:%d", i, count);

  return icons->pixbuf;
}
// ----------------------------------------------------------------------------
void icon_list_set_selected_icon_from_xy(icon_list_t *icons, int x, int y)
{
  int nth;
  g_assert(icons != NULL);

  nth = (y * icons->icon_per_row) + x + 1/*first nth is 1 and not 0*/;
  icons->selected_icon = icon_list_selected_nth(icons, nth);
  if (icons->selected_icon == NULL)
    TRACE("%s", "no icon selected");
  else
    TRACE("name:%s", icons->selected_icon->name);
}
// ----------------------------------------------------------------------------
void icon_list_set_selected_icon_from_filename(icon_list_t *icons,
                                               const char *file)
{
  char *dirname, *iconame;
  g_assert(icons != NULL);
  if (file == NULL)
    icons->selected_icon = NULL;
  else
  {
    dirname = g_path_get_dirname(file);
    iconame = g_path_get_basename(file);
    icons->selected_icon = icon_list_find(icons, dirname, iconame);
    g_free(dirname);
    g_free(iconame);
  }
  if (icons->selected_icon == NULL)
    TRACE("NULL selected, filename:%s not found", file);
  else
    TRACE("name:%s", icons->selected_icon->name);
}
// ----------------------------------------------------------------------------
void icon_list_set_selected_icon(icon_list_t *icons, ico_t *ico)
{
  g_assert(icons != NULL);
  icons->selected_icon = ico;
}
// ----------------------------------------------------------------------------
ico_t *icon_list_get_selected_icon(icon_list_t *icons)
{
  g_assert(icons != NULL);
  return icons->selected_icon;
}
// ----------------------------------------------------------------------------
gchar *icon_list_get_selected_icon_filename(icon_list_t *icons)
{
  gchar *filename;
  g_assert(icons != NULL);
  if (icons->selected_icon == NULL)
  {
    TRACE("%s", "icon_list_get_selected_icon_filename() no icon selected");
    return NULL;
  }
  filename = ico_get_full_filename(icons->selected_icon);
  TRACE("icon_list_get_selected_icon_filename() file:%s", filename);
  return filename;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
//  = 0 : not selected
// != 0 : selected, sorted and allocated in namelist by scandir
// do many check, if the check failed then return 0, else go to the next check.
// arrive at the end, return 1 because the file success all the tests.
static int icon_list_scandir_icon(const struct dirent *de)
{
  //icon_list_t *icons = scandir_context.icons;
  g_assert(de != NULL);

  // do not select . and .. directories
  if ((!strcmp(".", de->d_name)) || (!strcmp("..", de->d_name)))
  {
    //TRACE("ico_select() 1 de->d_name:%s", de->d_name);
    return 0;
  }
  // if file is a directory, then don't select it
  if (de->d_type == DT_DIR)
  {
    //TRACE("ico_select() 2 de->d_name:%s", de->d_name);
    return 0;
  }

  // if not return before here, then the filename is ok. return 1.
  return 1;
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
//  = 0 : not selected
// != 0 : selected, sorted and allocated in namelist by scandir
static int icon_list_scandir_path(const struct dirent *de)
{
  g_assert(de != NULL);

  // not select . & ..
  if ((!strcmp(".", de->d_name)) || (!strcmp("..", de->d_name)))
  {
    return 0;
  }
  // select directories only
  if (de->d_type != DT_DIR)
  {
    return 0;
  }
  //de->d_type;
  //de->d_name;
  return 1;
}
// ----------------------------------------------------------------------------
void icon_list_print_not_selected(icon_list_t *icons)  //debug stuff
{
  GList *hamster;
  ico_t *ico;
  hamster = icons->icos;
  while (hamster != NULL)
  {
    ico = hamster->data;
    if (ico->selected == FALSE)
      TRACE("!selected: %dx%d, name:%s, dir:%s", ico->width, ico->height, ico->name, ico->dir->path);
    hamster = g_list_next(hamster);
  }
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
