//
// Fl_File_Icon system icon routines.
//
// KDE icon code donated by Maarten De Boer.
//
// Copyright 1999-2010 by Michael Sweet.
// Copyright 2011-2019 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
//     https://www.fltk.org/bugs.php
//
// Contents:
//
//   Fl_File_Icon::load()              - Load an icon file...
//   Fl_File_Icon::load_fti()          - Load an SGI-format FTI file...
//   Fl_File_Icon::load_image()        - Load an image icon file...
//   Fl_File_Icon::load_system_icons() - Load the standard system icons/filetypes.
//   load_kde_icons()                  - Load KDE icon files.
//   load_kde_mimelnk()                - Load a KDE "mimelnk" file.
//   kde_to_fltk_pattern()             - Convert a KDE pattern to a FLTK pattern.
//   get_kde_val()                     - Get a KDE value.
//

//
// Include necessary header files...
//

#include <stdio.h>
#include <stdlib.h>
#include <FL/fl_utf8.h>
#include "flstring.h"
#include <ctype.h>
#include <errno.h>
#include <FL/math.h>
#include <FL/Fl_File_Icon.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_Widget.H>
#include <FL/fl_draw.H>
#include <FL/filename.H>
#ifndef F_OK
#  define F_OK  0
#endif

//
// Local functions...
//

static void     load_kde_icons(const char *directory, const char *icondir);
static void     load_kde_mimelnk(const char *filename, const char *icondir);
static char     *kde_to_fltk_pattern(const char *kdepattern);
static char     *get_kde_val(char *str, const char *key);


//
// Local globals...
//

static const char *kdedir = NULL;


/**
  Loads the specified icon image. The format is deduced from the filename.
  \param[in] f filename
*/
void
Fl_File_Icon::load(const char *f)       // I - File to read from
{
  int           i;                      // Load status...
  const char    *ext;                   // File extension


  ext = fl_filename_ext(f);

  if (ext && strcmp(ext, ".fti") == 0)
    i = load_fti(f);
  else
    i = load_image(f);

  if (i)
  {
    Fl::warning("Fl_File_Icon::load(): Unable to load icon file \"%s\".", f);
    return;
  }
}


/**
  Loads an SGI icon file.
  \param[in] fti icon filename
  \return 0 on success, non-zero on error
*/
int                                     // O - 0 on success, non-zero on error
Fl_File_Icon::load_fti(const char *fti) // I - File to read from
{
  FILE  *fp;                    // File pointer
  int   ch;                     // Current character
  char  command[255],           // Command string ("vertex", etc.)
        params[255],            // Parameter string ("10.0,20.0", etc.)
        *ptr;                   // Pointer into strings
  int   outline;                // Outline polygon


  // Try to open the file...
  if ((fp = fl_fopen(fti, "rb")) == NULL)
  {
    Fl::error("Fl_File_Icon::load_fti(): Unable to open \"%s\" - %s",
              fti, strerror(errno));
    return -1;
  }

  // Read the entire file, adding data as needed...
  outline = 0;

  while ((ch = getc(fp)) != EOF)
  {
    // Skip whitespace
    if (isspace(ch))
      continue;

    // Skip comments starting with "#"...
    if (ch == '#')
    {
      while ((ch = getc(fp)) != EOF)
        if (ch == '\n')
          break;

      if (ch == EOF)
        break;
      else
        continue;
    }

    // OK, this character better be a letter...
    if (!isalpha(ch))
    {
      Fl::error("Fl_File_Icon::load_fti(): Expected a letter at file position %ld (saw '%c')",
                ftell(fp) - 1, ch);
      break;
    }

    // Scan the command name...
    ptr    = command;
    *ptr++ = ch;

    while ((ch = getc(fp)) != EOF)
    {
      if (ch == '(')
        break;
      else if (ptr < (command + sizeof(command) - 1))
        *ptr++ = ch;
    }

    *ptr++ = '\0';

    // Make sure we stopped on a parenthesis...
    if (ch != '(')
    {
      Fl::error("Fl_File_Icon::load_fti(): Expected a ( at file position %ld (saw '%c')",
                ftell(fp) - 1, ch);
      break;
    }

    // Scan the parameters...
    ptr = params;

    while ((ch = getc(fp)) != EOF)
    {
      if (ch == ')')
        break;
      else if (ptr < (params + sizeof(params) - 1))
        *ptr++ = ch;
    }

    *ptr++ = '\0';

    // Make sure we stopped on a parenthesis...
    if (ch != ')')
    {
      Fl::error("Fl_File_Icon::load_fti(): Expected a ) at file position %ld (saw '%c')",
                ftell(fp) - 1, ch);
      break;
    }

    // Make sure the next character is a semicolon...
    if ((ch = getc(fp)) != ';')
    {
      Fl::error("Fl_File_Icon::load_fti(): Expected a ; at file position %ld (saw '%c')",
                ftell(fp) - 1, ch);
      break;
    }

    // Now process the command...
    if (strcmp(command, "color") == 0)
    {
      // Set the color; for negative colors blend the two primaries to
      // produce a composite color.  Also, the following symbolic color
      // names are understood:
      //
      //     name           FLTK color
      //     -------------  ----------
      //     iconcolor      FL_ICON_COLOR; mapped to the icon color in
      //                    Fl_File_Icon::draw()
      //     shadowcolor    FL_DARK3
      //     outlinecolor   FL_BLACK
      if (strcmp(params, "iconcolor") == 0)
        add_color(FL_ICON_COLOR);
      else if (strcmp(params, "shadowcolor") == 0)
        add_color(FL_DARK3);
      else if (strcmp(params, "outlinecolor") == 0)
        add_color(FL_BLACK);
      else
      {
        int c = atoi(params);   // Color value


        if (c < 0)
        {
          // Composite color; compute average...
          c = -c;
          add_color(fl_color_average((Fl_Color)(c >> 4),
                                     (Fl_Color)(c & 15), 0.5f));
        }
        else
          add_color((Fl_Color)c);
      }
    }
    else if (strcmp(command, "bgnline") == 0)
      add(LINE);
    else if (strcmp(command, "bgnclosedline") == 0)
      add(CLOSEDLINE);
    else if (strcmp(command, "bgnpolygon") == 0)
      add(POLYGON);
    else if (strcmp(command, "bgnoutlinepolygon") == 0)
    {
      add(OUTLINEPOLYGON);
      outline = int(add(0) - data_);
      add(0);
    }
    else if (strcmp(command, "endoutlinepolygon") == 0 && outline)
    {
      unsigned cval; // Color value

      // Set the outline color; see above for valid values...
      if (strcmp(params, "iconcolor") == 0)
        cval = FL_ICON_COLOR;
      else if (strcmp(params, "shadowcolor") == 0)
        cval = FL_DARK3;
      else if (strcmp(params, "outlinecolor") == 0)
        cval = FL_BLACK;
      else
      {
        int c = atoi(params);   // Color value


        if (c < 0)
        {
          // Composite color; compute average...
          c = -c;
          cval = fl_color_average((Fl_Color)(c >> 4), (Fl_Color)(c & 15), 0.5f);
        }
        else
          cval = c;
      }

      // Store outline color...
      data_[outline]     = cval >> 16;
      data_[outline + 1] = cval;

      outline = 0;
      add(END);
    }
    else if (strncmp(command, "end", 3) == 0)
      add(END);
    else if (strcmp(command, "vertex") == 0)
    {
      float x, y;               // Coordinates of vertex


      if (sscanf(params, "%f,%f", &x, &y) != 2)
        break;

      add_vertex((short)(int)rint(x * 100.0), (short)(int)rint(y * 100.0));
    }
    else
    {
      Fl::error("Fl_File_Icon::load_fti(): Unknown command \"%s\" at file position %ld.",
                command, ftell(fp) - 1);
      break;
    }
  }

  // Close the file and return...
  fclose(fp);

#ifdef DEBUG
  printf("Icon File \"%s\":\n", fti);
  for (int i = 0; i < num_data_; i ++)
    printf("    %d,\n", data_[i]);
#endif /* DEBUG */

  return 0;
}


/**
  Load an image icon file from an image filename.
  \param[in] ifile image filename
  \return 0 on success, non-zero on error
*/
int Fl_File_Icon::load_image(const char *ifile) // I - File to read from
{
  Fl_Shared_Image       *img;           // Image file


  img = Fl_Shared_Image::get(ifile);
  if (!img || !img->count() || !img->w() || !img->h()) return -1;

  if (img->count() == 1) {
    int         x, y;           // X & Y in image
    int         startx;         // Starting X coord
    Fl_Color    c,              // Current color
                temp;           // Temporary color
    const uchar *row;           // Pointer into image

    const int extra_data = img->ld() ? (img->ld()-img->w()*img->d()) : 0;

    // Loop through grayscale or RGB image...
    for (y = 0, row = (const uchar *)(*(img->data())); y < img->h(); y ++, row += extra_data)
    {
      for (x = 0, startx = 0, c = (Fl_Color)-1;
           x < img->w();
           x ++, row += img->d())
      {
        switch (img->d())
        {
          case 1 :
              temp = fl_rgb_color(row[0], row[0], row[0]);
              break;
          case 2 :
              if (row[1] > 127)
                temp = fl_rgb_color(row[0], row[0], row[0]);
              else
                temp = (Fl_Color)-1;
              break;
          case 3 :
              temp = fl_rgb_color(row[0], row[1], row[2]);
              break;
          default :
              if (row[3] > 127)
                temp = fl_rgb_color(row[0], row[1], row[2]);
              else
                temp = (Fl_Color)-1;
              break;
        }

        if (temp != c)
        {
          if (x > startx && c != (Fl_Color)-1)
          {
            add_color(c);
            add(POLYGON);
            add_vertex(startx * 9000 / img->w() + 1000, 9500 - y * 9000 / img->h());
            add_vertex(x * 9000 / img->w() + 1000,      9500 - y * 9000 / img->h());
            add_vertex(x * 9000 / img->w() + 1000,      9500 - (y + 1) * 9000 / img->h());
            add_vertex(startx * 9000 / img->w() + 1000, 9500 - (y + 1) * 9000 / img->h());
            add(END);
          }

          c      = temp;
          startx = x;
        }
      }

      if (x > startx && c != (Fl_Color)-1)
      {
        add_color(c);
        add(POLYGON);
        add_vertex(startx * 9000 / img->w() + 1000, 9500 - y * 9000 / img->h());
        add_vertex(x * 9000 / img->w() + 1000,      9500 - y * 9000 / img->h());
        add_vertex(x * 9000 / img->w() + 1000,      9500 - (y + 1) * 9000 / img->h());
        add_vertex(startx * 9000 / img->w() + 1000, 9500 - (y + 1) * 9000 / img->h());
        add(END);
      }
    }
  } else {
    int         i, j;                   // Looping vars
    int         ch;                     // Current character
    int         newch;                  // New character
    int         bg;                     // Background color
    char        val[16];                // Color value
    const char  *lineptr,               // Pointer into line
                *const*ptr;             // Pointer into data array
    int         ncolors,                // Number of colors
                chars_per_color;        // Characters per color
    Fl_Color    *colors;                // Colors
    int         red, green, blue;       // Red, green, and blue values
    int         x, y;                   // X & Y in image
    int         startx;                 // Starting X coord

    // Get the pixmap data...
    ptr = img->data();
    sscanf(*ptr, "%*d%*d%d%d", &ncolors, &chars_per_color);

    colors = new Fl_Color[int(1 << (chars_per_color * 8))];

    // Read the colormap...
    memset(colors, 0, sizeof(Fl_Color) << (chars_per_color * 8));
    bg = ' ';

    ptr ++;

    if (ncolors < 0) {
      // Read compressed colormap...
      const uchar *cmapptr;

      ncolors = -ncolors;

      for (i = 0, cmapptr = (const uchar *)*ptr; i < ncolors; i ++, cmapptr += 4)
        colors[cmapptr[0]] = fl_rgb_color(cmapptr[1], cmapptr[2], cmapptr[3]);

      ptr ++;
    } else {
      for (i = 0; i < ncolors; i ++, ptr ++) {
        // Get the color's character
        lineptr = *ptr;
        ch      = *lineptr++;

        if (chars_per_color > 1) ch = (ch << 8) | *lineptr++;

        // Get the color value...
        if ((lineptr = strstr(lineptr, "c ")) == NULL) {
          // No color; make this black...
          colors[ch] = FL_BLACK;
        } else if (lineptr[2] == '#') {
          // Read the RGB triplet...
          lineptr += 3;
          for (j = 0; j < 12; j ++)
            if (!isxdigit(lineptr[j]))
              break;

          switch (j) {
            case 0 :
                bg = ch;
            default :
                red = green = blue = 0;
                break;

            case 3 :
                val[0] = lineptr[0];
                val[1] = '\0';
                red = 255 * (int)strtol(val, NULL, 16) / 15;

                val[0] = lineptr[1];
                val[1] = '\0';
                green = 255 * (int)strtol(val, NULL, 16) / 15;

                val[0] = lineptr[2];
                val[1] = '\0';
                blue = 255 * (int)strtol(val, NULL, 16) / 15;
                break;

            case 6 :
            case 9 :
            case 12 :
                j /= 3;

                val[0] = lineptr[0];
                val[1] = lineptr[1];
                val[2] = '\0';
                red = (int)strtol(val, NULL, 16);

                val[0] = lineptr[j + 0];
                val[1] = lineptr[j + 1];
                val[2] = '\0';
                green = (int)strtol(val, NULL, 16);

                val[0] = lineptr[2 * j + 0];
                val[1] = lineptr[2 * j + 1];
                val[2] = '\0';
                blue = (int)strtol(val, NULL, 16);
                break;
          }

          colors[ch] = fl_rgb_color((uchar)red, (uchar)green, (uchar)blue);
        } else {
          // Read a color name...
          if (strncasecmp(lineptr + 2, "white", 5) == 0) colors[ch] = FL_WHITE;
          else if (strncasecmp(lineptr + 2, "black", 5) == 0) colors[ch] = FL_BLACK;
          else if (strncasecmp(lineptr + 2, "none", 4) == 0) {
            colors[ch] = FL_BLACK;
            bg = ch;
          } else colors[ch] = FL_GRAY;
        }
      }
    }

    // Read the image data...
    for (y = 0; y < img->h(); y ++, ptr ++) {
      lineptr = *ptr;
      startx  = 0;
      ch      = bg;

      for (x = 0; x < img->w(); x ++) {
        newch = *lineptr++;

        if (chars_per_color > 1) newch = (newch << 8) | *lineptr++;

        if (newch != ch) {
          if (ch != bg) {
            add_color(colors[ch]);
            add(POLYGON);
            add_vertex(startx * 9000 / img->w() + 1000, 9500 - y * 9000 / img->h());
            add_vertex(x * 9000 / img->w() + 1000,      9500 - y * 9000 / img->h());
            add_vertex(x * 9000 / img->w() + 1000,      9500 - (y + 1) * 9000 / img->h());
            add_vertex(startx * 9000 / img->w() + 1000, 9500 - (y + 1) * 9000 / img->h());
            add(END);
          }

          ch     = newch;
          startx = x;
        }
      }

      if (ch != bg) {
        add_color(colors[ch]);
        add(POLYGON);
        add_vertex(startx * 9000 / img->w() + 1000, 9500 - y * 9000 / img->h());
        add_vertex(x * 9000 / img->w() + 1000,      9500 - y * 9000 / img->h());
        add_vertex(x * 9000 / img->w() + 1000,      9500 - (y + 1) * 9000 / img->h());
        add_vertex(startx * 9000 / img->w() + 1000, 9500 - (y + 1) * 9000 / img->h());
        add(END);
      }
    }

    // Free the colormap...
    delete[] colors;
  }

  img->release();

#ifdef DEBUG
{
  int i;
  printf("Icon File \"%s\":\n", ifile);
  for (i = 0; i < num_data_; i ++)
  {
    printf("    %d,\n", data_[i]);
  }
}
#endif // DEBUG

  return 0;
}


/**
  Loads all system-defined icons. This call is useful when using the
  FileChooser widget and should be used when the application starts:

  \code
  Fl_File_Icon::load_system_icons();
  \endcode
*/
void
Fl_File_Icon::load_system_icons(void) {
  int           i;                              // Looping var
  Fl_File_Icon  *icon;                          // New icons
  char          filename[FL_PATH_MAX + 60];     // Filename
  char          icondir[FL_PATH_MAX];           // Icon directory
  static int    init = 0;       // Have the icons been initialized?
  const char * const icondirs[] = {
                  "Bluecurve",  // Icon directories to look for, in order
                  "crystalsvg",
                  "default.kde",
                  "hicolor",
                  NULL
                };
  static short  plain[] = {     // Plain file icon
                  COLOR, -1, -1, OUTLINEPOLYGON, 0, FL_GRAY,
                  VERTEX, 2000, 1000, VERTEX, 2000, 9000,
                  VERTEX, 6000, 9000, VERTEX, 8000, 7000,
                  VERTEX, 8000, 1000, END, OUTLINEPOLYGON, 0, FL_GRAY,
                  VERTEX, 6000, 9000, VERTEX, 6000, 7000,
                  VERTEX, 8000, 7000, END,
                  COLOR, 0, FL_BLACK, LINE, VERTEX, 6000, 7000,
                  VERTEX, 8000, 7000, VERTEX, 8000, 1000,
                  VERTEX, 2000, 1000, END, LINE, VERTEX, 3000, 7000,
                  VERTEX, 5000, 7000, END, LINE, VERTEX, 3000, 6000,
                  VERTEX, 5000, 6000, END, LINE, VERTEX, 3000, 5000,
                  VERTEX, 7000, 5000, END, LINE, VERTEX, 3000, 4000,
                  VERTEX, 7000, 4000, END, LINE, VERTEX, 3000, 3000,
                  VERTEX, 7000, 3000, END, LINE, VERTEX, 3000, 2000,
                  VERTEX, 7000, 2000, END,
                  END
                };
  static short  image[] = {     // Image file icon
                  COLOR, -1, -1, OUTLINEPOLYGON, 0, FL_GRAY,
                  VERTEX, 2000, 1000, VERTEX, 2000, 9000,
                  VERTEX, 6000, 9000, VERTEX, 8000, 7000,
                  VERTEX, 8000, 1000, END, OUTLINEPOLYGON, 0, FL_GRAY,
                  VERTEX, 6000, 9000, VERTEX, 6000, 7000,
                  VERTEX, 8000, 7000, END,
                  COLOR, 0, FL_BLACK, LINE, VERTEX, 6000, 7000,
                  VERTEX, 8000, 7000, VERTEX, 8000, 1000,
                  VERTEX, 2000, 1000, END,
                  COLOR, 0, FL_RED, POLYGON, VERTEX, 3500, 2500,
                  VERTEX, 3000, 3000, VERTEX, 3000, 4000,
                  VERTEX, 3500, 4500, VERTEX, 4500, 4500,
                  VERTEX, 5000, 4000, VERTEX, 5000, 3000,
                  VERTEX, 4500, 2500, END,
                  COLOR, 0, FL_GREEN, POLYGON, VERTEX, 5500, 2500,
                  VERTEX, 5000, 3000, VERTEX, 5000, 4000,
                  VERTEX, 5500, 4500, VERTEX, 6500, 4500,
                  VERTEX, 7000, 4000, VERTEX, 7000, 3000,
                  VERTEX, 6500, 2500, END,
                  COLOR, 0, FL_BLUE, POLYGON, VERTEX, 4500, 3500,
                  VERTEX, 4000, 4000, VERTEX, 4000, 5000,
                  VERTEX, 4500, 5500, VERTEX, 5500, 5500,
                  VERTEX, 6000, 5000, VERTEX, 6000, 4000,
                  VERTEX, 5500, 3500, END,
                  END
                };
  static short  dir[] = {       // Directory icon
                  COLOR, -1, -1, POLYGON, VERTEX, 1000, 1000,
                  VERTEX, 1000, 7500,  VERTEX, 9000, 7500,
                  VERTEX, 9000, 1000, END,
                  POLYGON, VERTEX, 1000, 7500, VERTEX, 2500, 9000,
                  VERTEX, 5000, 9000, VERTEX, 6500, 7500, END,
                  COLOR, 0, FL_WHITE, LINE, VERTEX, 1500, 1500,
                  VERTEX, 1500, 7000, VERTEX, 9000, 7000, END,
                  COLOR, 0, FL_BLACK, LINE, VERTEX, 9000, 7500,
                  VERTEX, 9000, 1000, VERTEX, 1000, 1000, END,
                  COLOR, 0, FL_GRAY, LINE, VERTEX, 1000, 1000,
                  VERTEX, 1000, 7500, VERTEX, 2500, 9000,
                  VERTEX, 5000, 9000, VERTEX, 6500, 7500,
                  VERTEX, 9000, 7500, END,
                  END
                };


  // Add symbols if they haven't been added already...
  if (!init) {
    // This method requires the images library...
    fl_register_images();

    if (!kdedir) {
      // Figure out where KDE is installed...
      if ((kdedir = fl_getenv("KDEDIR")) == NULL) {
        if (!fl_access("/opt/kde", F_OK)) kdedir = "/opt/kde";
        else if (!fl_access("/usr/local/share/mimelnk", F_OK)) kdedir = "/usr/local";
        else kdedir = "/usr";
      }
    }

    snprintf(filename, sizeof(filename), "%s/share/mimelnk", kdedir);

    if (!fl_access(filename, F_OK)) {
      // Load KDE icons...
      icon = new Fl_File_Icon("*", Fl_File_Icon::PLAIN);

      for (i = 0; icondirs[i]; i ++) {
        snprintf(icondir, sizeof(icondir), "%s/share/icons/%s", kdedir,
                 icondirs[i]);

        if (!fl_access(icondir, F_OK)) break;
      }

      if (icondirs[i]) {
        snprintf(filename, sizeof(filename), "%s/16x16/mimetypes/unknown.png",
                 icondir);
      } else {
        snprintf(filename, sizeof(filename), "%s/share/icons/unknown.xpm",
                 kdedir);
      }

      if (!fl_access(filename, F_OK)) icon->load_image(filename);

      icon = new Fl_File_Icon("*", Fl_File_Icon::LINK);

      snprintf(filename, sizeof(filename), "%s/16x16/filesystems/link.png",
               icondir);

      if (!fl_access(filename, F_OK)) icon->load_image(filename);

      snprintf(filename, sizeof(filename), "%s/share/mimelnk", kdedir);
      load_kde_icons(filename, icondir);
    } else if (!fl_access("/usr/share/icons/folder.xpm", F_OK)) {
      // Load GNOME icons...
      icon = new Fl_File_Icon("*", Fl_File_Icon::PLAIN);
      icon->load_image("/usr/share/icons/page.xpm");

      icon = new Fl_File_Icon("*", Fl_File_Icon::DIRECTORY);
      icon->load_image("/usr/share/icons/folder.xpm");
    } else if (!fl_access("/usr/dt/appconfig/icons", F_OK)) {
      // Load CDE icons...
      icon = new Fl_File_Icon("*", Fl_File_Icon::PLAIN);
      icon->load_image("/usr/dt/appconfig/icons/C/Dtdata.m.pm");

      icon = new Fl_File_Icon("*", Fl_File_Icon::DIRECTORY);
      icon->load_image("/usr/dt/appconfig/icons/C/DtdirB.m.pm");

      icon = new Fl_File_Icon("core", Fl_File_Icon::PLAIN);
      icon->load_image("/usr/dt/appconfig/icons/C/Dtcore.m.pm");

      icon = new Fl_File_Icon("*.{bmp|bw|gif|jpg|pbm|pcd|pgm|ppm|png|ras|rgb|tif|xbm|xpm}", Fl_File_Icon::PLAIN);
      icon->load_image("/usr/dt/appconfig/icons/C/Dtimage.m.pm");

      icon = new Fl_File_Icon("*.{eps|pdf|ps}", Fl_File_Icon::PLAIN);
      icon->load_image("/usr/dt/appconfig/icons/C/Dtps.m.pm");

      icon = new Fl_File_Icon("*.ppd", Fl_File_Icon::PLAIN);
      icon->load_image("/usr/dt/appconfig/icons/C/DtPrtpr.m.pm");
    } else if (!fl_access("/usr/lib/filetype", F_OK)) {
      // Load SGI icons...
      icon = new Fl_File_Icon("*", Fl_File_Icon::PLAIN);
      icon->load_fti("/usr/lib/filetype/iconlib/generic.doc.fti");

      icon = new Fl_File_Icon("*", Fl_File_Icon::DIRECTORY);
      icon->load_fti("/usr/lib/filetype/iconlib/generic.folder.closed.fti");

      icon = new Fl_File_Icon("core", Fl_File_Icon::PLAIN);
      icon->load_fti("/usr/lib/filetype/default/iconlib/CoreFile.fti");

      icon = new Fl_File_Icon("*.{bmp|bw|gif|jpg|pbm|pcd|pgm|ppm|png|ras|rgb|tif|xbm|xpm}", Fl_File_Icon::PLAIN);
      icon->load_fti("/usr/lib/filetype/system/iconlib/ImageFile.fti");

      if (!fl_access("/usr/lib/filetype/install/iconlib/acroread.doc.fti", F_OK)) {
        icon = new Fl_File_Icon("*.{eps|ps}", Fl_File_Icon::PLAIN);
        icon->load_fti("/usr/lib/filetype/system/iconlib/PostScriptFile.closed.fti");

        icon = new Fl_File_Icon("*.pdf", Fl_File_Icon::PLAIN);
        icon->load_fti("/usr/lib/filetype/install/iconlib/acroread.doc.fti");
      } else {
        icon = new Fl_File_Icon("*.{eps|pdf|ps}", Fl_File_Icon::PLAIN);
        icon->load_fti("/usr/lib/filetype/system/iconlib/PostScriptFile.closed.fti");
      }

      if (!fl_access("/usr/lib/filetype/install/iconlib/html.fti", F_OK)) {
        icon = new Fl_File_Icon("*.{htm|html|shtml}", Fl_File_Icon::PLAIN);
        icon->load_fti("/usr/lib/filetype/iconlib/generic.doc.fti");
        icon->load_fti("/usr/lib/filetype/install/iconlib/html.fti");
      }

      if (!fl_access("/usr/lib/filetype/install/iconlib/color.ps.idle.fti", F_OK)) {
        icon = new Fl_File_Icon("*.ppd", Fl_File_Icon::PLAIN);
        icon->load_fti("/usr/lib/filetype/install/iconlib/color.ps.idle.fti");
      }
    } else {
      // Create the default icons...
      new Fl_File_Icon("*", Fl_File_Icon::PLAIN, sizeof(plain) / sizeof(plain[0]), plain);
      new Fl_File_Icon("*.{bm|bmp|bw|gif|jpg|pbm|pcd|pgm|ppm|png|ras|rgb|tif|xbm|xpm}", Fl_File_Icon::PLAIN,
                   sizeof(image) / sizeof(image[0]), image);
      new Fl_File_Icon("*", Fl_File_Icon::DIRECTORY, sizeof(dir) / sizeof(dir[0]), dir);
    }

    // Mark things as initialized...
    init = 1;

#ifdef DEBUG
    int count;
    Fl_File_Icon *temp;
    for (count = 0, temp = first_; temp; temp = temp->next_, count ++);
    printf("count of Fl_File_Icon's is %d...\n", count);
#endif // DEBUG
  }
}


//
// 'load_kde_icons()' - Load KDE icon files.
//

static void
load_kde_icons(const char *directory,   // I - Directory to load
               const char *icondir) {   // I - Location of icons
  int           i;                      // Looping var
  int           n;                      // Number of entries in directory
  dirent        **entries;              // Entries in directory
  char          full[FL_PATH_MAX];      // Full name of file


  entries = (dirent **)0;
  n       = fl_filename_list(directory, &entries);

  for (i = 0; i < n; i ++) {
    if (entries[i]->d_name[0] != '.') {
      snprintf(full, sizeof(full), "%s/%s", directory, entries[i]->d_name);

      if (fl_filename_isdir(full)) load_kde_icons(full, icondir);
      else load_kde_mimelnk(full, icondir);
    }

    free((void *)entries[i]);
  }

  free((void*)entries);
}


//
// 'load_kde_mimelnk()' - Load a KDE "mimelnk" file.
//

static void
load_kde_mimelnk(const char *filename,  // I - mimelnk filename
                 const char *icondir) { // I - Location of icons
  FILE          *fp;
  char          tmp[1024];
  char          iconfilename[FL_PATH_MAX];
  char          pattern[1024];
  char          mimetype[1024];
  char          *val;
  char          full_iconfilename[2 * FL_PATH_MAX];
  Fl_File_Icon  *icon;


  mimetype[0]     = '\0';
  pattern[0]      = '\0';
  iconfilename[0] = '\0';

  if ((fp = fl_fopen(filename, "rb")) != NULL) {
    while (fgets(tmp, sizeof(tmp), fp)) {
      if ((val = get_kde_val(tmp, "Icon")) != NULL)
        strlcpy(iconfilename, val, sizeof(iconfilename));
      else if ((val = get_kde_val(tmp, "MimeType")) != NULL)
        strlcpy(mimetype, val, sizeof(mimetype));
      else if ((val = get_kde_val(tmp, "Patterns")) != NULL)
        strlcpy(pattern, val, sizeof(pattern));
    }

    fclose(fp);

#ifdef DEBUG
    printf("%s: Icon=\"%s\", MimeType=\"%s\", Patterns=\"%s\"\n", filename,
           iconfilename, mimetype, pattern);
#endif // DEBUG

    if (!pattern[0] && strncmp(mimetype, "inode/", 6)) return;

    if (iconfilename[0]) {
      if (iconfilename[0] == '/') {
        strlcpy(full_iconfilename, iconfilename, sizeof(full_iconfilename));
      } else if (!fl_access(icondir, F_OK)) {
        // KDE 3.x and 2.x icons
        int             i;              // Looping var
        static const char *paths[] = {  // Subdirs to look in...
          "16x16/actions",
          "16x16/apps",
          "16x16/devices",
          "16x16/filesystems",
          "16x16/mimetypes",
/*
          "20x20/actions",
          "20x20/apps",
          "20x20/devices",
          "20x20/filesystems",
          "20x20/mimetypes",

          "22x22/actions",
          "22x22/apps",
          "22x22/devices",
          "22x22/filesystems",
          "22x22/mimetypes",

          "24x24/actions",
          "24x24/apps",
          "24x24/devices",
          "24x24/filesystems",
          "24x24/mimetypes",
*/
          "32x32/actions",
          "32x32/apps",
          "32x32/devices",
          "32x32/filesystems",
          "32x32/mimetypes",
/*
          "36x36/actions",
          "36x36/apps",
          "36x36/devices",
          "36x36/filesystems",
          "36x36/mimetypes",

          "48x48/actions",
          "48x48/apps",
          "48x48/devices",
          "48x48/filesystems",
          "48x48/mimetypes",

          "64x64/actions",
          "64x64/apps",
          "64x64/devices",
          "64x64/filesystems",
          "64x64/mimetypes",

          "96x96/actions",
          "96x96/apps",
          "96x96/devices",
          "96x96/filesystems",
          "96x96/mimetypes"
*/      };

        for (i = 0; i < (int)(sizeof(paths) / sizeof(paths[0])); i ++) {
          snprintf(full_iconfilename, sizeof(full_iconfilename),
                   "%s/%s/%s.png", icondir, paths[i], iconfilename);

          if (!fl_access(full_iconfilename, F_OK)) break;
        }

        if (i >= (int)(sizeof(paths) / sizeof(paths[0]))) return;
      } else {
        // KDE 1.x icons
        snprintf(full_iconfilename, sizeof(full_iconfilename),
                 "%s/%s", tmp, iconfilename);

        if (fl_access(full_iconfilename, F_OK)) return;
      }

      if (strncmp(mimetype, "inode/", 6) == 0) {
        if (!strcmp(mimetype + 6, "directory"))
          icon = new Fl_File_Icon("*", Fl_File_Icon::DIRECTORY);
        else if (!strcmp(mimetype + 6, "blockdevice"))
          icon = new Fl_File_Icon("*", Fl_File_Icon::DEVICE);
        else if (!strcmp(mimetype + 6, "fifo"))
          icon = new Fl_File_Icon("*", Fl_File_Icon::FIFO);
        else return;
      } else {
        icon = new Fl_File_Icon(kde_to_fltk_pattern(pattern),
                                Fl_File_Icon::PLAIN);
      }

      icon->load(full_iconfilename);
    }
  }
}


//
// 'kde_to_fltk_pattern()' - Convert a KDE pattern to a FLTK pattern.
//

static char *
kde_to_fltk_pattern(const char *kdepattern) {
  char  *pattern,
        *patptr;


  pattern = (char *)malloc(strlen(kdepattern) + 3);
  strcpy(pattern, "{");
  strcpy(pattern + 1, kdepattern);

  if (pattern[strlen(pattern) - 1] == ';') pattern[strlen(pattern) - 1] = '\0';

  strcat(pattern, "}");

  for (patptr = pattern; *patptr; patptr ++) {
    if (*patptr == ';') *patptr = '|';
  }

  return (pattern);
}


//
// 'get_kde_val()' - Get a KDE value.
//

static char *
get_kde_val(char       *str,
            const char *key) {
  while (*str == *key) {
    str ++;
    key ++;
  }

  if (*key == '\0' && *str == '=') {
    if (str[strlen(str) - 1] == '\n') str[strlen(str) - 1] = '\0';

    return (str + 1);
  }

  return ((char *)0);
}
