Logo Search packages:      
Sourcecode: vertex version File versions

fb.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#ifdef __MSW__
# include <windows.h>
#else
# include <unistd.h>
# include <fnmatch.h>
# ifdef __SOLARIS__
#  include <sys/mnttab.h>
#  include <sys/vfstab.h>
# else
#  include <mntent.h>
# endif
#endif
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "../include/string.h"
#include "../include/strexp.h"
#include "../include/disk.h"

#include "guiutils.h"
#include "cdialog.h"
#include "pulist.h"
#include "fprompt.h"
#include "fb.h"
#include "config.h"


#include "images/icon_file_20x20.xpm"
#include "images/icon_folder_closed_20x20.xpm"
#include "images/icon_folder_opened_20x20.xpm"
#include "images/icon_folder_parent_20x20.xpm"
#include "images/icon_folder_noaccess_20x20.xpm"
#include "images/icon_folder_home_20x20.xpm"
#include "images/icon_link2_20x20.xpm"
#include "images/icon_pipe_20x20.xpm"
#include "images/icon_socket_20x20.xpm"
#include "images/icon_device_block_20x20.xpm"
#include "images/icon_device_character_20x20.xpm"
#include "images/icon_executable_20x20.xpm"
#include "images/icon_drive_fixed_20x20.xpm"
#include "images/icon_drive_root_20x20.xpm"

#include "images/icon_fb_list_standard_20x20.xpm"
#include "images/icon_fb_list_vertical_20x20.xpm"
#include "images/icon_fb_list_vertical_details_20x20.xpm"
#include "images/icon_rename_20x20.xpm"
#include "images/icon_chmod_20x20.xpm"
#include "images/icon_reload_20x20.xpm"
#include "images/icon_select_20x20.xpm"
#include "images/icon_ok_20x20.xpm"
#include "images/icon_cancel_20x20.xpm"

#include "images/icon_trash_32x32.xpm"


/*
 *    List formats:
 */
#define FB_LIST_FORMAT_STANDARD           0     /* Horizontal */
#define FB_LIST_FORMAT_VERTICAL           1     /* Vertical */
#define FB_LIST_FORMAT_VERTICAL_DETAILS   2     /* Vertical with details */

/*
 *    Icon index values:
 *
 *    The values represent the index of each icon defined in
 *    FB_ICON_DATA_LIST.
 */
#define FB_ICON_FILE          0
#define FB_ICON_FOLDER_CLOSED 1
#define FB_ICON_FOLDER_OPENED 2
#define FB_ICON_FOLDER_PARENT 3
#define FB_ICON_FOLDER_NOACCESS     4
#define FB_ICON_FOLDER_HOME   5
#define FB_ICON_LINK          6
#define FB_ICON_PIPE          7
#define FB_ICON_SOCKET        8
#define FB_ICON_DEVICE_BLOCK  9
#define FB_ICON_DEVICE_CHARACTER    10
#define FB_ICON_EXECUTABLE    11
#define FB_ICON_DRIVE_FIXED   12
#define FB_ICON_DRIVE_ROOT    13

/*
 *    Icons data list:
 *
 *    The order is important so that index of icon data coencides with
 *    defined FB_ICON_* values.  Each set contains 6 gpointers, and
 *    the gpointers in the last set are all NULL.
 */
#define FB_ICON_DATA_LIST     {     \
      "text/plain",                 \
      icon_file_20x20_xpm,          \
      NULL, NULL, NULL, NULL,       \
                              \
      "directory",                  \
      icon_folder_closed_20x20_xpm, \
      NULL, NULL, NULL, NULL,       \
                              \
      "directory",                  \
      icon_folder_opened_20x20_xpm, \
      NULL, NULL, NULL, NULL,       \
                              \
      "directory",                  \
      icon_folder_parent_20x20_xpm, \
      NULL, NULL, NULL, NULL,       \
                              \
        "directory",                \
        icon_folder_noaccess_20x20_xpm,   \
        NULL, NULL, NULL, NULL,           \
                              \
        "directory",                \
        icon_folder_home_20x20_xpm, \
        NULL, NULL, NULL, NULL,           \
                              \
      "link",                       \
      icon_link2_20x20_xpm,         \
      NULL, NULL, NULL, NULL,       \
                              \
      "pipe",                       \
      icon_pipe_20x20_xpm,          \
      NULL, NULL, NULL, NULL,       \
                              \
        "socket",             \
        icon_socket_20x20_xpm,            \
        NULL, NULL, NULL, NULL,           \
                              \
        "device/block",             \
        icon_device_block_20x20_xpm,      \
        NULL, NULL, NULL, NULL,           \
                              \
        "device/character",         \
        icon_device_character_20x20_xpm,\
        NULL, NULL, NULL, NULL,           \
                              \
      "executable",                 \
      icon_executable_20x20_xpm,    \
      NULL, NULL, NULL, NULL,       \
                              \
      "drive/fixed",                \
      icon_drive_fixed_20x20_xpm,   \
      NULL, NULL, NULL, NULL,       \
                              \
      "drive/root",                 \
      icon_drive_root_20x20_xpm,    \
      NULL, NULL, NULL, NULL,       \
                              \
      NULL, NULL, NULL, NULL, NULL, NULL\
}

/*
 *    Icon structure:
 */
typedef struct {

      GdkPixmap   *pixmap;
      GdkBitmap   *mask;
      gint        width, height;
      gchar       *desc;            /* Used for MIME type */

} FileBrowserIcon;
#define FILE_BROWSER_ICON(p)  ((FileBrowserIcon *)(p))

/*
 *    Object structure:
 */
typedef struct {

      gchar *name;
      gchar *full_path;
      gint icon_num;
      gint x, y, width, height;
      struct stat lstat_buf;

} FileBrowserObject;
#define FILE_BROWSER_OBJECT(p)      ((FileBrowserObject *)(p))

/*
 *    Column structure:
 *
 *    Used in the FileBrowser's list when displaying things that
 *    need columns.
 */
typedef struct {

      gint        position;
      gchar       *label;
      gint        label_justify;    /* One of GTK_JUSTIFY_*. */
      gulong            flags;

      /* If drag is TRUE then it means the column is being dragged
       * (resized and drag_position indicates the right or lower edge
       * of the column.
       */
      gboolean    drag;
      gint        drag_position;
      gint        drag_last_drawn_position;

} FileBrowserColumn;
#define FILE_BROWSER_COLUMN(p)      ((FileBrowserColumn *)(p))

/*
 *    File browser structure:
 */
typedef struct {

      gboolean    map_state;
      gint        busy_count;
      GtkAccelGroup     *accelgrp;

      GdkCursor   *cur_busy,
                  *cur_column_hresize;

      GtkWidget   *toplevel,
                  *main_vbox,
                  *dir_pulist_da,
                  *dir_pulist_btn,
                  *goto_parent_btn,
                  *new_directory_btn,
                  *rename_btn,
                  *refresh_btn,
                  *list_format_standard_tb,
                  *list_format_vertical_tb,
                  *list_format_vertical_details_tb,
                  *list_header_da,  /* List header GtkDrawingArea */
                  *list_da,   /* List GtkDrawingArea */
                  *list_vsb,  /* List vertical scrollbar */
                  *list_hsb,  /* List horizontal scrollbar */
                  *list_menu, /* List right-click menu */
                  *entry,           /* Location entry */
                  *type_combo,      /* File type combo */
                  *ok_btn_label,
                  *ok_btn,
                  *cancel_btn_label,
                  *cancel_btn;

      GdkPixmap   *list_pm;

      pulist_struct     *dir_pulist;
      gchar       *cur_location;    /* Current directory */

      gint        block_loop_level;
      gboolean    user_response;    /* TRUE if user clicked on OK */

      gint        list_format;      /* One of FB_LIST_FORMAT_* */
      gint        list_format_toggle_freeze_count;

      FileBrowserColumn **column;
      gint              total_columns;

      gint        type_change_freeze_count;

      fb_type_struct    cur_type;   /* Current file type */
      gchar             **selected_path;
      gint        total_selected_paths;

      FileBrowserIcon   **icon;
      gint        total_icons;

      gint        uid, euid;
      gint        gid, egid;
      gchar       *home_path;
      gchar       **drive_path;
      gint        total_drive_paths;

      gint        focus_object;
      GList       *selection,
                  *selection_end;
      FileBrowserObject **object;
      gint              total_objects;
      gint              objects_per_row;  /* For use with
                                           * FB_LIST_FORMAT_STANDARD
                                           */

} FileBrowser;
#define FILE_BROWSER(p)       ((FileBrowser *)(p))
static FileBrowser file_browser;


/* Utilities. */
static gchar **FileBrowserDNDBufParse(
      const gchar *buf, gint len, gint *n
);
static gchar *FileBrowserDNDBufFormat(
      FileBrowserObject **object, gint total, GList *selection,
      gint *len
);

static gboolean FileBrowserObjectNameFilter(
      const gchar *name, const gchar *full_path,
      const gchar *ext
);

static gchar **FileBrowserGetDrivePaths(gint *n);

static void FileBrowserSetBusy(FileBrowser *fb, gboolean busy);

static void FileBrowserSetListFormat(FileBrowser *fb, gint list_format);

static void FileBrowserSetLocation(FileBrowser *fb, const gchar *path);
static gchar *FileBrowserGetLocation(FileBrowser *fb);

static void FileBrowserDirPUListUpdate(FileBrowser *fb);
static void FileBrowserDirPUListDraw(FileBrowser *fb);

static FileBrowserIcon *FileBrowserGetIcon(FileBrowser *fb, gint i);
static FileBrowserIcon *FileBrowserIconAppend(
      FileBrowser *fb, guint8 **xpm_data, const gchar *desc
);
static gint FileBrowserMatchIconNumFromPath(
        FileBrowser *fb, const gchar *full_path, struct stat *lstat_buf
);

static FileBrowserObject *FileBrowserGetObject(FileBrowser *fb, gint i);
static void FileBrowserObjectUpdateValues(
      FileBrowser *fb, FileBrowserObject *o
);
static FileBrowserObject *FileBrowserObjectAppend(
        FileBrowser *fb, const gchar *name, const gchar *full_path,
        struct stat *lstat_buf
);
static void FileBrowserListUpdate(FileBrowser *fb, const gchar *filter);
static void FileBrowserListObjectSetDNDIcon(FileBrowser *fb, gint i);
static gint FileBrowserListObjectVisibility(FileBrowser *fb, gint i);
static void FileBrowserListMoveToObject(
        FileBrowser *fb, gint i, gfloat coeff
);
static gint FileBrowserListSelectCoordinates(
        FileBrowser *fb, gint x, gint y
);
static void FileBrowserListHeaderDraw(FileBrowser *fb);
static void FileBrowserListDraw(FileBrowser *fb);

static FileBrowserColumn *FileBrowserListGetColumn(FileBrowser *fb, gint i);
static FileBrowserColumn *FileBrowserListColumnAppend(
      FileBrowser *fb, const gchar *label, gint width
);
static void FileBrowserListColumnsClear(FileBrowser *fb);

static void FileBrowserEntrySetSelectedObjects(FileBrowser *fb);


/* Callbacks. */
static void FileBrowserDirPUListItemDestroyCB(gpointer data);
static void FileBrowserIconDestroyCB(gpointer data);
static void FileBrowserObjectDestroyCB(gpointer data);
static void FileBrowserColumnDestroyCB(gpointer data);

static gboolean FileBrowserDragmotionCB(
        GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t,
        gpointer data
);
static void FileBrowserDragDataReceivedCB(
        GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
        GtkSelectionData *selection_data, guint info, guint t,
        gpointer data
);
static void FileBrowserDragDataGetCB(
        GtkWidget *widget, GdkDragContext *dc,
        GtkSelectionData *selection_data, guint info, guint t,
        gpointer data
);
static void FileBrowserDragDataDeleteCB(
        GtkWidget *widget, GdkDragContext *dc, gpointer data
);

static void FileBrowserListFormatToggleCB(GtkWidget *widget, gpointer data);

static void FileBrowserGoToParentCB(GtkWidget *widget, gpointer data);
static void FileBrowserNewDirectoryCB(GtkWidget *widget, gpointer data);
static void FileBrowserRefreshCB(GtkWidget *widget, gpointer data);
static void FileBrowserOKCB(GtkWidget *widget, gpointer data);
static void FileBrowserCancelCB(GtkWidget *widget, gpointer data);

static gint FileBrowserDirPUListDAEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
);
static void FileBrowserDirPUListMapCB(GtkWidget *widget, gpointer data);

static gint FileBrowserListHeaderEventCB(
        GtkWidget *widget, GdkEvent *event, gpointer data
);
static gint FileBrowserListEventCB(
        GtkWidget *widget, GdkEvent *event, gpointer data
);
static void FileBrowserListScrollCB(
        GtkAdjustment *adj, gpointer data
);

static void FileBrowserSelectAllCB(GtkWidget *widget, gpointer data);
static void FileBrowserUnselectAllCB(GtkWidget *widget, gpointer data);
static void FileBrowserInvertSelectionCB(GtkWidget *widget, gpointer data);
static void FileBrowserRenameFPromptCB(
        gpointer data, const gchar *value
);
static void FileBrowserRenameCB(GtkWidget *widget, gpointer data);
static void FileBrowserCHModFPromptCB(
        gpointer data, const gchar *value
);
static void FileBrowserCHModCB(GtkWidget *widget, gpointer data);
static void FileBrowserDeleteCB(GtkWidget *widget, gpointer data);

static void FileBrowserEntryEnterCB(GtkWidget *widget, gpointer data);

static void FileBrowserTypeListChangeCB(
        GtkWidget *widget, gpointer data, GList *glist
);


/* Front ends. */
gint FileBrowserInit(void);
void FileBrowserSetTransientFor(GtkWidget *w);
gbool FileBrowserIsQuery(void);
void FileBrowserBreakQuery(void);
gbool FileBrowserGetResponse(
      const gchar *title,
      const gchar *ok_label, const gchar *cancel_label,
        const gchar *path,
        fb_type_struct **type, gint total_types,
        gchar ***path_rtn, gint *path_total_rtns,
        fb_type_struct **type_rtn
);
void FileBrowserMap(void);
void FileBrowserUnmap(void);
void FileBrowserShutdown(void);

/* File browser file type extension management. */
gint FileBrowserTypeListNew(
        fb_type_struct ***list, gint *total,
        const gchar *ext,     /* Space separated list of extensions. */
        const gchar *name     /* Descriptive name. */
);
void FileBrowserDeleteTypeList(
        fb_type_struct **t, gint total
);


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))

#ifndef ISBLANK
# define ISBLANK(c)     (((c) == ' ') || ((c) == '\t'))
#endif

#define FB_WIDTH        480
#define FB_HEIGHT       -1

#define FB_BTN_WIDTH          (100 + (2 * 3))
#define FB_BTN_HEIGHT         (30 + (2 * 3))

#define FB_TB_BTN_WIDTH       (20 + 5)
#define FB_TB_BTN_HEIGHT      (20 + 5)

#define FB_LOCATION_LIST_MAX_CHARACTERS   25

#define FB_LIST_HEADER_WIDTH  -1
#define FB_LIST_HEADER_HEIGHT (20 + 2)

#define FB_LIST_WIDTH         -1
#define FB_LIST_HEIGHT        150

#define FB_LIST_ICON_WIDTH    20
#define FB_LIST_ICON_HEIGHT   20
#define FB_LIST_ICON_BORDER   2     /* Border between icon and text */

#define FB_DEFAULT_TYPE_STR   "All files (*.*)"

#define FB_NEW_DIRECTORY_NAME "new_directory"


/*
 *    Returns a list of full paths parsed from the given DND buffer.
 */
static gchar **FileBrowserDNDBufParse(
        const gchar *buf, gint len, gint *n
)
{
      const gchar *buf_end = buf + len;   /* Record end of buffer */
      gchar **strv = NULL;
      gint i, strc = 0;

      if(n != NULL)
          *n = strc;

      if((buf == NULL) || (len <= 0))
          return(strv);

      /* Iterate through buffer that contains a list of '\0' character
       * separated strings.
       */
      while(buf < buf_end)
      {
          const gchar *s = buf;
          gint s_len = strlen(s);

          /* Is the protocol prefix of the string one that we want? */
          if(strcasepfx(s, "file://"))
          {
            /* Seek past protocol "file://" */
            s += strlen("file://");

            /* Seek past "username:password@host:port"
             * too many programs fail to do this!
                 */
            s = (s != NULL) ? strchr(s, '/') : NULL;

            /* Able to seek to full path portion of the string? */
            if(s != NULL)
            {
                /* Got full path portion, now add it to the list of
                 * return strings.
                 */
                i = strc;
                strc = i + 1;
                strv = (gchar **)g_realloc(
                  strv, strc * sizeof(gchar *)
                );
                if(strv == NULL)
                {
                  strc = 0;
                  break;
                }
                strv[i] = STRDUP(s);
            }
          }

          /* Seek buf to next string.  First seek buf past this string
           * then seek past any '\0' deliminator(s) until we reach
           * the next string or the end of the buffer.
           */
          buf += s_len;
          while((buf < buf_end) ? (*buf == '\0') : FALSE)
            buf++;
      }

      /* Update total return value */
      if(n != NULL)
          *n = strc;

      return(strv);
}

/*
 *    Generates a DND buffer based on the list of objects that are
 *    selected based on the given selection.
 */
static gchar *FileBrowserDNDBufFormat(
        FileBrowserObject **object, gint total, GList *selection,
        gint *len
)
{
      gchar *buf = NULL;
      gint i, buf_len = 0;
      GList *glist;
      FileBrowserObject *o;


      /* Iterate through selection. */
      for(glist = selection; glist != NULL; glist = glist->next)
      {
          i = (gint)glist->data;
          if((i >= 0) && (i < total))
            o = object[i];
          else
            o = NULL;
          if((o != NULL) ? (o->full_path != NULL) : FALSE)
          {
            gchar *url = g_strdup_printf(
                "file://%s",
                o->full_path
            );
            gint url_len = (url != NULL) ? strlen(url) : 0;
            gint last_buf_len = buf_len;

            /* Increase allocation of buffer for this url string
             * which we will append to the buffer (include the
             * '\0' character counted in buf_len).
             */
            buf_len += url_len + 1;
            buf = (gchar *)g_realloc(buf, buf_len * sizeof(gchar));
            if(buf == NULL)
            {
                buf_len = 0;
                break;
            }
            memcpy(
                buf + last_buf_len,
                (url != NULL) ? url : "",
                url_len + 1         /* Include the '\0' character */
            );

            g_free(url);
          }
      }

      /* Update return value */
        if(len != NULL)
            *len = buf_len;

      return(buf);
}


/*
 *    Checks if the object name specified by name and full_path
 *    match the extensions given in ext.
 */
static gboolean FileBrowserObjectNameFilter(
        const gchar *name, const gchar *full_path,
        const gchar *ext
)
{
      gchar *st, *st_end, cur_ext[80];
      gint cur_ext_len, name_len;
      gboolean use_regex;


      if((name == NULL) || (full_path == NULL) || (ext == NULL))
          return(FALSE);

      name_len = strlen(name);

      while(ISBLANK(*ext))
          ext++;

      /* Iterate through each extension in ext */
      while(*ext != '\0')
      {
          use_regex = FALSE;

          /* Copy current extension string in ext to cur_ext and
           * seek ext to next position.
           */
          cur_ext_len = 0;
          st = cur_ext;
          st_end = cur_ext + 79;    /* Set end 1 character premature */
          while((st < st_end) && !ISBLANK(*ext) && (*ext != '\0'))
          {
            /* Use this opportunity to check if there are characters
             * in the extension string to warrent the use of regex
             */
            if((*ext == '*') || (*ext == '?'))
                use_regex = TRUE;

            *st++ = *ext++;
            cur_ext_len++;
          }
          *st = '\0';

          /* Seek ext to next extension string. */
          while(ISBLANK(*ext))
            ext++;

          /* Check if there is a match */
#ifndef __MSW__
          if(use_regex)
          {
            /* Use regex to match */
            if(!fnmatch(cur_ext, name, 0))
                return(TRUE);
          }
          else
#endif
          {
            /* Check if cur_ext is a postfix of name */
            if(name_len >= cur_ext_len)
            {
                if(!g_strcasecmp(
                  name + name_len - cur_ext_len,
                  cur_ext
                ))
                  return(TRUE);
            }
          }
      }
      return(FALSE);
}


/*
 *    Returns a list of strings describing the drive paths.
 */
static gchar **FileBrowserGetDrivePaths(gint *n)
{
#ifdef __MSW__
      gchar drive_letter = 'a';
      gchar drive_name[10];
        gint i, strc = 0;
        gchar **strv = NULL;
/*    struct stat stat_buf; */

      for(drive_letter = 'a'; drive_letter <= 'g'; drive_letter++)
      {
          sprintf(drive_name, "%c:\\", drive_letter);
/*
          if(stat(drive_name, &stat_buf))
            break;
 */
          i = strc;
          strc = i + 1;
          strv = (gchar **)g_realloc(
            strv, strc * sizeof(gchar *)
          );
          if(strv == NULL)
          {
            strc = 0;
            break;
          }

          strv[i] = STRDUP(drive_name);
      }

      if(n != NULL)
          *n = strc;

      return(strv);
#else
      gint i, strc = 0;
      gchar **strv = NULL;
#ifdef __SOLARIS__
        struct vfstab *vfs_ptr = NULL;
        int mtback;
#else
        struct mntent *mt_ptr;
#endif

      /* Open system devices list file */
#ifdef __SOLARIS__
      FILE *fp = FOpen("/etc/vfstab", "rb");
#else
      FILE *fp = setmntent("/etc/fstab", "rb");
#endif
      if(fp == NULL)
      {
          if(n != NULL)
            *n = strc;
          return(strv);
      }

      /* Begin reading system devices list file */
#ifdef __SOLARIS__
        vfs_ptr = (struct vfstab *)g_malloc(sizeof(struct vfstab));
        mtback = getvfsent(fp, vfs_ptr);
        while(mtback != 0)
#else
        mt_ptr = getmntent(fp);
        while(mt_ptr != NULL)
#endif
        {
          i = strc;
          strc = i + 1;
          strv = (gchar **)g_realloc(
            strv, strc * sizeof(gchar *)
          );
          if(strv == NULL)
          {
            strc = 0;
            break;
          }

          /* Get mount path as the drive path */
#ifdef __SOLARIS__
          strv[i] = STRDUP(vfs_ptr->vfs_mountp);
#else
          strv[i] = STRDUP(mt_ptr->mnt_dir);
#endif

          /* Read next mount entry */
#ifdef __SOLARIS__
            mtback = getmntent(fp, vfs_ptr);
#else
            mt_ptr = getmntent(fp);
#endif
        }


      /* Close system devices list file */
#ifdef __SOLARIS__
        FClose(fp);
        fp = NULL;
        vfs_ptr = NULL;
#else
        endmntent(fp);
        fp = NULL;
#endif

      if(n != NULL)
          *n = strc;

      return(strv);
#endif
}


/*
 *    Sets the file browser busy or ready.
 */
static void FileBrowserSetBusy(FileBrowser *fb, gboolean busy)
{
        GdkCursor *cursor;
        GtkWidget *w;


        if(fb == NULL)
            return;

        w = fb->toplevel;
        if(w != NULL)
        {
            if(busy)
            {
                /* Increase busy count. */
                fb->busy_count++;

                /* If already busy then don't change anything. */
                if(fb->busy_count > 1)
                    return;

                cursor = fb->cur_busy;
            }
            else
            {
                /* Reduce busy count. */
                fb->busy_count--;
                if(fb->busy_count < 0)
                    fb->busy_count = 0;

                /* If still busy do not change anything. */
                if(fb->busy_count > 0)
                    return;

                cursor = NULL;  /* Use default cursor. */
            }

            /* Update toplevel window's cursor. */
            if(w->window != NULL)
            {
                gdk_window_set_cursor(w->window, cursor);
                gdk_flush();
            }
        }
}

/*
 *    Sets the list format and updates the list.
 */
static void FileBrowserSetListFormat(FileBrowser *fb, gint list_format)
{
      GtkWidget *w;
      FileBrowserColumn *column;

        if(fb == NULL)
            return;

      /* Skip if no change in list format */
      if(fb->list_format == list_format)
          return;

      fb->list_format_toggle_freeze_count++;

      /* Set new list format */
      fb->list_format = list_format;

      /* Update list format toggle buttons, make sure we freeze so the
       * toggle callback does not recurse.
       */
#define SET_TB(p,b)                       \
{ if((p) != NULL) {                       \
 gtk_toggle_button_set_active(                  \
  GTK_TOGGLE_BUTTON(p), b                 \
 );                                 \
} }
      switch(list_format)
      {
        case FB_LIST_FORMAT_VERTICAL:
          SET_TB(fb->list_format_standard_tb, FALSE);
          SET_TB(fb->list_format_vertical_tb, TRUE);
          SET_TB(fb->list_format_vertical_details_tb, FALSE);
          break;

          case FB_LIST_FORMAT_VERTICAL_DETAILS:
            SET_TB(fb->list_format_standard_tb, FALSE);
            SET_TB(fb->list_format_vertical_tb, FALSE);
            SET_TB(fb->list_format_vertical_details_tb, TRUE);
            break;

          default:      /* FB_LIST_FORMAT_STANDARD */
            SET_TB(fb->list_format_standard_tb, TRUE);
            SET_TB(fb->list_format_vertical_tb, FALSE);
            SET_TB(fb->list_format_vertical_details_tb, FALSE);
            break;
      }

      /* Begin updating things to recognize the new list format. */

      /* Update list columns. */
      FileBrowserListColumnsClear(fb);
      switch(list_format)
        {
          case FB_LIST_FORMAT_VERTICAL_DETAILS:
          /* Set up list columns */
          /* Name */
          column = FileBrowserListColumnAppend(
            fb, "Name", 145
          );
          /* Size */
            column = FileBrowserListColumnAppend(
                fb, "Size", 70
            );
          column->label_justify = GTK_JUSTIFY_RIGHT;
            /* Permissions */
            column = FileBrowserListColumnAppend(
                fb, "Permissions", 78
            );
          /* Last Modified */
          column = FileBrowserListColumnAppend(
                fb, "Last Modified", 160
            );

          /* Show list header */
          w = fb->list_header_da;
          if(w != NULL)
            gtk_widget_show(w);
          break;

        default:
          /* Hide list header */
            w = fb->list_header_da;
            if(w != NULL)
                gtk_widget_hide(w);
          break;
      }
      gtk_widget_queue_resize(fb->toplevel);


      /* Update list, this will reget the list of objects and 
       * map/unmap list widgets to reflect the new list format.
       * The list will also be redrawn.
       */
      FileBrowserListUpdate(fb, NULL);

      fb->list_format_toggle_freeze_count--;
}

/*
 *    Sets the new location and updates the list and directory pulist.
 *
 *    The new location must be a directory, if it is not then its
 *    parent will be used instead.
 */
static void FileBrowserSetLocation(FileBrowser *fb, const gchar *path)
{
      gchar *new_location;
      GtkWidget *w;

      if((fb == NULL) || (path == NULL))
          return;

      if(path == fb->cur_location)
          return;

      /* Make a copy of the given path which will be modified and
       * checked before (if) it is finally accepted.
       */
      new_location = STRDUP(path);


      /* Special notation checks. */

      /* Current location? */
      if(!strcmp(new_location, "."))
      {
            g_free(new_location);
#ifdef __MSW__
            new_location = (fb->cur_location != NULL) ?
                STRDUP(fb->cur_location) : STRDUP("c:");
#else
            new_location = (fb->cur_location != NULL) ?
            STRDUP(fb->cur_location) : STRDUP("/");
#endif
      }

      /* Parent? */
        if(!strcmp(new_location, ".."))
        {
            g_free(new_location);
#ifdef __MSW__
            new_location = (fb->cur_location != NULL) ?
                STRDUP(GetParentDir(fb->cur_location)) : STRDUP("c:");
#else
            new_location = (fb->cur_location != NULL) ?
                STRDUP(GetParentDir(fb->cur_location)) : STRDUP("/");
#endif
        }

      /* Home directory prefix? */
      if(*new_location == '~')
      {
          const gchar *home = fb->home_path;
          gchar *s = g_strdup_printf(
            "%s%s",
            (home != NULL) ? home : "",
            new_location + 1
          );
          g_free(new_location);
            new_location = s;
      }

      /* At this point new_location must be an absolute path */
      if(!ISPATHABSOLUTE(new_location))
      {
          gchar *s, cwd[PATH_MAX];
          if(getcwd(cwd, PATH_MAX) != NULL)
            cwd[PATH_MAX - 1] = '\0';
          else
            strcpy(cwd, "/");

          s = STRDUP(PrefixPaths(cwd, new_location));
          g_free(new_location);
          new_location = s;
      }

      /* Make sure the new location is a directory, if it is not then
       * get its parent instead.
       */
      if(!ISPATHDIR(new_location))
      {
          gchar *s = STRDUP(GetParentDir(new_location));
          g_free(new_location);
          new_location = s;
      }

      /* Strip tailing deliminator. */
      if(new_location != NULL)
      {
#ifdef __MSW__
            gchar *s = strrchr(new_location, DIR_DELIMINATOR);
          gint prefix_len = strlen("x:\\");
#else
            gchar *s = strrchr(new_location, DIR_DELIMINATOR);
            gint prefix_len = strlen("/");
#endif
          while(s >= (new_location + prefix_len))
          {
            if((s[0] == DIR_DELIMINATOR) &&
               (s[1] == '\0')
            )
                *s = '\0';
            else
                break;
            s--;
          }
      }

      /* If there is no change in the location then do nothing. */
      if((fb->cur_location != NULL) ?
          !strcmp(fb->cur_location, new_location) : FALSE
      )
      {
          g_free(new_location);
          return;
      }

      /* Accept and set new location. */
      g_free(fb->cur_location);
      fb->cur_location = new_location;

      /* Clear file name entry each time we set a new location. */
      w = fb->entry;
      if(w != NULL)
          gtk_entry_set_text(GTK_ENTRY(w), "");

      /* Get new listing of objects due to location change. */
      FileBrowserListUpdate(fb, NULL);

      /* Update paths in the directory popup list. */
      FileBrowserDirPUListUpdate(fb);

      /* Redraw. */
      FileBrowserDirPUListDraw(fb);
}

/*
 *    Returns the current location, never returns NULL.
 */
static gchar *FileBrowserGetLocation(FileBrowser *fb)
{
#ifdef __MSW__
      return((fb != NULL) ? fb->cur_location : "c:\\");
#else
      return((fb != NULL) ? fb->cur_location : "/");
#endif
}


/*
 *    DND "drag_motion" signal callback.
 */
static gboolean FileBrowserDragmotionCB(
        GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t,
        gpointer data
)
{
      FileBrowser *fb = FILE_BROWSER(data);
        if((dc == NULL) || (fb == NULL))
            return(FALSE);

        if(dc->actions & GDK_ACTION_COPY)
            gdk_drag_status(dc, GDK_ACTION_COPY, t);
        else
            gdk_drag_status(dc, 0, t);

        return(TRUE);
}

/*
 *    DND "drag_data_received" signal callback.
 */
static void FileBrowserDragDataReceivedCB(
        GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
        GtkSelectionData *selection_data, guint info, guint t,
        gpointer data
)
{
        FileBrowser *fb = FILE_BROWSER(data);
        if((widget == NULL) || (dc == NULL) || (fb == NULL))
            return;

      if(selection_data == NULL)
          return;

      /* Make sure the DND info is one that we want. */
        if((info == 0) || (info == 1) || (info == 2))
      {
          /* Handle by destination widget */
          /* List */
          if(widget == fb->list_da)
          {
            /* When dropping to the list, first check if there is
             * exactly one object and if so then check if it leads
             * to a directory, in which case we will set the new
             * location.  Otherwise we just set it to the file
             * name entry.
             */
                gint i, strc;
                gchar **strv = FileBrowserDNDBufParse(
                    (const gchar *)selection_data->data,
                    selection_data->length,
                    &strc
                );
            const gchar *new_location = (strc == 1) ? strv[0] : NULL;
            if((new_location != NULL) ? ISPATHDIR(new_location) : FALSE)
            {
                /* Exactly one object dropped and it leads to a
                 * directory so go to the new location.
                 */
                FileBrowserSetLocation(fb, new_location);
            }
            else
            {
                /* Not one object or the one object does not lead to
                 * a directory, so just set the dropped objects to
                 * the file name entry.
                 */
                GtkEntry *entry = GTK_ENTRY(fb->entry);
                gchar *s = NULL;
                for(i = 0; i < strc; i++)
                    {
                        s = strcatalloc(s, strv[i]);
                        if((i + 1) < strc)
                            s = strcatalloc(s, ",");
                    }
                    gtk_entry_set_text(entry, s);
                    gtk_entry_set_position(entry, -1);
                    g_free(s);
            }
            for(i = 0; i < strc; i++)
                g_free(strv[i]);
            g_free(strv);
            }
          /* File name entry */
          else if(widget == fb->entry)
          {
            GtkEntry *entry = GTK_ENTRY(widget);
            gchar *s = NULL;
            gint i, strc;
            gchar **strv = FileBrowserDNDBufParse(
                (const gchar *)selection_data->data,
                selection_data->length,
                &strc
            );
            for(i = 0; i < strc; i++)
            {
                s = strcatalloc(s, strv[i]);
                if((i + 1) < strc)
                  s = strcatalloc(s, ",");
                g_free(strv[i]);
            }
            g_free(strv);
            gtk_entry_set_text(entry, s);
            gtk_entry_set_position(entry, -1);
            g_free(s);
          }
      }
}

/*
 *    DND "drag_data_get" signal callback.
 */
static void FileBrowserDragDataGetCB(
        GtkWidget *widget, GdkDragContext *dc,
        GtkSelectionData *selection_data, guint info, guint t,
        gpointer data
)
{
      gboolean data_sent = FALSE;
        FileBrowser *fb = FILE_BROWSER(data);
        if((widget == NULL) || (fb == NULL))
            return;

        /* Make sure the DND info is one that we support. */
      if((info == 0) || (info == 1) || (info == 2))
      {
            /* Handle by destination widget */
            /* List */
          if(widget == fb->list_da)
          {
            gint buf_len;
            gchar *buf = FileBrowserDNDBufFormat(
                fb->object, fb->total_objects, fb->selection,
                &buf_len
            );
            if(buf != NULL)
            {
                /* Send out DND data. */
                gtk_selection_data_set(
                  selection_data,
                  GDK_SELECTION_TYPE_STRING,
                  8,    /* 8 bits per character */
                  buf, buf_len
                );
                data_sent = TRUE;
                g_free(buf);
            }
          }
      }

        /* Failed to send out DND data? */
        if(!data_sent)
        {
            /* Send a response indicating error. */
            const gchar *s = "Error";
            gtk_selection_data_set(
                selection_data,
                GDK_SELECTION_TYPE_STRING,
                8,            /* 8 bits per character */
                s,
            strlen(s) + 1     /* Include the '\0' character */
            );
            data_sent = TRUE;
        }
}

/*
 *      DND "drag_data_delete" signal callback.
 */
static void FileBrowserDragDataDeleteCB(
        GtkWidget *widget, GdkDragContext *dc, gpointer data
)
{
        FileBrowser *fb = FILE_BROWSER(data);
        if((widget == NULL) || (fb == NULL))
            return;

      /* Typical this means that some objects have been deleted
       * so we need to refresh.
       */
      FileBrowserRefreshCB(fb->refresh_btn, fb);
}

/*
 *    List format toggle callback.
 */
static void FileBrowserListFormatToggleCB(GtkWidget *widget, gpointer data)
{
      gint list_format = -1;
      GtkToggleButton *tb = GTK_TOGGLE_BUTTON(widget);
        FileBrowser *fb = FILE_BROWSER(data);
        if((widget == NULL) || (fb == NULL))
            return;

      /* If format toggling is frozen then do not do anything or
       * else recursing may occure.
       */
      if(fb->list_format_toggle_freeze_count > 0)
          return;

      fb->list_format_toggle_freeze_count++;

      /* Do not let toggle button become untoggled when toggling
       * was not frozen.
       */
      if(!tb->active)
      {
          gtk_toggle_button_set_active(tb, TRUE);
          fb->list_format_toggle_freeze_count--;
          return;
      }

      /* Begin checking which toggle button it was so we know which
       * list format to set to.
       */
      /* Standard */
      if(widget == fb->list_format_standard_tb)
          list_format = FB_LIST_FORMAT_STANDARD;
      /* Vertical */
      else if(widget == fb->list_format_vertical_tb)
          list_format = FB_LIST_FORMAT_VERTICAL;
      /* Vertical with details */
        else if(widget == fb->list_format_vertical_details_tb)
            list_format = FB_LIST_FORMAT_VERTICAL_DETAILS;

      /* Did we find out which list format to use? */
      if(list_format > -1)
          FileBrowserSetListFormat(fb, list_format);

      fb->list_format_toggle_freeze_count--;
}

/*
 *    Go to parent callback.
 */
static void FileBrowserGoToParentCB(GtkWidget *widget, gpointer data)
{
      const gchar *cur_location;
      gchar *parent;
        FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

      /* Get current location, then get its parent. */
      cur_location = FileBrowserGetLocation(fb);
      parent = STRDUP(GetParentDir(cur_location));

      /* Go to the parent of the current location. */
      if(parent != NULL)
          FileBrowserSetLocation(fb, parent);

      g_free(parent);
}

/*
 *    New directory callback.
 */
static void FileBrowserNewDirectoryCB(GtkWidget *widget, gpointer data)
{
      mode_t m;
      gchar *name, *full_path;
      const gchar *cur_location;
        FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

#ifdef PROG_LANGUAGE_ENGLISH
# define TITLE_MKDIR_FAILED   "Create New Directory Failed"
#endif
#ifdef PROG_LANGUAGE_SPANISH
# define TITLE_MKDIR_FAILED     "Cree Guía Nueva Fallada"
#endif
#ifdef PROG_LANGUAGE_FRENCH
# define TITLE_MKDIR_FAILED     "Créer Le Nouvel Annuaire Echoué"
#endif
#ifdef PROG_LANGUAGE_GERMAN
# define TITLE_MKDIR_FAILED     "Schaffen Sie Neues Versagten Verzeichnis"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
# define TITLE_MKDIR_FAILED     "Creare L'Elenco Nuovo Fallito"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
# define TITLE_MKDIR_FAILED     "Skap New Directory Failed"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
# define TITLE_MKDIR_FAILED     "Crie Novo Guia Fracassado"
#endif

#define MESSAGE_MKDIR_FAILED(s)        {        \
 CDialogSetTransientFor(fb->toplevel);          \
 CDialogGetResponse(                            \
  TITLE_MKDIR_FAILED,                     \
  (s), NULL,                                    \
  CDIALOG_ICON_WARNING,                         \
  CDIALOG_BTNFLAG_OK,                           \
  CDIALOG_BTNFLAG_OK                            \
 );                                             \
 CDialogSetTransientFor(NULL);                  \
}

        /* Get current location. */
        cur_location = FileBrowserGetLocation(fb);
      if(cur_location == NULL)
          return;


      FileBrowserSetBusy(fb, TRUE);

      /* Generate new name and full path. */
      name = STRDUP(FB_NEW_DIRECTORY_NAME);
      full_path = STRDUP(PrefixPaths(cur_location, name));

#if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
      m =   S_IRUSR | S_IWUSR | S_IXUSR |
            S_IRGRP | S_IWGRP | S_IXGRP |
            S_IROTH | S_IWOTH | S_IXOTH;
#else
      m = 0;
#endif
      /* Create new directory. */
#ifdef __MSW__
      if(mkdir(full_path))
#else
      if(mkdir(full_path, m))
#endif
      {
          gint i;
          gboolean created = FALSE;

          /* Failed to create, try creating it under a different
           * name.
           */
          for(i = 2; i < 100; i++)
          {
            /* Regenerate new name and full path. */
            g_free(name);
            g_free(full_path);
            name = g_strdup_printf(
                "%s%i",
                FB_NEW_DIRECTORY_NAME, i
            );
            full_path = STRDUP(PrefixPaths(cur_location, name));

            /* Try to create new directory, if success then
             * break out of the loop.
             */
#ifdef __MSW__
            if(!mkdir(full_path))
#else
            if(!mkdir(full_path, m))
#endif
            {
                created = TRUE;
                break;
            }

            /* Error creating directory and it was not because it
             * already exists?
             */
            if(errno != EEXIST)
            {
                gchar *buf;
#ifdef PROG_LANGUAGE_ENGLISH
                switch(errno)
                {
                      case EACCES:
                        buf = STRDUP(
"You do not have sufficient permission to create a\n\
new directory at this location."
                        );
                        break;

                      case ENAMETOOLONG:
                        buf = STRDUP(
"The name for the new directory is too long."
                        );
                        break;

                  case ENOENT:
                        buf = STRDUP(
"A compoent of the current location is a dangling symbolic link."
                        );
                  break;

                  case ENOMEM:
                        buf = STRDUP(
"The system is out of memory."
                        );
                        break;

                  case EROFS:
                        buf = STRDUP(
"The current location is at a read-only filesystem."
                        );
                        break;
#ifdef ELOOP
                  case ELOOP:
                        buf = STRDUP(
"Too many symbolic links were encountered at this location."
                        );
                        break;
#endif
                  case ENOSPC:
                  buf = STRDUP(
"The device is out of free space."
                  );
                  break;

                  default:
                  buf = STRDUP(
"Unable to create new directory."
                  );
                  break;
                }
#else
                buf =
#ifdef PROG_LANGUAGE_SPANISH
 STRDUP("Incapaz de crear guía nueva.")
#endif
#ifdef PROG_LANGUAGE_FRENCH
 STRDUP("Incapable pour créer le nouvel annuaire.")
#endif
#ifdef PROG_LANGUAGE_GERMAN
 STRDUP("Unfähig, neues Verzeichnis zu schaffen.")
#endif
#ifdef PROG_LANGUAGE_ITALIAN
 STRDUP("Incapace per creare l'elenco nuovo.")
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
 STRDUP("Maktesløs skape ny katalog.")
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
 STRDUP("Incapaz de criar novo guia.")
#endif
                ;
#endif
                MESSAGE_MKDIR_FAILED(buf);
                g_free(buf);
                break;
            }
          }
          /* Failed to create new directory? */
          if(!created)
          {
            g_free(name);
            g_free(full_path);
            FileBrowserSetBusy(fb, FALSE);
            return;
          }
      }

      /* Update list. */
      FileBrowserListUpdate(fb, NULL);
      if(name != NULL)
      {
          gint i;
          FileBrowserObject *o;

          /* Search through newly updated objects in the list to
           * find the one we created by matching of its name.
           */
          for(i = 0; i < fb->total_objects; i++)
          {
            o = fb->object[i];
            if((o != NULL) ? (o->name == NULL) : TRUE)
                continue;

            /* Name of this object matches the one we
             * created?
             */
            if(!strcmp(name, o->name))
            {
                /* Select this object. */
                fb->focus_object = i;
                fb->selection = g_list_append(
                  fb->selection, (gpointer)i
                );
                fb->selection_end = g_list_last(fb->selection);

                /* Scroll to this object. */
                FileBrowserListMoveToObject(fb, i, 0.5f);

                break;
            }
          }
      }

      FileBrowserSetBusy(fb, FALSE);

      g_free(name);
      g_free(full_path);
#undef MESSAGE_MKDIR_FAILED
#undef TITLE_MKDIR_FAILED
}

/*
 *    Refresh callback.
 */
static void FileBrowserRefreshCB(GtkWidget *widget, gpointer data)
{
      gint last_scroll_x = 0, last_scroll_y = 0;
      FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

      FileBrowserSetBusy(fb, TRUE);

      /* Record last scroll position. */
      if(fb->list_hsb != NULL)
      {
          GtkRange *range = GTK_RANGE(fb->list_hsb);
          GtkAdjustment *adj = range->adjustment;
          if(adj != NULL)
            last_scroll_x = (gint)adj->value;
      }
        if(fb->list_vsb != NULL)
        {
            GtkRange *range = GTK_RANGE(fb->list_vsb);
            GtkAdjustment *adj = range->adjustment;
            if(adj != NULL)
                last_scroll_y = (gint)adj->value;
        }


      /* Begin refreshing */

      /* Directory popup list */
      FileBrowserDirPUListUpdate(fb);

      /* List */
      FileBrowserListUpdate(fb, NULL);


      /* Restore scroll position. */
        if(fb->list_hsb != NULL)
        {
            GtkRange *range = GTK_RANGE(fb->list_hsb);
            GtkAdjustment *adj = range->adjustment;
            if(adj != NULL)
          {
                adj->value = CLIP(
                (float)last_scroll_x,
                adj->lower,
                MAX(adj->upper - adj->page_size, adj->lower)
            );
                gtk_signal_emit_by_name(
                    GTK_OBJECT(adj), "value_changed"
                );
          }
        }
        if(fb->list_vsb != NULL)
        {
            GtkRange *range = GTK_RANGE(fb->list_vsb);
            GtkAdjustment *adj = range->adjustment;
            if(adj != NULL)
          {
                adj->value = CLIP(
                    (float)last_scroll_y,
                    adj->lower,
                MAX(adj->upper - adj->page_size, adj->lower)
                );
                gtk_signal_emit_by_name(
                    GTK_OBJECT(adj), "value_changed"
                );
          }
        }

      FileBrowserSetBusy(fb, FALSE);
}


/*
 *    Updates the directory popup list based on the current location.
 */
static void FileBrowserDirPUListUpdate(FileBrowser *fb)
{
      const gchar *cur_location;
      pulist_struct *pulist;
      const FileBrowserIcon *icon;
      struct stat stat_buf;
      GtkDestroyNotify destroy_cb = FileBrowserDirPUListItemDestroyCB;

        if(fb == NULL)
            return;

      pulist = fb->dir_pulist;
      if(pulist == NULL)
          return;

      FileBrowserSetBusy(fb, TRUE);

      /* Delete any existing items in the list. */
      PUListClear(pulist);

#define GET_ICON(p)     {           \
 if(stat((p), &stat_buf))           \
  icon = NULL;                      \
 else                         \
  icon = FileBrowserGetIcon(        \
   fb,                              \
   FileBrowserMatchIconNumFromPath( \
    fb, (p), &stat_buf              \
   )                          \
  );                          \
}

/* Takes string s and shortens it, allocating a new string s2
 * that is a shortened (as needed) version of s.  The string s2
 * must be deallocated.
 */
#define ALLOC_STRING_SHORTENED            \
{ if(s != NULL) {             \
 gint    len = strlen(s),           \
         max_characters = FB_LOCATION_LIST_MAX_CHARACTERS;\
                              \
 /* Length of s is too long? */           \
 if(len > max_characters)           \
  s2 = g_strdup_printf(             \
   "...%s",                   \
   &s[len - max_characters + 3]           \
  );                          \
 else                         \
  s2 = STRDUP(s);             \
} else {                      \
 s2 = NULL;                   \
} }

      /* Get current location and set items for it and each parent
       * location.
       */
      cur_location = FileBrowserGetLocation(fb);
      if(cur_location != NULL)
      {
          gint icon_num;
          gchar   *s = STRDUP(cur_location),
                  *sd = strrchr(s, DIR_DELIMINATOR),
                  *s2;

          /* Current location. */
          if(stat(s, &stat_buf))
            icon_num = -1;
          else
            icon_num = FileBrowserMatchIconNumFromPath(
                fb, s, &stat_buf
            );
          if(icon_num == FB_ICON_FOLDER_CLOSED)
            icon_num = FB_ICON_FOLDER_OPENED;
          icon = FileBrowserGetIcon(fb, icon_num);
          ALLOC_STRING_SHORTENED
          if(icon != NULL)
                PUListAddItemPixText(
                    pulist, s2, icon->pixmap, icon->mask,
                    STRDUP(s), destroy_cb
                );
            else
                PUListAddItem(
                    pulist, s2,
                STRDUP(s), destroy_cb
                );
          g_free(s2);

          /* Parent locations. */
#ifdef __MSW__
          while(sd > (s + 3))
#else
          while(sd > s)
#endif
          {
            *sd = '\0';

                GET_ICON(s);
            ALLOC_STRING_SHORTENED
            if(icon != NULL)
                PUListAddItemPixText(
                  pulist, s2, icon->pixmap, icon->mask,
                  STRDUP(s), destroy_cb
                );
            else
                PUListAddItem(
                  pulist, s2,
                  STRDUP(s), destroy_cb
                );
            g_free(s2);

            sd = strrchr(s, DIR_DELIMINATOR);
          }

          /* Toplevel. */
#ifdef __MSW__
          /* On Win32, use s as is from the above parent fetching loop */
          sd = strchr(s, DIR_DELIMINATOR);
          if((sd != NULL) ? (*(sd + 1) != '\0') : FALSE)
          {
            *(sd + 1) = '\0';
            GET_ICON(s);
            ALLOC_STRING_SHORTENED
            if(icon != NULL)
                PUListAddItemPixText(
                  pulist, s2, icon->pixmap, icon->mask,
                  STRDUP(s), destroy_cb
                );
            else
                PUListAddItem(
                  pulist, s2,
                  STRDUP(s), destroy_cb
                );
            g_free(s2);
          }
          g_free(s);
#else
          g_free(s);
          s = STRDUP("/");
            GET_ICON(s);
          ALLOC_STRING_SHORTENED
          if(icon != NULL)
            PUListAddItemPixText(
                pulist, s2, icon->pixmap, icon->mask,
                STRDUP(s), destroy_cb
            );
          else
            PUListAddItem(
                    pulist, s2,
                STRDUP(s), destroy_cb
                );
          g_free(s2);
          g_free(s);
#endif
      }

#ifndef __MSW__
      /* Home */
      if(fb->home_path != NULL)
      {
          const gchar *s = fb->home_path;
            gchar *s2;
            GET_ICON(s);
          ALLOC_STRING_SHORTENED
            if(icon != NULL)
                PUListAddItemPixText(
                    pulist, s2, icon->pixmap, icon->mask,
                    STRDUP(s), destroy_cb
                );
            else
                PUListAddItem(
                    pulist, s2,
                    STRDUP(s), destroy_cb
                );
            g_free(s2);
      }
#endif

      /* Drives */
      if(fb->total_drive_paths > 0)
      {
          gint i;
          const gchar *s;
          gchar *s2;

          for(i = 0; i < fb->total_drive_paths; i++)
          {
            s = fb->drive_path[i];
            if(s == NULL)
                continue;

            /* Ignore toplevel */
            if(!strcmp(s, "/"))
                continue;
#ifdef __MSW__
            icon = FileBrowserGetIcon(fb, FB_ICON_DRIVE_FIXED);
#else
                GET_ICON(s);
#endif
            ALLOC_STRING_SHORTENED

                if(icon != NULL)
                    PUListAddItemPixText(
                        pulist, s2, icon->pixmap, icon->mask,
                        STRDUP(s), destroy_cb
                    );
                else
                    PUListAddItem(
                        pulist, s2,
                        STRDUP(s), destroy_cb
                    );
                g_free(s2);
          }


      }

/* Other things to be added to the popup list, like mounted drives? */





      FileBrowserSetBusy(fb, FALSE);

#undef GET_ICON
#undef ALLOC_STRING_SHORTENED
}

/*
 *    Redraws the directory popup list's drawing area.
 */
static void FileBrowserDirPUListDraw(FileBrowser *fb)
{
        gint state, width, height;
        GdkFont *font;
      GdkDrawable *drawable;
        GdkWindow *window;
        GdkGC *gc, *gc_text;
        GtkStyle *style;
        GtkWidget *w;
      const gchar *cur_location;


        if(fb == NULL)
            return;

      drawable = (GdkDrawable *)fb->list_pm;
      w = fb->dir_pulist_da;
      window = (w != NULL) ? w->window : NULL;
      if(window == NULL)
          return;

      if(drawable == NULL)
          drawable = (GdkDrawable *)window;

      state = GTK_WIDGET_STATE(w);
      style = gtk_widget_get_style(w);
      gdk_window_get_size(window, &width, &height);
      if((style == NULL) || (width <= 0) || (height <= 0))
          return;

      font = style->font;


        /* Draw background. */
        gdk_draw_rectangle(
            drawable, style->base_gc[state], TRUE,
            0, 0, width, height
        );

      /* Draw current location. */
        cur_location = FileBrowserGetLocation(fb);
      if(cur_location != NULL)
      {
          gint x = 2;
          gint icon_num;
          const FileBrowserIcon *icon;
          struct stat stat_buf;

          /* Match icon */
          if(!stat(cur_location, &stat_buf))
            icon_num = FileBrowserMatchIconNumFromPath(
                fb, cur_location, &stat_buf
            );
          else
            icon_num = -1;
          /* If regular folder then get the opened folder */
          if(icon_num == FB_ICON_FOLDER_CLOSED)
            icon_num = FB_ICON_FOLDER_OPENED;
          icon = FileBrowserGetIcon(fb, icon_num);

          /* Draw icon */
          gc = style->fg_gc[state];
          if(icon != NULL)
          {
            gint  cx = x,
                  cy = (height / 2) - (icon->height / 2);

            gdk_gc_set_clip_mask(gc, icon->mask);
            gdk_gc_set_clip_origin(gc, cx, cy);
            gdk_draw_pixmap(
                drawable, gc, icon->pixmap,
                0, 0, cx, cy, icon->width, icon->height
            );
            gdk_gc_set_clip_mask(gc, NULL);

            x += icon->width + FB_LIST_ICON_BORDER;
          }

          if(TRUE)
          {
            gchar *s;
            gint lbearing, rbearing, swidth, ascent, descent;
            gint  len = strlen(cur_location),
                  max_characters = FB_LOCATION_LIST_MAX_CHARACTERS;

            if(len > max_characters)
                s = g_strdup_printf(
                  "...%s",
                  &cur_location[len - max_characters + 3]
                );
            else
                s = STRDUP(cur_location);

            gc_text = style->text_gc[state];
            gdk_string_extents(
                font, s,
                &lbearing, &rbearing, &swidth, &ascent, &descent
            );
            gdk_draw_string(
                drawable, font, gc_text,
                x + lbearing,
                (height / 2) - ((ascent + descent) / 2) + ascent,
                s
            );
            g_free(s);
          }
      }


        /* Draw focus rectangle if widget is in focus. */
        if(GTK_WIDGET_HAS_FOCUS(w) && (state != GTK_STATE_INSENSITIVE))
            gdk_draw_rectangle(
                drawable, style->fg_gc[state], FALSE,
                0, 0, width - 1, height - 1
            );

      /* Send drawable to window if drawable is not the window. */
      if(drawable != (GdkDrawable *)window)
          gdk_draw_pixmap(
            window, style->fg_gc[state], drawable,
            0, 0, 0, 0, width, height
          );
}

/*
 *    Returns the icon at index i or NULL on error.
 */
static FileBrowserIcon *FileBrowserGetIcon(FileBrowser *fb, gint i)
{
      if(fb == NULL)
          return(NULL);

      if((i < 0) || (i >= fb->total_icons))
          return(NULL);
      else
          return(fb->icon[i]);
}

/*
 *    Appends an icon to the list.
 */
static FileBrowserIcon *FileBrowserIconAppend(
        FileBrowser *fb, guint8 **xpm_data, const gchar *desc
)
{
        gint i;
      GdkWindow *window;
      GtkStyle *style;
      GtkWidget *w;
        FileBrowserIcon *icon;

        if((fb == NULL) || (xpm_data == NULL))
            return(NULL);

      w = fb->toplevel;
      if(w == NULL)
          return(NULL);

      window = w->window;
      style = gtk_widget_get_style(w);

        i = MAX(fb->total_icons, 0);
        fb->total_icons = i + 1;
        fb->icon = (FileBrowserIcon **)g_realloc(
            fb->icon, fb->total_icons * sizeof(FileBrowserIcon *)
        );
        if(fb->icon == NULL)
        {
            fb->total_icons = 0;
            return(NULL);
        }

        fb->icon[i] = icon = FILE_BROWSER_ICON(g_malloc0(
            sizeof(FileBrowserIcon)
        ));
        if(icon != NULL)
      {
          icon->pixmap = gdk_pixmap_create_from_xpm_d(
            window, &icon->mask,
            (style != NULL) ? &style->bg[GTK_STATE_NORMAL] : NULL,
            (gchar **)xpm_data
          );
          gdk_window_get_size(icon->pixmap, &icon->width, &icon->height);
          icon->desc = STRDUP(desc);
      }
      return(icon);
}


/*
 *    Returns the icon index appropriate for the object specified by
 *    full_path and lstat_buf.
 */
static gint FileBrowserMatchIconNumFromPath(
        FileBrowser *fb, const gchar *full_path, struct stat *lstat_buf
)
{
      gint i, uid, gid;
      const gchar *ext;
      mode_t m;

      if(fb == NULL)
          return(FB_ICON_FILE);

      if(full_path != NULL)
      {
          const gchar *s;

#ifndef __MSW__
          /* Home directory? */
          s = fb->home_path;
          if((s != NULL) ? !strcmp(s, full_path) : FALSE)
            return(FB_ICON_FOLDER_HOME);

          /* Toplevel? */
          if(!strcmp(full_path, "/"))
            return(FB_ICON_DRIVE_ROOT);
#endif
          /* Drive path? */
          for(i = 0; i < fb->total_drive_paths; i++)
          {
            s = fb->drive_path[i];
            if((s != NULL) ? !strcmp(s, full_path) : FALSE)
                return(FB_ICON_DRIVE_FIXED);
          }

          /* Get extension (if any) */
          ext = strrchr(full_path, '.');
      }
      else
      {
          ext = NULL;
      }

      /* Get object's statistics */
      if(lstat_buf != NULL)
      {
          m = lstat_buf->st_mode;
          uid = lstat_buf->st_uid;
          gid = lstat_buf->st_gid;
      }
      else
      {
          m = 0;
          uid = 0;
          gid = 0;
      }

      /* Directory? */
        if(S_ISDIR(m))
      {
#ifdef __MSW__
          return(FB_ICON_FOLDER_CLOSED);
#else
          /* Accessable? */
          if(fb->euid != 0)
          {
            /* For non-root process id's we should check if the
             * process or the process' group owns the object and
             * respectively see if the object (the directory) is
             * executable (accessable) and return the appropriate
             * icon number.
             */
            /* This process owns object? */
            if(fb->euid == uid)
                return((m & S_IXUSR) ?
                  FB_ICON_FOLDER_CLOSED : FB_ICON_FOLDER_NOACCESS
                );
            /* This process' group id owns object? */
            else if(fb->egid == gid)
                return((m & S_IXGRP) ?
                        FB_ICON_FOLDER_CLOSED : FB_ICON_FOLDER_NOACCESS
                    );
            /* Anonymous */
            else
                return((m & S_IXOTH) ?
                        FB_ICON_FOLDER_CLOSED : FB_ICON_FOLDER_NOACCESS
                    );
          }
          else
          {
            /* Root always owns the object, so check if owner has
             * access.
             */
            return((m & S_IXUSR) ?
                        FB_ICON_FOLDER_CLOSED : FB_ICON_FOLDER_NOACCESS
                );
          }
#endif
      }

#ifdef __MSW__
      if(ext != NULL)
      {
          if(g_strcasecmp(ext, ".exe") ||
             g_strcasecmp(ext, ".com") ||
             g_strcasecmp(ext, ".bat")
          )
            return(FB_ICON_EXECUTABLE);
          else
            return(FB_ICON_FILE);
      }
      else
      {
          return(FB_ICON_FILE);
      }
#else
      if(S_ISLNK(m))
          return(FB_ICON_LINK);
        else if(S_ISFIFO(m))
          return(FB_ICON_PIPE);
        else if(S_ISSOCK(m))
            return(FB_ICON_SOCKET);
        else if(S_ISBLK(m))
            return(FB_ICON_DEVICE_BLOCK);
        else if(S_ISCHR(m))
            return(FB_ICON_DEVICE_CHARACTER);
        else if((m & S_IXUSR) || (m & S_IXGRP) || (m & S_IXOTH))
            return(FB_ICON_EXECUTABLE);
        else
            return(FB_ICON_FILE);
#endif
}


/*
 *      Returns the object at index i or NULL on error.
 */
static FileBrowserObject *FileBrowserGetObject(FileBrowser *fb, gint i)
{
        if(fb == NULL)
            return(NULL);

        if((i < 0) || (i >= fb->total_objects))
            return(NULL);
        else
            return(fb->object[i]);
}

/*
 *    Updates values on the given object.
 *
 *    The object's name and lstat_buf must be updated prior to this
 *    call since the information used to update the other values
 *    is based on the name and lstat_buf values.
 *
 *      The values that will be updated are icon_num, width, and height.
 */
static void FileBrowserObjectUpdateValues(
        FileBrowser *fb, FileBrowserObject *o
)
{
      GdkFont *font;
      GtkStyle *style;
      GtkWidget *w;
      FileBrowserIcon *icon;


      if((fb == NULL) || (o == NULL))
          return;

      /* Get the list widget's style and font */
      w = fb->list_da;
      style = (w != NULL) ? gtk_widget_get_style(w) : NULL;
      font = (style != NULL) ? style->font : NULL;

      /* Get icon number based on the object's path and statistics */
      o->icon_num = FileBrowserMatchIconNumFromPath(
          fb, o->full_path, &o->lstat_buf
      );

      /* Get icon */
      icon = FileBrowserGetIcon(fb, o->icon_num);


      /* Update size */
      if((o->name != NULL) && (font != NULL))
      {
          gint lbearing, rbearing, swidth, ascent, descent;

          gdk_string_extents(
            font, o->name,
            &lbearing, &rbearing, &swidth,
            &ascent, &descent
          );
          o->width = ((icon != NULL) ? (icon->width + FB_LIST_ICON_BORDER) : 0) +
            swidth + 2;
          o->height = MAX(
            ((icon != NULL) ? icon->height : 0),
            ascent + descent
          );
      }
      else
      {
          o->width = (icon != NULL) ? icon->width : 1;
          o->height = (icon != NULL) ? icon->height : 1;
      }
}

/*
 *    Appends an object to the list.
 *
 *    Returns the new object or NULL on error.  The calling function
 *    needs to calculate the position.
 */
static FileBrowserObject *FileBrowserObjectAppend(
      FileBrowser *fb, const gchar *name, const gchar *full_path,
      struct stat *lstat_buf
)
{
      gint i;
      FileBrowserObject *o;

        if((fb == NULL) || (full_path == NULL))
            return(NULL);

      /* Allocate one more pointer for the array. */
      i = MAX(fb->total_objects, 0);
      fb->total_objects = i + 1;
      fb->object = (FileBrowserObject **)g_realloc(
          fb->object, fb->total_objects * sizeof(FileBrowserObject *)
      );
      if(fb->object == NULL)
      {
          fb->total_objects = 0;
          return(NULL);
      }

      /* Allocate a new object. */
      fb->object[i] = o = FILE_BROWSER_OBJECT(g_malloc0(
          sizeof(FileBrowserObject)
      ));
      if(o != NULL)
      {
          o->name = STRDUP(name);
          o->full_path = STRDUP(full_path);
          if(lstat_buf != NULL)
            memcpy(&o->lstat_buf, lstat_buf, sizeof(struct stat));
          else
            memset(&o->lstat_buf, 0x00, sizeof(struct stat));

          /* Update values. */
          FileBrowserObjectUpdateValues(fb, o);
      }

      return(o);
}

/*
 *    Updates the list.
 */
static void FileBrowserListUpdate(FileBrowser *fb, const gchar *filter)
{
      gint i, width, height;
      gint border_major = 5, border_minor = 2;
      gboolean match_all_files;
      gboolean need_resize = FALSE;
      gint  list_x_max = 0, list_y_max = 0,
            list_x_inc = 0, list_x_page_inc = 0,
            list_y_inc = 0,   list_y_page_inc = 0;
      const gchar *cur_location, *ext;
      GdkWindow *window;
      GtkWidget *w;

        if(fb == NULL)
            return;

      FileBrowserSetBusy(fb, TRUE);

      /* Get size of list. */
      w = fb->list_da;
      window = (w != NULL) ? w->window : NULL;
      if(window != NULL)
      {
          gdk_window_get_size(window, &width, &height);
      }
      else
      {
          width = 0;
          height = 0;
      }

      /* Get extension specified in the current file type. */
      ext = (filter != NULL) ? filter : fb->cur_type.ext;
      if(ext != NULL)
          match_all_files = (!strcmp(ext, "*.*") || !strcmp(ext, "*")) ? TRUE : FALSE;
      else
          match_all_files = TRUE;

      /* Get listing of contents in current location. */

      /* Delete current objects list and the selection. */

      /* Unselect all */
      fb->focus_object = -1;
      g_list_free(fb->selection);
      fb->selection = fb->selection_end = NULL;

      /* Delete all objects */
      for(i = 0; i < fb->total_objects; i++)
          FileBrowserObjectDestroyCB(fb->object[i]);
      g_free(fb->object);
      fb->object = NULL;
      fb->total_objects = 0;  

      /* Reset objects per row */
      fb->objects_per_row = 0;


      /* Get new objects. */
      cur_location = FileBrowserGetLocation(fb);
      if(cur_location != NULL)
      {
          gint strc, objects_per_row = 0;
          gchar **strv = GetDirEntNames2(cur_location, &strc);
          if(strv != NULL)
          {
            gchar *s, *full_path;
            struct stat lstat_buf;
            FileBrowserObject *o = NULL;
            gint  border_x = border_minor,
                  border_y = border_minor,
                  cur_x = border_x,
                  cur_y = border_y,
                  longest_width = 0,
                  last_longest_width = longest_width;

            StringQSort(strv, strc);

/* Sets the x and y position of the object o.  Uses cur_x and cur_y to
 * keep track of the last position.
 */
#define SET_OBJECT_POSITION   {           \
 if(o != NULL) {                    \
  switch(fb->list_format) {               \
   case FB_LIST_FORMAT_VERTICAL_DETAILS:  \
   case FB_LIST_FORMAT_VERTICAL:          \
    if(longest_width < o->width)                \
    {                                           \
     last_longest_width = longest_width =       \
      o->width;                                 \
    }                                           \
    o->x = cur_x;                   \
    o->y = cur_y;                   \
    cur_y += o->height + 1;               \
    break;                          \
   default: /* FB_LIST_FORMAT_STANDARD */ \
    if(longest_width < o->width)          \
    {                               \
     /* Note that last_longest_width gets \
      * updated with longest_width but is not   \
      * reset to 0 when we go to a new column,  \
      * this is so that last_longest_width can  \
      * be used in case longest_width was reset \
      * before the next column but when we get  \
      * to the next column there are no objects \
      * and longest_width = 0 is not really     \
      * accurate anymore.                 \
      */                            \
     last_longest_width = longest_width = \
      o->width;                           \
    }                               \
    o->x = cur_x;                               \
    o->y = cur_y;                               \
    cur_y += o->height + 1;               \
    objects_per_row++;                    \
    /* Need to increment "column" when next     \
     * object height would go off the list.     \
     */                                   \
    if((cur_y + o->height) > height) {    \
     if(objects_per_row > fb->objects_per_row)  \
      fb->objects_per_row = objects_per_row;    \
     objects_per_row = 0;                 \
     cur_y = border_y;                    \
     cur_x += longest_width + border_major;     \
     longest_width = 0;                   \
    }                               \
    break;                          \
  }                                 \
 }                                  \
}

            /* We will make several passes through the strv array
             * to load objects in proper order, directories first
             * and all other types of objects afterwards.  Each
             * string in strv is deleted as the object is added
             * or determined to be filtered from the list.
             */

            /* Add directories. */
            for(i = 0; i < strc; i++)
            {
                s = strv[i];
                if(s == NULL)
                  continue;

                /* Skip special directory notations. */
                if(!strcmp(s, ".") ||
                   !strcmp(s, "..")
                )
                {
                  g_free(strv[i]);
                  strv[i] = s = NULL;
                  continue;
                }

                /* Generate full path to object. */
                full_path = STRDUP(PrefixPaths(cur_location, s));

                /* Skip objects that are not or do not go to a directory. */
                if(!ISPATHDIR(full_path))
                {
                  g_free(full_path);
                  /* Do not delete string from strv, let
                   * subsequent loops through strv handle it.
                   */
                  continue;
                }

                /* Get local stats of object and append object to the
                 * list.
                 */
#ifdef __MSW__
                if(stat(full_path, &lstat_buf))
#else
                if(lstat(full_path, &lstat_buf))
#endif
                  memset(&lstat_buf, 0x00, sizeof(struct stat));
                o = FileBrowserObjectAppend(fb, s, full_path, &lstat_buf);
                SET_OBJECT_POSITION

                /* Delete full path, it is no longer needed. */
                g_free(full_path);

                /* Delete this name so it dosen't get messed with on
                 * the next loop.
                 */
                g_free(strv[i]);
                strv[i] = s = NULL;
            }

                /* Add all other objects. */
                for(i = 0; i < strc; i++)
                {
                    s = strv[i];
                    if(s == NULL)
                        continue;

                    /* Generate full path to object. */
                full_path = STRDUP(PrefixPaths(cur_location, s));

                /* Check if this object name gets filtered. */
                if(match_all_files ? FALSE : !FileBrowserObjectNameFilter(
                  s, full_path, ext
                ))
                {
                  g_free(full_path);
                  g_free(s);
                  strv[i] = s = NULL;
                  continue;
                }

                    /* Get local stats of object and append object to the
                     * list.
                     */
#ifdef __MSW__
                if(stat(full_path, &lstat_buf))
#else
                    if(lstat(full_path, &lstat_buf))
#endif
                  memset(&lstat_buf, 0x00, sizeof(struct stat));
                    o = FileBrowserObjectAppend(fb, s, full_path, &lstat_buf);
                SET_OBJECT_POSITION

                    /* Delete full path, it is no longer needed. */
                    g_free(full_path);

                    g_free(s);
                    strv[i] = s = NULL;
                }

            /* No more loops, delete string list strv.  All strings
             * remaining in strv should have been deleted in the
             * last loop above.
             */
            g_free(strv);

            /* Use last object to set list bounds, these bounds will
             * be used in updating of the scrollbar adjustments.
             */
            if(o != NULL)
            {
                list_x_max = o->x + last_longest_width + border_x;
                list_y_max = o->y + o->height + border_y;
                list_x_inc = (gint)(width * 0.25);
                list_x_page_inc = (gint)(width * 0.5);
                    list_y_inc = o->height;
                    list_y_page_inc = (gint)(height * 0.5);
            }
#undef SET_OBJECT_POSITION
          }
      }


      /* Update widgets based on list format. */
        switch(fb->list_format)
        {
          case FB_LIST_FORMAT_VERTICAL_DETAILS:
          case FB_LIST_FORMAT_VERTICAL:
          w = fb->list_hsb;
          if((w != NULL) ? GTK_WIDGET_MAPPED(w) : FALSE)
          {
            gtk_widget_hide(w);
            need_resize = TRUE;
          }
            w = fb->list_vsb;
            if(w != NULL)
          {
                GtkRange *range = GTK_RANGE(w);
                GtkAdjustment *adj = range->adjustment;
            if(adj != NULL)
            {
                adj->lower = 0.0f;
                adj->upper = (gfloat)list_y_max;
                adj->value = adj->lower;
                adj->step_increment = (gfloat)list_y_inc;
                adj->page_increment = (gfloat)list_y_page_inc;
                adj->page_size = (gfloat)height;
                gtk_signal_emit_by_name(
                  GTK_OBJECT(adj), "changed"
                );
                    gtk_signal_emit_by_name(
                        GTK_OBJECT(adj), "value_changed"
                    );

                /* If content size is larger than visible size then
                 * map the scrollbar, otherwise unmap the scrollbar
                 * since it would not be needed.
                 */
                if((adj->upper - adj->lower) > adj->page_size)
                {
                  if(!GTK_WIDGET_MAPPED(w))
                  {
                      gtk_widget_show(w);
                      need_resize = TRUE;
                  }
                }
                else
                    {
                        if(GTK_WIDGET_MAPPED(w))
                        {
                            gtk_widget_hide(w);
                            need_resize = TRUE;
                        }
                    }
            }
          }
            break;

          default:      /* FB_LIST_FORMAT_STANDARD */
            w = fb->list_vsb;
            if((w != NULL) ? GTK_WIDGET_MAPPED(w) : FALSE)
          {
                gtk_widget_hide(w);
            need_resize = TRUE;
          }
            w = fb->list_hsb;
            if(w != NULL)
            {
            GtkRange *range = GTK_RANGE(w);
            GtkAdjustment *adj = range->adjustment;
            if(!GTK_WIDGET_MAPPED(w))
            {
                gtk_widget_show(w);
                need_resize = TRUE;
            }
                if(adj != NULL)
                {
                    adj->lower = 0.0f;
                    adj->upper = (gfloat)list_x_max;
                    adj->value = adj->lower;
                    adj->step_increment = (gfloat)list_x_inc;
                    adj->page_increment = (gfloat)list_x_page_inc;
                    adj->page_size = (gfloat)width;
                    gtk_signal_emit_by_name(
                        GTK_OBJECT(adj), "changed"
                    );
                    gtk_signal_emit_by_name(
                        GTK_OBJECT(adj), "value_changed"
                    );

                    /* If content size is larger than visible size then
                     * map the scrollbar, otherwise unmap the scrollbar
                     * since it would not be needed.
                     */
                    if((adj->upper - adj->lower) > adj->page_size)
                    {
                        if(!GTK_WIDGET_MAPPED(w))
                        {
                            gtk_widget_show(w);
                            need_resize = TRUE;
                        }
                    }
                    else
                    {
                        if(GTK_WIDGET_MAPPED(w))
                        {
                            gtk_widget_hide(w);
                            need_resize = TRUE;
                        }
                    }
                }
            }
            break;
        }

      /* Need to resize due to widgets being mapped or unmapped? */
      if(need_resize)
          gtk_widget_queue_resize(fb->toplevel);

      FileBrowserSetBusy(fb, FALSE);
}


/*
 *    Sets the DND icon based on the object i.
 */
static void FileBrowserListObjectSetDNDIcon(FileBrowser *fb, gint i)
{
      FileBrowserIcon *icon;
        FileBrowserObject *o = FileBrowserGetObject(fb, i);
        if(fb == NULL)
            return;

      /* Get object's icon (if any) */
      icon = FileBrowserGetIcon(fb, o->icon_num);
      if(icon == NULL)
          return;

      /* Set new DND icon if it has a pixmap */
      if(icon->pixmap != NULL)
            GUIDNDSetDragIcon(
            icon->pixmap, icon->mask,
            icon->width / 2, icon->height / 2
          );
}

/*
 *    Returns one of GTK_VISIBILITY_* based on the visibility of
 *    object i.
 */
static gint FileBrowserListObjectVisibility(FileBrowser *fb, gint i)
{
      gint scroll_x = 0, scroll_y = 0;
      gint x, y, width, height;
      GdkWindow *window;
      GtkWidget *w;
      FileBrowserObject *o;

      if(fb == NULL)
          return(GTK_VISIBILITY_NONE);

      /* Get list's GdkWindow and its size, making sure it exists and
       * its size is positive.
       */
      w = fb->list_da;
      window = (w != NULL) ? w->window : NULL;
      if(window == NULL)
          return(GTK_VISIBILITY_NONE);
      gdk_window_get_size(window, &width, &height);
      if((width <= 0) || (height <= 0))
          return(GTK_VISIBILITY_NONE);

      /* Get object */
      o = FileBrowserGetObject(fb, i);
      if(o == NULL)
          return(GTK_VISIBILITY_NONE);

        /* Get current scroll position. */
        if(fb->list_hsb != NULL)
        {
            GtkRange *range = GTK_RANGE(fb->list_hsb);
            GtkAdjustment *adj = range->adjustment;
            scroll_x = (gint)((adj != NULL) ? adj->value : 0.0f);
        }
        if(fb->list_vsb != NULL)
        {
            GtkRange *range = GTK_RANGE(fb->list_vsb);
            GtkAdjustment *adj = range->adjustment;
            scroll_y = (gint)((adj != NULL) ? adj->value : 0.0f);
        }

      /* Check visibility by list display format. */
        switch(fb->list_format)
        {
          case FB_LIST_FORMAT_VERTICAL_DETAILS:
          case FB_LIST_FORMAT_VERTICAL:
          y = o->y - scroll_y;
          if(((y + o->height) <= 0) || (y >= height))
            return(GTK_VISIBILITY_NONE);
          else if((y < 0) || ((y + o->height) > height))
            return(GTK_VISIBILITY_PARTIAL);
          else
            return(GTK_VISIBILITY_FULL);
          break;

        default:  /* FB_LIST_FORMAT_STANDARD */
          x = o->x - scroll_x;
            if(((x + o->width) <= 0) || (x >= width))
                return(GTK_VISIBILITY_NONE);
            else if((x < 0) || ((x + o->width) > width))
                return(GTK_VISIBILITY_PARTIAL);
            else
                return(GTK_VISIBILITY_FULL);
            break;
      }

      return(GTK_VISIBILITY_NONE);
}

/*
 *    Scrolls to the object i, using the given coefficient as an
 *    offset within the list window for the final scroll position.
 */
static void FileBrowserListMoveToObject(
        FileBrowser *fb, gint i, gfloat coeff
)
{
        gint x, y, width, height;
        GdkWindow *window;
        GtkWidget *w;
        FileBrowserObject *o;

        if(fb == NULL)
          return;

      /* Get list's GdkWindow and its size, make sure that it exists
       * and its size is positive.
       */
        w = fb->list_da;
        window = (w != NULL) ? w->window : NULL;
        if(window == NULL)
            return;
        gdk_window_get_size(window, &width, &height);
        if((width <= 0) || (height <= 0))
            return;

      /* Get object */
      o = FileBrowserGetObject(fb, i);
        if(o == NULL)
            return;

      /* Move to object depending on list display format. */
        switch(fb->list_format)
        {
          case FB_LIST_FORMAT_VERTICAL_DETAILS:
          case FB_LIST_FORMAT_VERTICAL:
          y = o->y + (o->height / 2);
          y -= (gint)(height * CLIP(coeff, 0.0f, 1.0f));
          if(fb->list_vsb != NULL)
          {
            GtkRange *range = GTK_RANGE(fb->list_vsb);
            GtkAdjustment *adj = range->adjustment;
            if(adj != NULL)
            {
                adj->value = CLIP(
                        y,
                        adj->lower,
                  MAX(adj->upper - adj->page_size, adj->lower)
                    );
                gtk_signal_emit_by_name(
                  GTK_OBJECT(adj), "value_changed"
                );
            }
          }
          break;

        default:  /* FB_LIST_FORMAT_STANDARD */
            x = o->x + (o->width / 2);
            x -= (gint)(width * CLIP(coeff, 0.0f, 1.0f));
            if(fb->list_hsb != NULL)
            {
                GtkRange *range = GTK_RANGE(fb->list_hsb);
                GtkAdjustment *adj = range->adjustment;
                if(adj != NULL)
                {
                    adj->value = CLIP(
                  x,
                  adj->lower,
                  MAX(adj->upper - adj->page_size, adj->lower)
                );
                    gtk_signal_emit_by_name(
                        GTK_OBJECT(adj), "value_changed"
                    );
                }
            }
            break;
      }
}

/*
 *    Returns the object index number that is found at the given
 *    coordinates or -1 on failed match.
 */
static gint FileBrowserListSelectCoordinates(
      FileBrowser *fb, gint x, gint y
)
{
        gint scroll_x = 0, scroll_y = 0;

        if(fb == NULL)
            return(-1);

        /* Get scroll position. */
        if(fb->list_hsb != NULL)
        {
            GtkRange *range = GTK_RANGE(fb->list_hsb);
            GtkAdjustment *adj = range->adjustment;
            scroll_x = (gint)((adj != NULL) ? adj->value : 0.0f);
        }
        if(fb->list_vsb != NULL)
        {
            GtkRange *range = GTK_RANGE(fb->list_vsb);
            GtkAdjustment *adj = range->adjustment;
            scroll_y = (gint)((adj != NULL) ? adj->value : 0.0f);
        }

        /* Find object by list display format. */
        switch(fb->list_format)
        {
            gint i, cx, cy;
            FileBrowserObject *o;

          case FB_LIST_FORMAT_VERTICAL_DETAILS:
          case FB_LIST_FORMAT_VERTICAL:
            for(i = 0; i < fb->total_objects; i++)
            {
                o = fb->object[i];
                if(o == NULL)
                    continue;

                if((o->width <= 0) || (o->height <= 0))
                    continue;

                /* Calculate object position with scrolled adjust. */
                cx = o->x;
                cy = o->y - scroll_y;

                /* In bounds? */
                if((x >= cx) && (x < (cx + o->width)) &&
                   (y >= cy) && (y < (cy + o->height))
                )
                    return(i);
            }
            break;

          default:      /* FB_LIST_FORMAT_STANDARD */
            for(i = 0; i < fb->total_objects; i++)
            {
                o = fb->object[i];
                if(o == NULL)
                    continue;

                if((o->width <= 0) || (o->height <= 0))
                    continue;

            /* Calculate object position with scrolled adjust. */
                cx = o->x - scroll_x;
                cy = o->y;

            /* In bounds? */
            if((x >= cx) && (x < (cx + o->width)) &&
                   (y >= cy) && (y < (cy + o->height))
            )
                return(i);
          }
            break;
      }

      return(-1);
}

/*
 *    Redraws the list header.
 */
static void FileBrowserListHeaderDraw(FileBrowser *fb)
{
        gint state, width, height;
        GdkFont *font;
      GdkDrawable *drawable;
        GdkWindow *window;
        GtkStyle *style;
        GtkWidget *w;


        if(fb == NULL)
            return;

      drawable = (GdkDrawable *)fb->list_pm;
        w = fb->list_header_da;
        window = (w != NULL) ? w->window : NULL;
        if(window == NULL)
            return;

      if(drawable == NULL)
          drawable = (GdkDrawable *)window;

        state = GTK_WIDGET_STATE(w);
        style = gtk_widget_get_style(w);
        gdk_window_get_size(window, &width, &height);
        if((style == NULL) || (width <= 0) || (height <= 0))
            return;

        font = style->font;

        /* Draw background. */
        gdk_draw_rectangle(
            drawable, style->bg_gc[state], TRUE,
            0, 0, width, height
        );


      /* Draw frame around entire header */
      gtk_draw_box(
          style, drawable, state,
          GTK_SHADOW_OUT,
          0, 0, width, height
      );

      /* Any column headings to draw? */
      if(fb->total_columns > 0)
      {
          gint i, last_column_position = 0;
          GdkRectangle rect;
          FileBrowserColumn *column;
          GdkGC *gc_text;

          /* Draw each column heading */
          for(i = 0; i < fb->total_columns; i++)
          {
            column = fb->column[i];
            if(column == NULL)
                continue;

            rect.x = last_column_position;
            rect.y = 0;
            rect.width = MAX(column->position - last_column_position, 0);
            rect.height = height;

                gtk_draw_box(
                    style, drawable, state,
                    GTK_SHADOW_OUT,
                    rect.x, rect.y, rect.width, rect.height
                );

            gc_text = (column->flags & GTK_SENSITIVE) ?
                style->text_gc[state] : style->text_gc[GTK_STATE_INSENSITIVE];

            if((column->label != NULL) ? (*column->label != '\0') : FALSE)
            {
                const gchar *label = column->label;
                    gint lbearing, rbearing, swidth, ascent, descent;
                    gdk_string_extents(
                        font, label,
                        &lbearing, &rbearing, &swidth, &ascent, &descent
                    );
                gdk_gc_set_clip_origin(gc_text, 0, 0);
                gdk_gc_set_clip_rectangle(gc_text, &rect);
                switch(column->label_justify)
                {
                  case GTK_JUSTIFY_CENTER:
                  gdk_draw_string(
                      drawable, font, gc_text,
                      rect.x + (rect.width / 2) - (swidth / 2) + lbearing,
                      rect.y + (rect.height / 2) - ((ascent + descent) / 2) + ascent,
                      label
                  );
                  break;

                      case GTK_JUSTIFY_RIGHT:
                        gdk_draw_string(
                            drawable, font, gc_text,
                            rect.x + rect.width - swidth - 5 + lbearing,
                            rect.y + (rect.height / 2) - ((ascent + descent) / 2) + ascent,
                            label
                        );
                        break;

                      default:      /* GTK_JUSTIFY_LEFT */
                        gdk_draw_string(
                            drawable, font, gc_text,
                            rect.x + 5 + lbearing,
                            rect.y + (rect.height / 2) - ((ascent + descent) / 2) + ascent,
                            label
                        );
                  break;
                }
                gdk_gc_set_clip_rectangle(gc_text, NULL);
            }

            if((column->flags & GTK_SENSITIVE) &&
                   (column->flags & GTK_HAS_FOCUS) &&
               GTK_WIDGET_HAS_FOCUS(w) &&
               GTK_WIDGET_SENSITIVE(w)
            )
                gdk_draw_rectangle(
                  drawable, style->fg_gc[state], FALSE,
                  rect.x, rect.y, rect.width - 1, rect.height - 1
                );

            last_column_position = column->position;
          }
      }

      /* Send drawable to window if drawable is not the window. */
      if(drawable != (GdkDrawable *)window)
          gdk_draw_pixmap(
            window, style->fg_gc[state], drawable,
            0, 0, 0, 0, width, height
          );
}

/*
 *    Redraws the list.
 */
static void FileBrowserListDraw(FileBrowser *fb)
{
      gint state, width, height;
      gint scroll_x = 0, scroll_y = 0;
      GdkFont *font;
      GdkDrawable *drawable;
      GdkWindow *window;
      GtkStyle *style;
        GtkWidget *w;


        if(fb == NULL)
            return;

      drawable = (GdkDrawable *)fb->list_pm;
      w = fb->list_da;
      window = (w != NULL) ? w->window : NULL;
      if(window == NULL)
          return;

      if(drawable == NULL)
          drawable = (GdkDrawable *)window;

      state = GTK_WIDGET_STATE(w);
        style = gtk_widget_get_style(w);
        gdk_window_get_size(window, &width, &height);
      if((style == NULL) || (width <= 0) || (height <= 0))
          return;

      font = style->font;


      /* Draw background. */
      gdk_draw_rectangle(
          drawable, style->base_gc[state], TRUE,
          0, 0, width, height
      );


      /* Get scroll position. */
      if(fb->list_hsb != NULL)
      {
          GtkRange *range = GTK_RANGE(fb->list_hsb);
          GtkAdjustment *adj = range->adjustment;
          scroll_x = (gint)((adj != NULL) ? adj->value : 0.0f);
      }
        if(fb->list_vsb != NULL)
        {
            GtkRange *range = GTK_RANGE(fb->list_vsb);
            GtkAdjustment *adj = range->adjustment;
            scroll_y = (gint)((adj != NULL) ? adj->value : 0.0f);
        }

      /* Begin drawing objects by list display format. */
      switch(fb->list_format)
      {
          gboolean o_is_selected;
          gint lbearing, rbearing, swidth, ascent, descent;
          gint i, x, y, icon_width;
          GdkGC *gc, *gc_text;
          const FileBrowserObject *o;
          const FileBrowserIcon *icon;

        case FB_LIST_FORMAT_VERTICAL_DETAILS:
          /* Make sure we have enough columns to draw for this
           * list display format.
           */
          if(fb->total_columns >= 4)
          {
            mode_t m;
            const struct stat *lstat_buf;
            GdkRectangle rect;
            const FileBrowserColumn *column[4];

            /* Get pointer to all columns */
            for(i = 0; i < 4; i++)
                column[i] = fb->column[i];

            /* Iterate through objects. */
            for(i = 0; i < fb->total_objects; i++)
            {
                o = fb->object[i];
                    if(o == NULL)
                        continue;

                    if((o->width <= 0) || (o->height <= 0))
                        continue;

                    /* Object off screen? */
                    x = o->x;
                    y = o->y - scroll_y;
                    if(((y + o->height) < 0) || (y >= height))
                        continue;

                /* Get object values. */
                lstat_buf = &o->lstat_buf;
                m = lstat_buf->st_mode;

                /* Get rectangle for the limits of the object's
                 * name area.
                 */
                rect.x = x - 0;
                rect.y = y;
                rect.width = MIN(o->width, column[0]->position - 0);
                rect.height = o->height;

                /* Is object selected? */
                o_is_selected = (g_list_find(fb->selection, (gpointer)i) != NULL) ?
                  TRUE : FALSE;
                if(o_is_selected)
                {
                  gc = style->fg_gc[GTK_STATE_SELECTED];
                  gc_text = style->text_gc[GTK_STATE_SELECTED];
                  gdk_draw_rectangle(
                      drawable, style->bg_gc[GTK_STATE_SELECTED], TRUE,
                      rect.x, rect.y, rect.width, rect.height
                  );
                }
                else
                {
                  gc = style->fg_gc[state];
                  gc_text = style->text_gc[state];
                }

                /* Draw Icon */
                icon = FileBrowserGetIcon(fb, o->icon_num);
                if((icon != NULL) ? (icon->pixmap != NULL) : FALSE)
                {
                  GdkBitmap *mask = icon->mask;
                  GdkPixmap *pixmap = icon->pixmap;
                        gdk_gc_set_clip_origin(gc, x, y);
                        gdk_gc_set_clip_mask(gc, mask);
                        gdk_draw_pixmap(
                            drawable, gc, pixmap,
                            0, 0, x, y, icon->width, icon->height
                        );
                        gdk_gc_set_clip_mask(gc, NULL);
                  icon_width = icon->width;
                }
                else
                {
                  icon_width = 0;
                }

                    /* Set up clip rectangle for drawing of the
                     * object's name cell.
                     */
                    gdk_gc_set_clip_origin(gc, 0, 0);
                    gdk_gc_set_clip_rectangle(gc, &rect);
                    gdk_gc_set_clip_origin(gc_text, 0, 0);
                    gdk_gc_set_clip_rectangle(gc_text, &rect);

                /* Draw Name */
                    if(o->name != NULL)
                    {
                  const gchar *s = o->name;
                        gdk_string_extents(
                      font, s,
                      &lbearing, &rbearing, &swidth, &ascent, &descent
                        );
                        gdk_draw_string(
                            drawable, font, gc_text,
                            x + icon_width + FB_LIST_ICON_BORDER + lbearing,
                            y + (o->height / 2) - ((ascent + descent) / 2) + ascent,
                            s
                        );
                    }
                else
                {
                  /* No name, but we still need to have font extents
                   * for use with drawing the subsequent cells below.
                   */
                        gdk_string_extents(
                            font, "X",
                            &lbearing, &rbearing, &swidth, &ascent, &descent
                        );
                }

                /* If the object was selected it means we drew the
                 * icon and name with the selected gc's. We should
                 * now restore the gc's and use the current state
                 * gc's for drawing subsequent values.
                 */
                if(o_is_selected)
                {
                  gdk_gc_set_clip_rectangle(gc, NULL);
                        gdk_gc_set_clip_rectangle(gc_text, NULL);
                  gc = style->fg_gc[state];
                  gc_text = style->text_gc[state];
                }

                    /* Draw Size */
                if(S_ISREG(m))
                {
                  gchar s[80];
                  sprintf(s, "%ld", lstat_buf->st_size);
                  gdk_string_extents(
                      font, s,
                      NULL, NULL, &swidth, NULL, NULL
                  );
                  rect.x = column[0]->position;
                  rect.width = column[1]->position - column[0]->position;
                        if(rect.width > 0)
                            gdk_gc_set_clip_rectangle(gc_text, &rect);
                        gdk_draw_string(
                            drawable, font, gc_text,
                            rect.x + rect.width - swidth - 2 + lbearing,
                            y + (o->height / 2) - ((ascent + descent) / 2) + ascent,
                            s
                        );
                    }

                    /* Draw Permissions */
#ifdef S_ISLNK
                if(!S_ISLNK(m))
#else
                if(TRUE)
#endif
                {
                  gchar s[80];
#if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
                  sprintf(
                      s,
                      "%c%c%c%c%c%c%c%c%c",
                      (m & S_IRUSR) ? 'r' : '-',
                            (m & S_IWUSR) ? 'w' : '-',
                            (m & S_ISUID) ? 'S' :
                        ((m & S_IXUSR) ? 'x' : '-'),
                            (m & S_IRGRP) ? 'r' : '-',
                            (m & S_IWGRP) ? 'w' : '-',
                            (m & S_ISGID) ? 'G' :
                                ((m & S_IXGRP) ? 'x' : '-'),
                            (m & S_IROTH) ? 'r' : '-',
                            (m & S_IWOTH) ? 'w' : '-',
                            (m & S_ISVTX) ? 'T' :
                                ((m & S_IXOTH) ? 'x' : '-')
                  );
#else
                  strcpy(s, "rwxrwxrwx");
#endif
                        gdk_string_extents(
                            font, s,
                            NULL, NULL, &swidth, NULL, NULL
                        );
                        rect.x = column[1]->position;
                        rect.width = column[2]->position - column[1]->position;
                        if(rect.width > 0)
                            gdk_gc_set_clip_rectangle(gc_text, &rect);
                        gdk_draw_string(
                            drawable, font, gc_text,
                            rect.x + 2 + lbearing,
                            y + (o->height / 2) - ((ascent + descent) / 2) + ascent,
                            s
                        );
                    }

                    /* Draw Last Modified Date */
                    if(S_ISREG(m))
                    {
                  time_t mtime = lstat_buf->st_mtime;
                  gchar *s = STRDUP((mtime > 0) ? ctime(&mtime) : "*undefined*");
                  gchar *s2 = strchr(s, '\n');
                  if(s2 == NULL)
                      s2 = strchr(s, '\r');
                  if(s2 != NULL)
                      *s2 = '\0';
                  gdk_string_extents(
                            font, s,
                            NULL, NULL, &swidth, NULL, NULL
                        );
                        rect.x = column[2]->position;
                        rect.width = column[3]->position - column[2]->position;
                        if(rect.width > 0)
                            gdk_gc_set_clip_rectangle(gc_text, &rect);
                        gdk_draw_string(
                            drawable, font, gc_text,
                            rect.x + 2 + lbearing,
                            y + (o->height / 2) - ((ascent + descent) / 2) + ascent,
                            s
                        );
                  g_free(s);
                }

                /* Restore gc */
                    gdk_gc_set_clip_rectangle(gc_text, NULL);
                    gdk_gc_set_clip_rectangle(gc, NULL);

                /* Draw focus rectangle around object? */
                if((i == fb->focus_object) && GTK_WIDGET_HAS_FOCUS(w))
                {
                  GdkGCValues gcv;
                  gdk_gc_get_values(gc, &gcv);
                  gdk_gc_set_function(gc, GDK_INVERT);
                  rect.x = x - 0;
                  rect.width = MIN(o->width, column[0]->position - 0);
                  gdk_draw_rectangle(
                      drawable, gc, FALSE,
                      rect.x, rect.y,
                      rect.width - 1, rect.height - 1
                  );
                  gdk_gc_set_function(gc, gcv.function);
                }
            }
          }
          break;

          case FB_LIST_FORMAT_VERTICAL:
            for(i = 0; i < fb->total_objects; i++)
            {
                o = fb->object[i];
                if(o == NULL)
                    continue;

                if((o->width <= 0) || (o->height <= 0))
                    continue;

                /* Object off screen? */
                x = o->x;
                y = o->y - scroll_y;
                if(((y + o->height) < 0) || (y >= height))
                    continue;

                /* Is object selected? */
                o_is_selected = (g_list_find(fb->selection, (gpointer)i) != NULL) ?
                    TRUE : FALSE;
                if(o_is_selected)
                {
                    gc = style->fg_gc[GTK_STATE_SELECTED];
                    gc_text = style->text_gc[GTK_STATE_SELECTED];
                    gdk_draw_rectangle(
                        drawable, style->bg_gc[GTK_STATE_SELECTED], TRUE,
                        x, y, o->width, o->height
                    );
                }
                else
                {
                    gc = style->fg_gc[state];
                    gc_text = style->text_gc[state];
                }

                /* Draw icon. */
                icon = FileBrowserGetIcon(fb, o->icon_num);
                if((icon != NULL) ? (icon->pixmap != NULL) : FALSE)
                {
                    GdkBitmap *mask = icon->mask;
                GdkPixmap *pixmap = icon->pixmap;
                    gdk_gc_set_clip_origin(gc, x, y);
                    gdk_gc_set_clip_mask(gc, mask);
                    gdk_draw_pixmap(
                        drawable, gc, pixmap,
                        0, 0, x, y, icon->width, icon->height
                    );
                    gdk_gc_set_clip_mask(gc, NULL);
                    icon_width = icon->width;
                }
                else
                {
                    icon_width = 0;
                }

                /* Draw name. */
                if(o->name != NULL)
                {
                    const gchar *s = o->name;
                    gdk_string_extents(
                        font, s,
                        &lbearing, &rbearing, &swidth, &ascent, &descent
                    );
                    gdk_draw_string(
                        drawable, font, gc_text,
                        x + icon_width + FB_LIST_ICON_BORDER + lbearing,
                        y + (o->height / 2) - ((ascent + descent) / 2) + ascent,
                        s
                    );
                }

                /* Draw focus rectangle around object? */
                if((i == fb->focus_object) && GTK_WIDGET_HAS_FOCUS(w))
                {
                    GdkGCValues gcv;
                    gdk_gc_get_values(gc, &gcv);
                    gdk_gc_set_function(gc, GDK_INVERT);
                    gdk_draw_rectangle(
                        drawable, gc, FALSE,
                        x, y, o->width - 1, o->height - 1
                    );
                    gdk_gc_set_function(gc, gcv.function);
                }
            }
            break;

        default:  /* FB_LIST_FORMAT_STANDARD */
          for(i = 0; i < fb->total_objects; i++)
          {
            o = fb->object[i];
            if(o == NULL)
                continue;

            if((o->width <= 0) || (o->height <= 0))
                continue;

            /* Object off screen? */
            x = o->x - scroll_x;
            y = o->y;
            if(((x + o->width) < 0) || (x >= width))
                continue;

                /* Is object selected? */
                o_is_selected = (g_list_find(fb->selection, (gpointer)i) != NULL) ?
                    TRUE : FALSE;
                if(o_is_selected)
            {
                gc = style->fg_gc[GTK_STATE_SELECTED];
                    gc_text = style->text_gc[GTK_STATE_SELECTED];
                gdk_draw_rectangle(
                  drawable, style->bg_gc[GTK_STATE_SELECTED], TRUE,
                  x, y, o->width, o->height
                );
            }
            else
                {
                    gc = style->fg_gc[state];
                    gc_text = style->text_gc[state];
                }

            /* Draw icon. */
            icon = FileBrowserGetIcon(fb, o->icon_num);
            if((icon != NULL) ? (icon->pixmap != NULL) : FALSE)
            {
                GdkBitmap *mask = icon->mask;
                GdkPixmap *pixmap = icon->pixmap;
                gdk_gc_set_clip_origin(gc, x, y);
                gdk_gc_set_clip_mask(gc, mask);
                gdk_draw_pixmap(
                  drawable, gc, pixmap,
                  0, 0, x, y, icon->width, icon->height
                );
                gdk_gc_set_clip_mask(gc, NULL);
                icon_width = icon->width;
            }
            else
            {
                icon_width = 0;
            }

            /* Draw name. */
            if(o->name != NULL)
            {
                const gchar *s = o->name;
                gdk_string_extents(
                  font, s,
                  &lbearing, &rbearing, &swidth, &ascent, &descent
                );
                gdk_draw_string(
                  drawable, font, gc_text,
                  x + icon_width + FB_LIST_ICON_BORDER + lbearing,
                  y + (o->height / 2) - ((ascent + descent) / 2) + ascent,
                  s
                );
            }

                /* Draw focus rectangle around object? */
                if((i == fb->focus_object) && GTK_WIDGET_HAS_FOCUS(w))
                {
                    GdkGCValues gcv;
                    gdk_gc_get_values(gc, &gcv);
                    gdk_gc_set_function(gc, GDK_INVERT);
                    gdk_draw_rectangle(
                        drawable, gc, FALSE,
                        x, y, o->width - 1, o->height - 1
                    );
                    gdk_gc_set_function(gc, gcv.function);
                }
          }
          break;
      }

      /* Draw focus rectangle if widget is in focus. */
      if(GTK_WIDGET_HAS_FOCUS(w) && (state != GTK_STATE_INSENSITIVE))
          gdk_draw_rectangle(
            drawable, style->fg_gc[state], FALSE,
            0, 0, width - 1, height - 1
          );

      /* Send drawable to window if drawable is not the window. */
      if(drawable != (GdkDrawable *)window)
          gdk_draw_pixmap(
            window, style->fg_gc[state], drawable,
            0, 0, 0, 0, width, height
          );
}


/*
 *    Returns the list column at index i.
 */
static FileBrowserColumn *FileBrowserListGetColumn(FileBrowser *fb, gint i)
{
      if(fb == NULL)
          return(NULL);
      if((i < 0) || (i >= fb->total_columns))
          return(NULL);
      else
          return(fb->column[i]);
}

/*
 *    Appends a new list column.
 */
static FileBrowserColumn *FileBrowserListColumnAppend(
        FileBrowser *fb, const gchar *label, gint width
)
{
      gint i;
      FileBrowserColumn *column, *column_prev;

        if(fb == NULL)
            return(NULL);

      i = fb->total_columns;
      fb->total_columns = i + 1;
      fb->column = (FileBrowserColumn **)g_realloc(
          fb->column, fb->total_columns * sizeof(FileBrowserColumn *)
      );
      if(fb->column == NULL)
      {
          fb->total_columns = 0;
            return(NULL);
      }

      fb->column[i] = column = FILE_BROWSER_COLUMN(g_malloc0(
          sizeof(FileBrowserColumn)
      ));
      if(column == NULL)
          return(NULL);

      column->label = STRDUP(label);
      column_prev = FileBrowserListGetColumn(fb, i - 1);
      column->position = ((column_prev != NULL) ?
          column_prev->position : 0) + width;
      column->label_justify = GTK_JUSTIFY_LEFT;
      column->flags = GTK_SENSITIVE | GTK_CAN_FOCUS |
                  GTK_CAN_DEFAULT;

      column->drag = FALSE;
      column->drag_position = column->position;
      column->drag_last_drawn_position = column->drag_position;

      return(column);
}

/*
 *    Deletes all list columns.
 */
static void FileBrowserListColumnsClear(FileBrowser *fb)
{
      gint i;

      if(fb == NULL)
          return;

        for(i = 0; i < fb->total_columns; i++)
            FileBrowserColumnDestroyCB(fb->column[i]);
        g_free(fb->column);
        fb->column = NULL;
        fb->total_columns = 0;
}


/*
 *    Updates the entry with the selected objects, if there are no
 *    selected objects then the entry is blanked.
 */
static void FileBrowserEntrySetSelectedObjects(FileBrowser *fb)
{
      gchar *s;
      GList *glist;
      GtkEntry *entry;

      if(fb == NULL)
          return;

      entry = (GtkEntry *)fb->entry;
      if(entry == NULL)
          return;

      /* Go through selected objects and generate a string s to be
       * set as the new entry value.
       */
      s = NULL;
      glist = fb->selection;
      while(glist != NULL)
      {
          gint i = (gint)glist->data;
          FileBrowserObject *o = FileBrowserGetObject(fb, i);
          if((o != NULL) ? (o->name != NULL) : FALSE)
          {
            s = strcatalloc(s, o->name);

            /* Add deliminator to string if there is another
             * object after this one.
             */
            if(glist->next != NULL)
                s = strcatalloc(s, ",");
          }
          glist = glist->next;
      }

      /* Update entry value if there were objects or else clear it */
      gtk_entry_set_text(entry, (s != NULL) ? s : "");
      gtk_entry_set_position(entry, -1);

      g_free(s);
}


/*
 *    Directory popup list item destroy callback.
 */
static void FileBrowserDirPUListItemDestroyCB(gpointer data)
{
      gchar *full_path = (gchar *)data;
      if(full_path == NULL)
          return;

      g_free(full_path);
}

/*
 *    File browser icon destroy callback.
 */
static void FileBrowserIconDestroyCB(gpointer data)
{
      FileBrowserIcon *icon = FILE_BROWSER_ICON(data);
      if(icon == NULL)
          return;

      if(icon->pixmap != NULL)
          gdk_pixmap_unref(icon->pixmap);
        if(icon->mask != NULL)
            gdk_bitmap_unref(icon->mask);
      g_free(icon->desc);
      g_free(icon);
}

/*
 *    File browser object destroy callback.
 */
static void FileBrowserObjectDestroyCB(gpointer data)
{
      FileBrowserObject *o = FILE_BROWSER_OBJECT(data);
      if(o == NULL)
          return;

      g_free(o->full_path);
      g_free(o->name);
      g_free(o);
}

/*
 *      File browser list column destroy callback.
 */
static void FileBrowserColumnDestroyCB(gpointer data)
{
      FileBrowserColumn *column = FILE_BROWSER_COLUMN(data);
      if(column == NULL)
          return;

      g_free(column->label);
      g_free(column);
}

/*
 *    File browser ok button signal callback.
 */
static void FileBrowserOKCB(GtkWidget *widget, gpointer data)
{
        FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

        if(FPromptIsQuery())
        {
            FPromptBreakQuery();
        }
      else if(PUListIsQuery(fb->dir_pulist))
      {
          PUListBreakQuery(fb->dir_pulist);
      }
        else if(CDialogIsQuery())
        {

        }
      else
      {
          gint i;
          GtkWidget *w;

          /* Mark that user response was OK */
            fb->user_response = TRUE;

          /* Delete previous list of selected paths. */
          for(i = 0; i < fb->total_selected_paths; i++)
            g_free(fb->selected_path[i]);
          g_free(fb->selected_path);
          fb->selected_path = NULL;
          fb->total_selected_paths = 0;

          /* Get value in entry and explode it to the list of
           * return paths.
           */
          w = fb->entry;
          if(w != NULL)
          {
            const gchar *s = gtk_entry_get_text(GTK_ENTRY(w));
            if((s != NULL) ? (*s != '\0') : FALSE)
            {
                /* Explode the entry's string value at all ','
                 * characters and then prefix the current location
                 * to each exploded string.
                 */
                const gchar *name;
                gint n, strc;
                gchar **strv = strchrexp(s, ',', &strc);
                const gchar *cur_location = FileBrowserGetLocation(fb);
                for(i = 0; i < strc; i++)
                {
                  name = strv[i];
                  if(name == NULL)
                      continue;

                  /* Append a new path to the selected_path
                   * array, the new path will be either name
                   * (if name is an absolute path) or the
                   * cur_location prefixed to name (if name is
                   * not an absolute path).
                   */
                  n = MAX(fb->total_selected_paths, 0);
                  fb->total_selected_paths = n + 1;
                  fb->selected_path = (gchar **)g_realloc(
                      fb->selected_path,
                      fb->total_selected_paths * sizeof(gchar *)
                  );
                  if(fb->selected_path == NULL)
                  {
                      fb->total_selected_paths = 0;
                  }
                  else
                  {
                      /* Check if name is an absolute path, if it
                       * is then copy it to the selected_path.
                       * Otherwise copy the cur_location prefixed
                       * to name.
                       */
                      gchar *full_path;
                      if(ISPATHABSOLUTE(name))
                        full_path = STRDUP(name);
                      else
                        full_path = STRDUP(
                            PrefixPaths(cur_location, name)
                        );
                      fb->selected_path[n] = full_path;
                  }

                  g_free(strv[i]);
                }
                g_free(strv);
              }
              else
              {
                /* File name entry is empty, so set the current
                 * location as the return path.
                 */
                const gchar *cur_location = FileBrowserGetLocation(fb);
                gint n = MAX(fb->total_selected_paths, 0);
                fb->total_selected_paths = n + 1;
                fb->selected_path = (gchar **)g_realloc(
                    fb->selected_path,
                    fb->total_selected_paths * sizeof(gchar *)
                );
                    if(fb->selected_path == NULL)
                    {
                        fb->total_selected_paths = 0;
                    }
                else
                {
                    fb->selected_path[n] = STRDUP(cur_location);
                }
              }
          }

          /* Unmap. */
          FileBrowserUnmap();

          /* Break out of blocking loop. */
          if(fb->block_loop_level > 0)
          {
            gtk_main_quit();
            fb->block_loop_level--;
          }
      }
}

/*
 *    File browser cancel button signal callback.
 */
static void FileBrowserCancelCB(GtkWidget *widget, gpointer data)
{
        FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

      if(FPromptIsQuery())
      {
          FPromptBreakQuery();
      }
        else if(PUListIsQuery(fb->dir_pulist))
        {
            PUListBreakQuery(fb->dir_pulist);
        }
      else if(CDialogIsQuery())
      {

      }
      else
      {
          /* Unmap. */
          FileBrowserUnmap();

          /* Break out of blocking loop. */
          if(fb->block_loop_level > 0)
          {
            gtk_main_quit();
            fb->block_loop_level--;
          }
      }
}           

/*
 *      File browser toplevel "delete_event" signal callback.
 */
static gint FileBrowserDeleteEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
)
{
      FileBrowserCancelCB(widget, data);
      return(TRUE);
}


/*
 *    Directory popup list drawing area event callback.
 */
static gint FileBrowserDirPUListDAEventCB(
        GtkWidget *widget, GdkEvent *event, gpointer data
)
{
      gint status = FALSE;
        gboolean key_press;
        GdkEventConfigure *configure;
        GdkEventExpose *expose;
        GdkEventFocus *focus;
        GdkEventKey *key;
        GdkEventButton *button;
        GtkWidget *w;
        FileBrowser *fb = FILE_BROWSER(data);
        if((event == NULL) || (fb == NULL))
            return(status);

        w = fb->dir_pulist_da;
        if(w == NULL)
            return(status);

      switch((gint)event->type)
      {
          case GDK_CONFIGURE:
            configure = (GdkEventConfigure *)event;
#if 0
            if(fb->dir_pulist_pm != NULL)
                gdk_pixmap_unref(fb->dir_pulist_pm);
            if((configure->width > 0) && (configure->height > 0))
                fb->dir_pulist_pm = gdk_pixmap_new(
                    w->window, configure->width, configure->height, -1
                );
            else
                fb->dir_pulist_pm = NULL;
#endif
            status = TRUE;
            break;

          case GDK_EXPOSE:
          expose = (GdkEventExpose *)event;
          FileBrowserDirPUListDraw(fb);
            status = TRUE;
            break;

          case GDK_FOCUS_CHANGE:
            focus = (GdkEventFocus *)event;
            if(focus->in)
                GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS);
            else
                GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS);
          FileBrowserDirPUListDraw(fb);
            status = TRUE;
            break;

          case GDK_KEY_PRESS:
          case GDK_KEY_RELEASE:
            key = (GdkEventKey *)event;
            key_press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE;
#define STOP_KEY_SIGNAL_EMIT                            \
{ if(widget != NULL)                                    \
 gtk_signal_emit_stop_by_name(                          \
  GTK_OBJECT(widget),                                   \
  key_press ? "key_press_event" : "key_release_event"   \
 ); }
          switch(key->keyval)
          {
            case GDK_Return:
            case GDK_KP_Enter:
            case GDK_space:
            case GDK_Up:
              case GDK_KP_Up:
              case GDK_Down:
              case GDK_KP_Down:
            if(key_press)
                FileBrowserDirPUListMapCB(fb->dir_pulist_btn, fb);
            STOP_KEY_SIGNAL_EMIT
            status = TRUE;
            break;
          }
#undef STOP_KEY_SIGNAL_EMIT
          break;

          case GDK_BUTTON_PRESS:
            button = (GdkEventButton *)event;
            if(!GTK_WIDGET_HAS_FOCUS(w))
                gtk_widget_grab_focus(w);

            switch(button->button)
            {
              case 1:
            FileBrowserDirPUListMapCB(fb->dir_pulist_btn, fb);
            break;
          }
          status = TRUE;
          break;

          case GDK_BUTTON_RELEASE:
            button = (GdkEventButton *)event;

            status = TRUE;
            break;

      }

      return(status);
}

/*
 *    Directory popup list button callback.
 */
static void FileBrowserDirPUListMapCB(GtkWidget *widget, gpointer data)
{
      const gchar *v;
      pulist_struct *pulist;
        FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

      pulist = fb->dir_pulist;
      if(pulist == NULL)
          return;

      /* Map popup list to query for new location. */
      v = PUListMapQuery(
          pulist,
          NULL,         /* Initial value */
          -1,                 /* Lines visible */
          PULIST_RELATIVE_BELOW,
          fb->dir_pulist_da,
          widget
      );

      /* Got new location? */
      if(v != NULL)
      {
          const gchar *full_path = (const gchar *)PUListGetDataFromValue(
            pulist, v
          );

          /* Go to new location. */
          if(full_path != NULL)
            FileBrowserSetLocation(fb, full_path);
      }
}


/*
 *    List header event callback.
 */
static gint FileBrowserListHeaderEventCB(
        GtkWidget *widget, GdkEvent *event, gpointer data
)
{
        gint status = FALSE;
        GdkEventConfigure *configure;
        GdkEventExpose *expose;
        GdkEventFocus *focus;
        GdkEventButton *button;
      GdkEventMotion *motion;
        GtkWidget *w;
        FileBrowser *fb = FILE_BROWSER(data);
        if((event == NULL) || (fb == NULL))
            return(status);

        w = fb->list_header_da;
        if(w == NULL)
            return(status);

      /* List widget must also be valid */
      if(fb->list_da == NULL)
          return(status);

#define DRAW_DRAG_LINE(x)                             \
{ if(column != NULL) {                                \
 gint line_p = (gint)(x);                             \
 GdkWindow *window1, *window2;                              \
 GdkGC *gc;                                     \
 GtkStyle *style;                               \
                                                \
 window1 = w->window;                                 \
 window2 = fb->list_da->window;                             \
 style = gtk_widget_get_style(w);                     \
 gc = (style != NULL) ?                               \
  style->fg_gc[GTK_WIDGET_STATE(w)] : NULL;                 \
                                                \
 if((window1 != NULL) && (window2 != NULL) && (gc != NULL)) {     \
  GdkGCValues gcv;                                    \
  gint width, height;                                 \
                                                \
  gdk_gc_get_values(gc, &gcv);                              \
  gdk_gc_set_function(gc, GDK_INVERT);                      \
                                                \
  /* Draw drag line on window1 */                     \
  gdk_window_get_size(window1, &width, &height);            \
  gdk_draw_line(window1, gc, line_p, 0, line_p, height);    \
                                                \
  /* Draw drag line on window2 */                               \
  gdk_window_get_size(window2, &width, &height);                \
  gdk_draw_line(window2, gc, line_p, 0, line_p, height);        \
                                                \
  gdk_gc_set_function(gc, gcv.function);              \
 }                                              \
} }


        switch((gint)event->type)
        {
          case GDK_CONFIGURE:
            configure = (GdkEventConfigure *)event;
/*
            if(fb->list_pm != NULL)
                gdk_pixmap_unref(fb->list_pm);
            if((configure->width > 0) && (configure->height > 0))
                fb->list_pm = gdk_pixmap_new(
                    w->window, configure->width, configure->height, -1
                );
            else
                fb->list_pm = NULL;
 */
            status = TRUE;
            break;

          case GDK_EXPOSE:
            expose = (GdkEventExpose *)event;
            FileBrowserListHeaderDraw(fb);
            status = TRUE;
            break;

          case GDK_FOCUS_CHANGE:
            focus = (GdkEventFocus *)event;
            if(focus->in && !GTK_WIDGET_HAS_FOCUS(w))
          {
                GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS);
            FileBrowserListHeaderDraw(fb);
          }
            else if(!focus->in && GTK_WIDGET_HAS_FOCUS(w))
          {
                GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS);
            FileBrowserListHeaderDraw(fb);
          }
            status = TRUE;
            break;

          case GDK_BUTTON_PRESS:
            button = (GdkEventButton *)event;
            if(!GTK_WIDGET_HAS_FOCUS(w))
                gtk_widget_grab_focus(w);
            if(fb->total_columns > 0)
            {
                gint i, cp, tolor = 3;
                gint p = (gint)button->x;
                FileBrowserColumn *column;

#define COLUMN_POSITION(p)    (((p) != NULL) ? (p)->position : 0)

            /* Iterate through all columns to update focus and
             * reset drag state.
             */
                for(i = 0; i < fb->total_columns; i++)
                {
                    column = fb->column[i];
                    if(column == NULL)
                        continue;

                /* Get left edge column position */
                    cp = ((i - 1) >= 0) ?
                        COLUMN_POSITION(fb->column[i - 1]) : 0;

                    if((p >= (cp + tolor)) && (p < (column->position - tolor)))
                  column->flags |= GTK_HAS_FOCUS;
                else
                  column->flags &= ~GTK_HAS_FOCUS;

                column->drag = FALSE;
            }
                FileBrowserListHeaderDraw(fb);

                /* Iterate through all columns, checking for one
             * where the pointer is over its resizing area.
                 */
                for(i = fb->total_columns - 1; i >= 0; i--)
                {
                    column = fb->column[i];
                    if(column == NULL)
                        continue;

                cp = column->position;
                if((p >= (cp - tolor)) && (p < (cp + (2 * tolor))))
                {
                  /* Update column drag values */
                  column->drag = TRUE;
                  column->drag_position = cp;

                  /* Draw drag line on list header and list. */
                  DRAW_DRAG_LINE(cp);
                  column->drag_last_drawn_position =
                      column->drag_position;

                  break;
                    }
            }
#undef COLUMN_POSITION
          }
            status = TRUE;
            break;

          case GDK_BUTTON_RELEASE:
            button = (GdkEventButton *)event;
            if(fb->total_columns > 0)
            {
                gint i, pos_shift_delta = 0;
                FileBrowserColumn *column;

            /* Iterate through all columns, checking for one that
             * is being dragged and set that new column's
             * position.
             */
                for(i = 0; i < fb->total_columns; i++)
                {
                    column = fb->column[i];
                    if(column == NULL)
                        continue;

                    /* This column being dragged? */
                    if(column->drag)
                    {
                  column->drag = FALSE;
                  pos_shift_delta = column->drag_position -
                      column->position;
                  column->position = column->drag_position;
                }
                else
                {
                  column->position += pos_shift_delta;
                }
            }

            /* Redraw the list header and the list */
            FileBrowserListHeaderDraw(fb);
            FileBrowserListDraw(fb);
          }
            status = TRUE;
            break;

          case GDK_MOTION_NOTIFY:
            motion = (GdkEventMotion *)event;
          if(fb->total_columns > 0)
          {
            gint i, tolor = 3, left_column_pos = 0;
            GdkCursor *cursor = NULL;
            gint p = (gint)motion->x;
            FileBrowserColumn *column;


            /* Iterate through all columns and check if one is
             * being dragged (resized) in which case it will be
             * handled accordingly.  Also if a pointer has
             * moved into the dragging area of a column then
             * the new cursor will be specified.
             */
            for(i = 0; i < fb->total_columns; i++)
            {
                column = fb->column[i];
                if(column == NULL)
                  continue;

                /* This column being dragged? */
                if(column->drag)
                {
                  column->drag_position = CLIP(
                      p, left_column_pos, w->allocation.width
                  );

                        /* Draw drag line on list header and list. */
                        DRAW_DRAG_LINE(column->drag_last_drawn_position);
                  DRAW_DRAG_LINE(column->drag_position);
                        column->drag_last_drawn_position =
                            column->drag_position;

                  /* Update cursor just in case. */
                  cursor = fb->cur_column_hresize;

                  /* No need to handle other columns after
                   * this one since it should be the only
                   * being dragged.
                   */
                  break;
                }
                else
                {
                  /* Column not being dragged, check if the
                   * pointer has moved into the dragging
                   * area of this column.
                   */
                  gint cp = column->position;
                  if((p >= (cp - tolor)) && (p < (cp + (2 * tolor))))
                  {
                      cursor = fb->cur_column_hresize;
                  }

                  left_column_pos = column->position;
                }
            }
            gdk_window_set_cursor(w->window, cursor);
                gdk_flush();
          }
            status = TRUE;
            break;

        case GDK_LEAVE_NOTIFY:
            gdk_window_set_cursor(w->window, NULL);
            gdk_flush();
          status = TRUE;
          break;
        }

#undef DRAW_DRAG_LINE

        return(status);
}

/*
 *    List event callback.
 */
static gint FileBrowserListEventCB(
      GtkWidget *widget, GdkEvent *event, gpointer data
)
{
        gint status = FALSE;
      gboolean key_press;
      GdkEventConfigure *configure;
      GdkEventExpose *expose;
      GdkEventFocus *focus;
      GdkEventKey *key;
      GdkEventButton *button;
      GtkWidget *w;
        FileBrowser *fb = FILE_BROWSER(data);
        if((event == NULL) || (fb == NULL))
            return(status);

      w = fb->list_da;
      if(w == NULL)
          return(status);

        switch((gint)event->type)
        {
        case GDK_CONFIGURE:
          configure = (GdkEventConfigure *)event;
          if(fb->list_pm != NULL)
            gdk_pixmap_unref(fb->list_pm);
          if((configure->width > 0) && (configure->height > 0))
            fb->list_pm = gdk_pixmap_new(
                w->window, configure->width, configure->height, -1
            );
          else
            fb->list_pm = NULL;
          status = TRUE;
            break;

          case GDK_EXPOSE:
          expose = (GdkEventExpose *)event;
            FileBrowserListDraw(fb);
            status = TRUE;
            break;

        case GDK_FOCUS_CHANGE:
          focus = (GdkEventFocus *)event;
            if(focus->in && !GTK_WIDGET_HAS_FOCUS(w))
            {
                GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS);
                FileBrowserListDraw(fb);
            }
            else if(!focus->in && GTK_WIDGET_HAS_FOCUS(w))
            {
                GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS);
                FileBrowserListDraw(fb);
            }
          status = TRUE;
          break;

        case GDK_KEY_PRESS:
        case GDK_KEY_RELEASE:
          key = (GdkEventKey *)event;
          key_press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE;
#define STOP_KEY_SIGNAL_EMIT                    \
{ if(widget != NULL)                            \
 gtk_signal_emit_stop_by_name(                        \
  GTK_OBJECT(widget),                           \
  key_press ? "key_press_event" : "key_release_event" \
 ); }
          /* First handle key event by list format. */
          switch(fb->list_format)
          {
            case FB_LIST_FORMAT_VERTICAL_DETAILS:
            case FB_LIST_FORMAT_VERTICAL:
            switch(key->keyval)
            {
              case GDK_Up:          /* Change Focus Up */
                  case GDK_KP_Up:
                  case GDK_Left:
                  case GDK_KP_Left:
                if(key_press)
                {
                  if(fb->focus_object > 0)
                  {
                      gint i = fb->focus_object - 1;
                      fb->focus_object = i;
                            if(FileBrowserListObjectVisibility(fb, i) !=
                                GTK_VISIBILITY_FULL
                            )
                                FileBrowserListMoveToObject(fb, i, 0.5f);
                      else
                        FileBrowserListDraw(fb);
                  }
                }
                STOP_KEY_SIGNAL_EMIT                
                status = TRUE;
                break;

                  case GDK_Down:    /* Change Focus Down */
                  case GDK_KP_Down:
                  case GDK_Right:
                  case GDK_KP_Right:
                    if(key_press)
                    {
                        if(fb->focus_object < fb->total_objects - 1)
                        {
                            gint i = fb->focus_object + 1;
                            fb->focus_object = i;
                            if(FileBrowserListObjectVisibility(fb, i) !=
                                GTK_VISIBILITY_FULL
                            )
                                FileBrowserListMoveToObject(fb, i, 0.5f);
                            else
                                FileBrowserListDraw(fb);
                        }
                    }
                    STOP_KEY_SIGNAL_EMIT
                    status = TRUE;
                    break;

                  case GDK_Page_Up: /* Page Decrement */
                  case GDK_KP_Page_Up:
                    if(key_press)
                    {
                        GtkRange *range = (GtkRange *)fb->list_vsb;
                        GtkAdjustment *adj = (range != NULL) ?
                            range->adjustment : NULL;
                        if(adj != NULL)
                        {
                            adj->value = MAX(
                                adj->value - adj->page_increment,
                                adj->lower
                            );
                            gtk_signal_emit_by_name(
                                GTK_OBJECT(adj), "value_changed"
                            );
                        }
                    }
                    status = TRUE;
                    break;

                  case GDK_Page_Down:     /* Page Increment */
                  case GDK_KP_Page_Down:
                    if(key_press)
                    {
                        GtkRange *range = (GtkRange *)fb->list_vsb;
                        GtkAdjustment *adj = (range != NULL) ?
                            range->adjustment : NULL;
                        if(adj != NULL)
                        {
                            adj->value = MIN(
                                adj->value + adj->page_increment,
                                MAX(adj->upper - adj->page_size, adj->lower)
                            );
                            gtk_signal_emit_by_name(
                                GTK_OBJECT(adj), "value_changed"
                            );
                        }
                    }
                    status = TRUE;
                    break;

                  case GDK_Home:    /* Scroll to beginning */
                  case GDK_KP_Home:
                    if(key_press)
                    {
                        GtkRange *range = (GtkRange *)fb->list_vsb;
                        GtkAdjustment *adj = (range != NULL) ?
                            range->adjustment : NULL;
                        if(adj != NULL)
                        {
                            adj->value = adj->lower;
                            gtk_signal_emit_by_name(
                                GTK_OBJECT(adj), "value_changed"
                            );
                        }
                    }
                    status = TRUE;
                    break;

                  case GDK_End:           /* Scroll to end */
                  case GDK_KP_End:
                    if(key_press)
                    {
                        GtkRange *range = (GtkRange *)fb->list_vsb;
                        GtkAdjustment *adj = (range != NULL) ?
                            range->adjustment : NULL;
                        if(adj != NULL)
                        {
                            adj->value = MAX(
                                adj->upper - adj->page_size,
                                adj->lower
                            );
                            gtk_signal_emit_by_name(
                                GTK_OBJECT(adj), "value_changed"
                            );
                        }
                    }
                    status = TRUE;
                    break;
            }
            break;

            default:    /* FB_LIST_FORMAT_STANDARD */
                switch(key->keyval)
                {
                  case GDK_Up:            /* Change Focus Up */
                  case GDK_KP_Up:
                    if(key_press)
                    {
                        if(fb->focus_object > 0)
                        {
                            gint i = fb->focus_object - 1;
                            fb->focus_object = i;
                            if(FileBrowserListObjectVisibility(fb, i) !=
                                GTK_VISIBILITY_FULL
                            )
                                FileBrowserListMoveToObject(fb, i, 0.5f);
                            else
                                FileBrowserListDraw(fb);
                        }
                    }
                    STOP_KEY_SIGNAL_EMIT
                    status = TRUE;
                    break;

                  case GDK_Down:    /* Change Focus Down */
                  case GDK_KP_Down:
                    if(key_press)
                    {
                        if(fb->focus_object < fb->total_objects - 1)
                        {
                            gint i = fb->focus_object + 1;
                            fb->focus_object = i;
                            if(FileBrowserListObjectVisibility(fb, i) !=
                                GTK_VISIBILITY_FULL
                            )
                                FileBrowserListMoveToObject(fb, i, 0.5f);
                            else
                                FileBrowserListDraw(fb);
                        }
                    }
                    STOP_KEY_SIGNAL_EMIT
                    status = TRUE;
                    break;

                  case GDK_Left:    /* Change Focus Left */
                  case GDK_KP_Left:
                    if(key_press)
                    {
                  gint i = MAX(
                      fb->focus_object - fb->objects_per_row,
                      0
                  );
                  fb->focus_object = i;
                        if(FileBrowserListObjectVisibility(fb, i) !=
                            GTK_VISIBILITY_FULL
                        )
                            FileBrowserListMoveToObject(fb, i, 0.5f);
                        else
                            FileBrowserListDraw(fb);
                    }
                    STOP_KEY_SIGNAL_EMIT
                    status = TRUE;
                    break;

                  case GDK_Right:   /* Change Focus Right */
                  case GDK_KP_Right:
                    if(key_press)
                    {
                        gint i = MIN(
                            fb->focus_object + fb->objects_per_row,
                            fb->total_objects - 1
                        );
                        fb->focus_object = i;
                        if(FileBrowserListObjectVisibility(fb, i) !=
                            GTK_VISIBILITY_FULL
                        )
                            FileBrowserListMoveToObject(fb, i, 0.5f);
                        else
                            FileBrowserListDraw(fb);
                    }
                    STOP_KEY_SIGNAL_EMIT
                    status = TRUE;
                    break;

              case GDK_Page_Up:     /* Page Decrement */
                  case GDK_KP_Page_Up:
                if(key_press)
                {
                  GtkRange *range = (GtkRange *)fb->list_hsb;
                  GtkAdjustment *adj = (range != NULL) ?
                      range->adjustment : NULL;
                  if(adj != NULL)
                  {
                      adj->value = MAX(
                        adj->value - adj->page_increment,
                        adj->lower
                      );
                      gtk_signal_emit_by_name(
                        GTK_OBJECT(adj), "value_changed"
                      );
                  }
                }
                    status = TRUE;
                    break;

                  case GDK_Page_Down:     /* Page Increment */
                  case GDK_KP_Page_Down:
                    if(key_press)
                    {
                        GtkRange *range = (GtkRange *)fb->list_hsb;
                        GtkAdjustment *adj = (range != NULL) ?
                            range->adjustment : NULL;
                        if(adj != NULL)
                        {
                            adj->value = MIN(
                                adj->value + adj->page_increment,
                                MAX(adj->upper - adj->page_size, adj->lower)
                            );
                            gtk_signal_emit_by_name(
                                GTK_OBJECT(adj), "value_changed"
                            );
                        }
                    }
                    status = TRUE;
                    break;

                  case GDK_Home:    /* Scroll to beginning */
                  case GDK_KP_Home:
                    if(key_press)
                    {
                        GtkRange *range = (GtkRange *)fb->list_hsb;
                        GtkAdjustment *adj = (range != NULL) ?
                            range->adjustment : NULL;
                        if(adj != NULL)
                        {
                            adj->value = adj->lower;
                            gtk_signal_emit_by_name(
                                GTK_OBJECT(adj), "value_changed"
                            );
                        }
                    }
                    status = TRUE;
                    break;

                  case GDK_End:           /* Scroll to end */
                  case GDK_KP_End:
                    if(key_press)
                    {
                        GtkRange *range = (GtkRange *)fb->list_hsb;
                        GtkAdjustment *adj = (range != NULL) ?
                            range->adjustment : NULL;
                        if(adj != NULL)
                        {
                            adj->value = MAX(
                        adj->upper - adj->page_size,
                        adj->lower
                      );
                            gtk_signal_emit_by_name(
                                GTK_OBJECT(adj), "value_changed"
                            );
                        }
                    }
                    status = TRUE;
                    break;
                }
                break;
          }
          /* If key event was not handled above then we handle
           * it generally independent of list format display
           * here.
           */
          if(!status)
          {
            switch(key->keyval)
            {
              case GDK_Return:      /* Enter */
              case GDK_KP_Enter:
              case GDK_3270_Enter:
              case GDK_ISO_Enter:
                if(key_press)
                  FileBrowserEntryEnterCB(fb->entry, fb);
                    status = TRUE;
                    break;

              case GDK_space: /* Toggle Select */
                if(key_press)
                {
                  gint i = fb->focus_object;
                        if(g_list_find(fb->selection, (gpointer)i) != NULL)
                            fb->selection = g_list_remove(
                                fb->selection, (gpointer)i
                            );
                        else
                            fb->selection = g_list_append(
                                fb->selection, (gpointer)i
                            );
                        fb->selection_end = g_list_last(fb->selection);
                  FileBrowserListDraw(fb);
                  FileBrowserEntrySetSelectedObjects(fb);
                }
                    status = TRUE;
                break;

                  case GDK_BackSpace:
                    if(key_press)
                  FileBrowserGoToParentCB(NULL, fb);
                    status = TRUE;
                    break;

                  case GDK_F5:      /* Refresh */
                    if(key_press)
                        FileBrowserRefreshCB(NULL, fb);
                    status = TRUE;
                    break;

                  case GDK_Insert:  /* New Directory */
/* Some other accelerator seems to catch this even with this widget in
   focus so we don't need to respond to it.
                    if(key_press)
                        FileBrowserNewDirectoryCB(NULL, fb);
 */
                    status = TRUE;
                    break;

                  case GDK_F2:            /* Rename */
                    if(key_press)
                        FileBrowserRenameCB(NULL, fb);
                    status = TRUE;
                    break;

              case GDK_Delete:      /* Delete */
                if(key_press)
                        FileBrowserDeleteCB(NULL, fb);
                    status = TRUE;
                    break;
#if 0
/* Some other accelerator seems to catch this even with this widget in
 * focus so we don't need to respond to it.
 */
              case 'a':       /* Select All */
                if(key_press && (key->state & GDK_CONTROL_MASK))
                  FileBrowserSelectAllCB(NULL, fb);
                    status = TRUE;
                    break;

                  case 'u':         /* Unelect All */
                    if(key_press && (key->state & GDK_CONTROL_MASK))
                        FileBrowserUnselectAllCB(NULL, fb);
                    status = TRUE;
                    break;

                  case 'i':         /* Invert Selection */
                    if(key_press && (key->state & GDK_CONTROL_MASK))
                        FileBrowserInvertSelectionCB(NULL, fb);
                    status = TRUE;
                    break;
#endif
            }
          }
#undef STOP_KEY_SIGNAL_EMIT
          break;

        case GDK_BUTTON_PRESS:
          button = (GdkEventButton *)event;
            if(FPromptIsQuery())
                FPromptBreakQuery();
          if(!GTK_WIDGET_HAS_FOCUS(w))
            gtk_widget_grab_focus(w);

          switch(button->button)
          {
            case 1:
            if(button->state & GDK_CONTROL_MASK)
            {
                    gint i = FileBrowserListSelectCoordinates(
                        fb, (gint)button->x, (gint)button->y
                    );
                if(i > -1)
                    {
                  /* If new object is already selected then unselect
                   * it, otherwise add it to the selection.
                   */
                        fb->focus_object = i;
                  if(g_list_find(fb->selection, (gpointer)i) != NULL)
                  {
                      fb->selection = g_list_remove(
                        fb->selection, (gpointer)i
                            );
                      fb->selection_end = g_list_last(fb->selection);
                  }
                  else
                  {
                      fb->selection = g_list_append(
                        fb->selection, (gpointer)i
                      );
                      fb->selection_end = g_list_last(fb->selection);

                            /* Update DND icon based on the newly selected
                       * object.
                       */
                      FileBrowserListObjectSetDNDIcon(fb, i);
                  }

                        /* If matched object is not fully visibile then
                         * scroll so that it is in the center of the
                         * list.
                         */
                  if(FileBrowserListObjectVisibility(fb, i) !=
                      GTK_VISIBILITY_FULL
                  )
                      FileBrowserListMoveToObject(fb, i, 0.5f);
                }
            }
                else if(button->state & GDK_SHIFT_MASK)
                {
                    gint i = FileBrowserListSelectCoordinates(
                        fb, (gint)button->x, (gint)button->y
                    );
                    if(i > -1)
                    {
                  /* Select all objects between the last selected
                   * object and the current one.
                   */
                  fb->focus_object = i;
                  if(fb->selection_end != NULL)
                  {
                      gint j = i, n = (gint)fb->selection_end->data;
                      if(j > n)
                      {
                        while(j > n)
                        {
                            if(g_list_find(fb->selection, (gpointer)j) == NULL)
                              fb->selection = g_list_append(
                                  fb->selection, (gpointer)j
                              );
                            j--;
                        }
                                fb->selection_end = g_list_last(fb->selection);
                      }
                      else if(j < n)
                      {
                                while(j < n)
                                {
                                    if(g_list_find(fb->selection, (gpointer)j) == NULL)
                                        fb->selection = g_list_append(
                                            fb->selection, (gpointer)j
                                        );
                                    j++;
                                }
                                fb->selection_end = g_list_last(fb->selection);
                      }
                  }
                  else
                  {
                      /* No previously selected object, so just select
                       * this one.
                       */
                            fb->selection = g_list_append(
                                fb->selection, (gpointer)i
                            );
                            fb->selection_end = g_list_last(fb->selection);
                  }

                  /* Update DND icon based on the newly selected
                   * object.
                   */
                        FileBrowserListObjectSetDNDIcon(fb, i);

                        /* If matched object is not fully visibile then
                         * scroll so that it is in the center of the
                         * list.
                         */
                        if(FileBrowserListObjectVisibility(fb, i) !=
                            GTK_VISIBILITY_FULL
                        )
                            FileBrowserListMoveToObject(fb, i, 0.5f);
                }
                }
            else
            {
                gint i = FileBrowserListSelectCoordinates(
                        fb, (gint)button->x, (gint)button->y
                    );
                if(i > -1)
                {
                  /* If this object is already selected then do rename. */
                  if(g_list_find(fb->selection, (gpointer)i) != NULL)
                  {
#if 0
/* This interferes with double click. */
                            fb->focus_object = i;

                      /* Make sure this is the last selected object. */
                      fb->selection = g_list_remove(
                        fb->selection, (gpointer)i
                      );
                      fb->selection = g_list_append(
                        fb->selection, (gpointer)i
                      );
                      fb->selection_end = g_list_last(fb->selection);

                      /* Do rename. */
                      FileBrowserRenameCB(NULL, fb);
#endif
                  }

                  /* Unselect all */
                  fb->focus_object = -1;
                  if(fb->selection != NULL)
                      g_list_free(fb->selection);
                  fb->selection = fb->selection_end = NULL;

                  /* Select this object. */
                  fb->focus_object = i;
                  fb->selection = g_list_append(
                      fb->selection, (gpointer)i
                  );
                  fb->selection_end = g_list_last(fb->selection);

                        /* Update DND icon based on the newly selected
                         * object.
                         */
                        FileBrowserListObjectSetDNDIcon(fb, i);

                  /* If matched object is not fully visibile then
                   * scroll so that it is in the center of the
                   * list.
                   */
                        if(FileBrowserListObjectVisibility(fb, i) !=
                            GTK_VISIBILITY_FULL
                        )
                            FileBrowserListMoveToObject(fb, i, 0.5f);
                }
                else
                {
                        /* Unselect all */
                        fb->focus_object = -1;
                        if(fb->selection != NULL)
                            g_list_free(fb->selection);
                        fb->selection = fb->selection_end = NULL;
                }
            }
            /* Update entry with the new list of selected
             * objects.
             */
            FileBrowserEntrySetSelectedObjects(fb);
            break;

            case 2:
            if(TRUE)
            {
                    gint i = FileBrowserListSelectCoordinates(
                        fb, (gint)button->x, (gint)button->y
                    );
                    if(i > -1)
                    {
                        fb->focus_object = i;

                        /* Make sure this is the last selected object. */
                        fb->selection = g_list_remove(
                            fb->selection, (gpointer)i
                        );
                        fb->selection = g_list_append(
                            fb->selection, (gpointer)i
                        );
                        fb->selection_end = g_list_last(fb->selection);

                        /* Update DND icon based on the newly selected
                         * object.
                         */
                        FileBrowserListObjectSetDNDIcon(fb, i);

                        /* Do rename. */
                        FileBrowserRenameCB(NULL, fb);
                }
            }
            break;

            case 3:     /* Map right-click menu */
            if(fb->list_menu != NULL)
            {
                GtkMenu *menu = GTK_MENU(fb->list_menu);
                    gint i = FileBrowserListSelectCoordinates(
                        fb, (gint)button->x, (gint)button->y
                    );
                /* If ctrl or shift modifier keys are held then do
                 * not modify selection before mapping of menu.
                 */
                if((button->state & GDK_CONTROL_MASK) ||
                   (button->state & GDK_SHIFT_MASK)
                )
                {
                  /* Do not modify selection, just update focus
                   * object.
                   */
                        fb->focus_object = i;
                }
                else if(i > -1)
                {
                  /* Unselect all objects. */
                  fb->focus_object = -1;
                        if(fb->selection != NULL)
                            g_list_free(fb->selection);
                  fb->selection = fb->selection_end = NULL;

                  /* Select this object. */
                        fb->focus_object = i;
                        fb->selection = g_list_append(
                            fb->selection, (gpointer)i
                        );
                        fb->selection_end = g_list_last(fb->selection);

                        /* Update DND icon based on the newly selected
                         * object.
                         */
                        FileBrowserListObjectSetDNDIcon(fb, i);
                }

                /* Map menu */
                    gtk_menu_popup(
                        menu, NULL, NULL,
                        NULL, NULL,
                        button->button, button->time
                    );
            }
            break;
          }
            FileBrowserListDraw(fb);
          status = TRUE;
            break;

          case GDK_BUTTON_RELEASE:
            button = (GdkEventButton *)event;

            status = TRUE;
            break;

        case GDK_2BUTTON_PRESS:
          button = (GdkEventButton *)event;
            switch(button->button)
            {
              case 1:
            FileBrowserEntryEnterCB(fb->entry, fb);
            break;
          }
          status = TRUE;
          break;
      }

        return(status);
}

/*
 *    List scrollbar GtkAdjustment "value_changed" signal callback.
 */
static void FileBrowserListScrollCB(
      GtkAdjustment *adj, gpointer data
)
{
        FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

      FileBrowserListDraw(fb);
}

/*
 *    Select all callback.
 */
static void FileBrowserSelectAllCB(GtkWidget *widget, gpointer data)
{
      gint i;
      FileBrowser *fb = FILE_BROWSER(data);
      if(fb == NULL)
          return;

        if(fb->selection != NULL)
            g_list_free(fb->selection);
        fb->selection = NULL;
        for(i = 0; i < fb->total_objects; i++)
            fb->selection = g_list_append(
                fb->selection, (gpointer)i
            );
        fb->selection_end = g_list_last(fb->selection);
        FileBrowserListDraw(fb);
        FileBrowserEntrySetSelectedObjects(fb);
}

/*
 *    Unselect all callback.
 */
static void FileBrowserUnselectAllCB(GtkWidget *widget, gpointer data)
{
      FileBrowser *fb = FILE_BROWSER(data);
      if(fb == NULL)
          return;

        if(fb->selection != NULL)
            g_list_free(fb->selection);
        fb->selection = fb->selection_end = NULL;
        FileBrowserListDraw(fb);
        FileBrowserEntrySetSelectedObjects(fb);
}

/*
 *    Invert selection callback.
 */
static void FileBrowserInvertSelectionCB(GtkWidget *widget, gpointer data)
{
      gint i;
      GList *glist = NULL;
      FileBrowser *fb = FILE_BROWSER(data);
      if(fb == NULL)
          return;

        for(i = 0; i < fb->total_objects; i++)
        {
            if(g_list_find(fb->selection, (gpointer)i) == NULL)
                glist = g_list_append(glist, (gpointer)i);
        }
        if(fb->selection != NULL)
            g_list_free(fb->selection);
        fb->selection = glist;
        fb->selection_end = g_list_last(fb->selection);
        FileBrowserListDraw(fb);
        FileBrowserEntrySetSelectedObjects(fb);
}

/*
 *    Used by FileBrowserRenameCB() as the apply callback for
 *    the floating prompt.
 */
static void FileBrowserRenameFPromptCB(
      gpointer data, const gchar *value
)
{
        gint i;
        GList *glist;
        FileBrowserObject *o;
        FileBrowser *fb = FILE_BROWSER(data);
        if((fb == NULL) || (value == NULL))
            return;

      if(*value == '\0')
          return;

#ifdef PROG_LANGUAGE_ENGLISH
# define TITLE_RENAME_FAILED     "Rename Failed"
#endif
#ifdef PROG_LANGUAGE_SPANISH
# define TITLE_RENAME_FAILED     "Reagrupe Fallado"
#endif
#ifdef PROG_LANGUAGE_FRENCH
# define TITLE_RENAME_FAILED     "Renommer Echoué"
#endif
#ifdef PROG_LANGUAGE_GERMAN
# define TITLE_RENAME_FAILED     "Benennen Sie Versagt Um"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
# define TITLE_RENAME_FAILED     "Rinominare Fallito"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
# define TITLE_RENAME_FAILED     "Ombenevn Failed"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
# define TITLE_RENAME_FAILED     "O Rename Fracassou"
#endif
#define MESSAGE_RENAME_FAILED(s)    {     \
 CDialogSetTransientFor(fb->toplevel);          \
 CDialogGetResponse(                      \
  TITLE_RENAME_FAILED,                    \
  (s), NULL,                              \
  CDIALOG_ICON_WARNING,                   \
  CDIALOG_BTNFLAG_OK,                     \
  CDIALOG_BTNFLAG_OK                      \
 );                                 \
 CDialogSetTransientFor(NULL);                  \
}

      /* No directory deliminators may exist in the new value. */
      if(strchr(value, DIR_DELIMINATOR) != NULL)
      {
          gchar *buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"The new name \"%s\" contains '%c' directory\n\
deliminators which are not allowed in an object's name"
#endif
#ifdef PROG_LANGUAGE_SPANISH
"El nombre nuevo \"%s\" contiene deliminators de\n\
guía de '%c' que no se permiten en un nombre de objeto."
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Le nouveau \"%s\" de nom contient deliminators d'annuaire\n\
de '%c' qui ne sont pas permis dans un nom de l'objet."
#endif
#ifdef PROG_LANGUAGE_GERMAN
"Der neu \"%s\" enthält '%c' Verzeichnis deliminators, das\n\
im Namen eines Objekts nicht erlaubt wird."
#endif
#ifdef PROG_LANGUAGE_ITALIAN
"Lo \"%s\" di nome nuovo contiene il deliminators\n\
di elenco di '%c' che non sono lasciati in un nome dell'oggetto."
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
"Den nye navne \"%s\" inneholder '%c' katalog deliminators\n\
som ikke tillater i et objekts navn."
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
"O novo \"%s\" de nome contem deliminators de guia\n\
de '%c' que nao são permitidos num nome do objeto."
#endif
            ,
            value, DIR_DELIMINATOR
          );
          MESSAGE_RENAME_FAILED(buf);
          g_free(buf);
          return;
      }

        /* Get last selected object. */
        glist = fb->selection_end;
        i = (glist != NULL) ? (gint)glist->data : -1;
      o = FileBrowserGetObject(fb, i);
        if((o != NULL) ? (o->full_path != NULL) : FALSE)
      {
          const gchar *cur_location = FileBrowserGetLocation(fb);
          gchar   *old_full_path = STRDUP(o->full_path),
                  *new_full_path = STRDUP(
                      PrefixPaths(cur_location, value)
                  );
          struct stat lstat_buf;

          /* New name and old name the same? */
          if(!strcmp(o->name, value))
          {

          }
          /* Make sure new name does not exist. */
#ifdef __MSW__
          else if(stat(new_full_path, &lstat_buf) == 0)
#else
          else if(lstat(new_full_path, &lstat_buf) == 0)
#endif
          {
            gchar *buf = g_strdup_printf(
"An object by the name of \"%s\" already exists.",
                value
            );
            MESSAGE_RENAME_FAILED(buf);
            g_free(buf);
          }
          /* Rename. */
          else if(rename(old_full_path, new_full_path))
          {
            gchar *buf;
#ifdef PROG_LANGUAGE_ENGLISH
            switch(errno)
            {
              case EISDIR:
                buf = g_strdup_printf(
"\"%s\" is an existing directory, but \"%s\" is not a directory.",
                  value, o->name
                );
                break;

                  case ENOTEMPTY:
              case EEXIST:
                buf = g_strdup_printf(
"\"%s\" is a non-empty directory.",
                  value
                );
                    break;

              case EBUSY:
                    buf = g_strdup_printf(
"\"%s\" or \"%s\" is a directory that is currently in use.",
                  o->name, value
                );
                break;

              case EINVAL:
                    buf = g_strdup_printf(
"\"%s\" contains a path prefix of \"%s\" or an attempt\n\
was made to make a directory a subdirectory of itself.",
                  value, o->name
                );
                break;

                  case EMLINK:
                    buf = g_strdup_printf(
"\"%s\" already has the maximum number of links\n\
to it, or it was a directory and the directory containing\n\
\"%s\" has reached the maximum number of links.",
                        o->name, value
                    );
                    break;

              case ENOTDIR:
                    buf = g_strdup_printf(
"A compoent in \"%s\"\n\
is not a directory or \"%s\" is a directory and\n\
\"%s\" exists but is not a directory.",
                  old_full_path,
                  o->name, value
                );
                    break;

              case EACCES:
                    buf = g_strdup_printf(
"You do not have sufficient permission to rename\n\
\"%s\" to \"%s\".",
                  o->name, value
                );
                break;

                  case ENAMETOOLONG:
                    buf = g_strdup_printf(
"The name \"%s\" is too long.",
                  value
                );
                    break;

                  case ENOENT:
                    buf = g_strdup_printf(
"One or more directory compoents of \"%s\" or \"%s\"\n\
is a dangling symbolic link.",
                        value, o->name
                    );
                    break;

              case ENOMEM:
                buf = STRDUP("The system is out of memory.");
                break;

              case EROFS:
                    buf = g_strdup_printf(
"The object \"%s\" is on a read-only filesystem\n\
and cannot be renamed.",
                  o->name
                );
                break;
#ifdef ELOOP
              case ELOOP:
                    buf = g_strdup_printf(
"Too many symbolic links were encountered when trying to\n\
resolve \"%s\" or \"%s\".",
                        value, o->name
                    );
                    break;
#endif
                  case ENOSPC:
                    buf = g_strdup_printf(
"The device that \"%s\" is on is out of free space.",
                        o->name
                    );
                    break;

              default:
                buf = g_strdup_printf(
"Unable to rename \"%s\" to \"%s\".",
                  o->name, value
                );
                break;
            }
#else
            buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_SPANISH
                "Incapaz de reagrupar \"%s\" a \"%s\"."
#endif
#ifdef PROG_LANGUAGE_FRENCH
                    "Incapable pour renommer \"%s\" à \"%s\"."
#endif
#ifdef PROG_LANGUAGE_GERMAN
                    "Unfähig, \"%s\" zu \"%s\" umzubenennen."
#endif
#ifdef PROG_LANGUAGE_ITALIAN
                    "Incapace per rinominare \"%s\" a \"%s\"."
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
                    "Maktesløs ombenevne \"%s\" til \"%s\"."
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
                    "Incapaz a \"%s\" de rename a \"%s\"."
#endif
                ,
                o->name, value
            );
#endif
            MESSAGE_RENAME_FAILED(buf);
                g_free(buf);
          }
          else
          {
            /* Rename successful, update values on object. */

            /* Name */
            g_free(o->name);
            o->name = STRDUP(value);

            /* Full path */
            g_free(o->full_path);
            o->full_path = STRDUP(new_full_path);

            /* Statistics */
#ifdef __MSW__
            if(stat(o->full_path, &o->lstat_buf))
#else
            if(lstat(o->full_path, &o->lstat_buf))
#endif
                memset(&o->lstat_buf, 0x00, sizeof(struct stat));

            /* Update values. */
            FileBrowserObjectUpdateValues(fb, o);
          }

          g_free(old_full_path);
          g_free(new_full_path);
      }

      /* Need to redraw and update the value in the entry
       * since one of the selected object's names may have
       * changed.
       */
      FileBrowserListDraw(fb);
      FileBrowserEntrySetSelectedObjects(fb);

#undef MESSAGE_RENAME_FAILED
#undef TITLE_RENAME_FAILED
}

/*
 *    List object rename callback.
 */
static void FileBrowserRenameCB(GtkWidget *widget, gpointer data)
{
      gint i, x, y, width, height;
      const GList *glist;
      const FileBrowserObject *o;
        FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

      if(FPromptIsQuery())
          return;

      /* Get last selected object. */
      glist = fb->selection_end;
      i = (glist != NULL) ? (gint)glist->data : -1;
      o = FileBrowserGetObject(fb, i);
      if(o == NULL)
          return;

        /* Get fprompt geometry based on list display format. */
      x = o->x;
      y = o->y;
      width = MAX(o->width + 4, 100);
      height = -1;
        switch(fb->list_format)
        {
          case FB_LIST_FORMAT_VERTICAL_DETAILS:
          case FB_LIST_FORMAT_VERTICAL:
            if(fb->list_vsb != NULL)
            {
                GtkRange *range = GTK_RANGE(fb->list_vsb);
                GtkAdjustment *adj = range->adjustment;
                y -= (gint)((adj != NULL) ? adj->value : 0.0f);
            }
          break;
        default:  /* FB_LIST_FORMAT_STANDARD */
            if(fb->list_hsb != NULL)
            {
                GtkRange *range = GTK_RANGE(fb->list_hsb);
                GtkAdjustment *adj = range->adjustment;
                x -= (gint)((adj != NULL) ? adj->value : 0.0f);
            }
            break;
      }

        /* Get root window relative coordinates of the object for
       * placement of the floating prompt.
       */
      if(fb->list_da != NULL)
      {
          GtkWidget *w = fb->list_da;
          GdkWindow *window = w->window;
          if(window != NULL)
          {
            gint rx, ry;
            gdk_window_get_deskrelative_origin(
                window, &rx, &ry
            );
            x += rx;
            y += ry;
          }
      }
      /* At this point x and y should now be the root window
       * relative position for the floating prompt.
       */
        FPromptSetTransientFor(fb->toplevel);
      FPromptSetPosition(x - 2, y - 2);
      FPromptMapQuery(
          NULL,               /* No label */
          o->name,
          NULL,               /* No tooltip */
          FPROMPT_MAP_NO_MOVE,      /* Map code */
          width, height,            /* Width and height */
          0,                        /* No buttons */
          fb,                       /* Client data */
          NULL,               /* No browse callback */
          FileBrowserRenameFPromptCB,
          NULL                /* No cancel callback */
      );
      FPromptSetTransientFor(NULL);
}

/*
 *    CHMod fprompt callback.
 */
static void FileBrowserCHModFPromptCB(
        gpointer data, const gchar *value
)
{
        gint i;
        GList *glist;
        FileBrowserObject *o;
        FileBrowser *fb = FILE_BROWSER(data);
        if((fb == NULL) || (value == NULL))
            return;

        if(*value == '\0')
            return;

#ifdef PROG_LANGUAGE_ENGLISH
# define TITLE_CHMOD_FAILED     "Change Mode Failed"
#endif
#ifdef PROG_LANGUAGE_SPANISH
# define TITLE_CHMOD_FAILED     "Cambie El Modo Fallado"
#endif
#ifdef PROG_LANGUAGE_FRENCH
# define TITLE_CHMOD_FAILED     "Changer Le Mode A échoué"
#endif
#ifdef PROG_LANGUAGE_GERMAN
# define TITLE_CHMOD_FAILED     "Ändern Sie Versagten Modus"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
# define TITLE_CHMOD_FAILED     "Cambiare Il Modo Fallito"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
# define TITLE_CHMOD_FAILED     "Forandr Sviktet Modus"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
# define TITLE_CHMOD_FAILED     "Mude Modo Fracassado"
#endif
#define MESSAGE_CHMOD_FAILED(s)           {     \
 CDialogSetTransientFor(fb->toplevel);          \
 CDialogGetResponse(                            \
  TITLE_CHMOD_FAILED,                           \
  (s), NULL,                                    \
  CDIALOG_ICON_WARNING,                         \
  CDIALOG_BTNFLAG_OK,                           \
  CDIALOG_BTNFLAG_OK                            \
 );                                             \
 CDialogSetTransientFor(NULL);                  \
}

        /* Get last selected object. */
        glist = fb->selection_end;
        i = (glist != NULL) ? (gint)glist->data : -1;
        o = FileBrowserGetObject(fb, i);
      if((o != NULL) ? (o->full_path != NULL) : FALSE)
      {
#if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
          mode_t m = 0;
          const gchar *s = value;

          /* Begin parsing value to obtain new mode value */
          while(ISBLANK(*s))
            s++;

          /* User */
          /* Read */
          if(*s != '\0')
          {
            if(tolower(*s) == 'r')
                m |= S_IRUSR;
            s++;
          }
            /* Write */
            if(*s != '\0')
            {
                if(tolower(*s) == 'w')
                    m |= S_IWUSR;
                s++;
            }
            /* Execute */
            if(*s != '\0')
            {
                if(tolower(*s) == 'x')
                    m |= S_IXUSR;
            else if(tolower(*s) == 's')
                m |= S_ISUID;
                s++;
            }
            /* Group */
            /* Read */
            if(*s != '\0')
            {
                if(tolower(*s) == 'r')
                    m |= S_IRGRP;
                s++;
            }
            /* Write */
            if(*s != '\0')
            {
                if(tolower(*s) == 'w')
                    m |= S_IWGRP;
                s++;
            }
            /* Execute */
            if(*s != '\0')
            {
                if(tolower(*s) == 'x')
                    m |= S_IXGRP;
                else if(tolower(*s) == 'g')
                    m |= S_ISGID;
                s++;
            }
            /* Other */
            /* Read */
            if(*s != '\0')
            {
                if(tolower(*s) == 'r')
                    m |= S_IROTH;
                s++;
            }
            /* Write */
            if(*s != '\0')
            {
                if(tolower(*s) == 'w')
                    m |= S_IWOTH;
                s++;
            }
            /* Execute */
            if(*s != '\0')
            {
                if(tolower(*s) == 'x')
                    m |= S_IXOTH;
                else if(tolower(*s) == 't')
                    m |= S_ISVTX;
                s++;
            }

          /* CHMod */
          if(chmod(o->full_path, m))
          {
                gchar *buf;
#ifdef PROG_LANGUAGE_ENGLISH
                switch(errno)
                {
                  case EPERM:
                    buf = g_strdup_printf(
"You do not have sufficient permission to change the permissions\n\
of \"%s\".",
                        o->name
                    );
                    break;

              case EROFS:
                    buf = g_strdup_printf(
"The object \"%s\" is on a read-only filesystem\n\
and its permissions cannot be changed.",
                        o->name
                    );
                    break;

                  case ENAMETOOLONG:
                    buf = g_strdup_printf(
"The name \"%s\" is too long.",
                        o->name
                    );
                    break;

              case ENOMEM:
                buf = STRDUP(
"The system is out of memory."
                );
                break;

                  case ENOTDIR:
                    buf = STRDUP(
"A compoent of the path prefix is not a directory."
                    );
                    break;

                  case EACCES:
                    buf = STRDUP(
"Search permission is denied on a compoent of the path prefix."
                    );
                    break;

#ifdef ELOOP
                  case ELOOP:
                    buf = g_strdup_printf(
"Too many symbolic links were encountered when trying to\n\
resolve \"%s\".",
                        o->name
                    );
                    break;
#endif

                  default:
                    buf = g_strdup_printf(
"Unable to change the mode of \"%s\".",
                        o->name
                    );
                    break;
                }
#else
                buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_SPANISH
"Incapaz de cambiar el modo de \"%s\"."
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Incapable pour changer le mode de \"%s\"."
#endif
#ifdef PROG_LANGUAGE_GERMAN
"Unfähig, den Modus von \"%s\" zu ändern."
#endif
#ifdef PROG_LANGUAGE_ITALIAN
"Incapace per cambiare il modo di \"%s\"."
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
"Maktesløs forandre modus av \"%s\"."
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
"Incapaz de mudar o modo de \"%s\"."
#endif
                ,
                    o->name
                );
#endif
                MESSAGE_CHMOD_FAILED(buf);
                g_free(buf);
          }
          else
          {
                /* Statistics */
#ifdef __MSW__
                if(stat(o->full_path, &o->lstat_buf))
#else
                if(lstat(o->full_path, &o->lstat_buf))
#endif
                    memset(&o->lstat_buf, 0x00, sizeof(struct stat));

                /* Update values. */
                FileBrowserObjectUpdateValues(fb, o);
          }
#endif
      }

        /* Need to redraw and update the value in the entry
         * since one of the selected object's names may have
         * changed.
         */
        FileBrowserListDraw(fb);
        FileBrowserEntrySetSelectedObjects(fb);

#undef MESSAGE_RENAME_FAILED
#undef TITLE_RENAME_FAILED
}

/*
 *    CHMod callback.
 */
static void FileBrowserCHModCB(GtkWidget *widget, gpointer data)
{
      gint i, x, y, width, height;
      mode_t m;
      gchar *value;
      const GList *glist;
      const FileBrowserObject *o;
      const FileBrowserColumn *column, *column2;
      FileBrowser *fb = FILE_BROWSER(data);
      if(fb == NULL)
          return;

      if(FPromptIsQuery())
          return;

        /* Get last selected object. */
        glist = fb->selection_end;
        i = (glist != NULL) ? (gint)glist->data : -1;
        o = FileBrowserGetObject(fb, i);
        if(o == NULL)
            return;

      m = o->lstat_buf.st_mode;

#ifdef S_ISLNK
      /* Cannot chmod links */
      if(S_ISLNK(m))
      {
          CDialogSetTransientFor(fb->toplevel);
          CDialogGetResponse(
"Change Mode Failed",
"You cannot change the mode of symbolic link objects",
            NULL,
            CDIALOG_ICON_WARNING,
            CDIALOG_BTNFLAG_OK,
            CDIALOG_BTNFLAG_OK
          );
          CDialogSetTransientFor(NULL);
          return;
      }
#endif

#if !defined(S_IRUSR) || !defined(S_IWUSR) || !defined(S_IXUSR)
      /* Since permissions not supported we cannot chmod */
      if(TRUE)
      {
            CDialogSetTransientFor(fb->toplevel);
            CDialogGetResponse(
"Change Mode Failed",
"Changing the mode of objects is not supported on this system.",
                NULL,
                CDIALOG_ICON_WARNING,
                CDIALOG_BTNFLAG_OK,
                CDIALOG_BTNFLAG_OK
            );
            CDialogSetTransientFor(NULL);
          return;
      }
#else
      /* Format current value */
      value = g_strdup_printf(
          "%c%c%c%c%c%c%c%c%c",
          (m & S_IRUSR) ? 'r' : '-',
          (m & S_IWUSR) ? 'w' : '-',
          (m & S_ISUID) ? 'S' :
            ((m & S_IXUSR) ? 'x' : '-'),
          (m & S_IRGRP) ? 'r' : '-',
          (m & S_IWGRP) ? 'w' : '-',
          (m & S_ISGID) ? 'G' :
            ((m & S_IXGRP) ? 'x' : '-'),
          (m & S_IROTH) ? 'r' : '-',
          (m & S_IWOTH) ? 'w' : '-',
          (m & S_ISVTX) ? 'T' :
            ((m & S_IXOTH) ? 'x' : '-')
      );
#endif

        /* Get fprompt geometry based on list display format. */
        x = o->x;
        y = o->y;
      width = MAX(o->width + 4, 80);
      height = -1;
        switch(fb->list_format)
        {
          case FB_LIST_FORMAT_VERTICAL_DETAILS:
          case FB_LIST_FORMAT_VERTICAL:
            if(fb->list_vsb != NULL)
            {
                GtkRange *range = GTK_RANGE(fb->list_vsb);
                GtkAdjustment *adj = range->adjustment;
                y -= (gint)((adj != NULL) ? adj->value : 0.0f);
            }
          column = FileBrowserListGetColumn(fb, 1);
          if(column != NULL)
            x += column->position;
          column2 = FileBrowserListGetColumn(fb, 2);
          if((column != NULL) && (column2 != NULL))
            width = MAX(column2->position - column->position + 4, 80);
            break;

          default:      /* FB_LIST_FORMAT_STANDARD */
            if(fb->list_hsb != NULL)
            {
                GtkRange *range = GTK_RANGE(fb->list_hsb);
                GtkAdjustment *adj = range->adjustment;
                x -= (gint)((adj != NULL) ? adj->value : 0.0f);
            }
            break;
        }
        /* Get root window relative coordinates of the object for
         * placement of the floating prompt.
         */
        if(fb->list_da != NULL)
        {
            GtkWidget *w = fb->list_da;
            GdkWindow *window = w->window;
            if(window != NULL)
            {
                gint rx, ry;
                gdk_window_get_deskrelative_origin(
                    window, &rx, &ry
                );
                x += rx;
                y += ry;
            }
        }
        /* At this point x and y should now be the root window
         * relative position for the floating prompt.
         */
        FPromptSetTransientFor(fb->toplevel);
        FPromptSetPosition(x - 2, y - 2);
        FPromptMapQuery(
            NULL,                       /* No label */
            value,
            NULL,                       /* No tooltip */
            FPROMPT_MAP_NO_MOVE,        /* Map code */
            width, height,          /* Width and height */
            0,                          /* No buttons */
            fb,                         /* Client data */
            NULL,                       /* No browse callback */
          FileBrowserCHModFPromptCB,
            NULL                        /* No cancel callback */
        );
        FPromptSetTransientFor(NULL);

      g_free(value);
}

/*
 *    List object delete callback.
 */
static void FileBrowserDeleteCB(GtkWidget *widget, gpointer data)
{
      gint objects_deleted = 0;
      gint i, response;
      gchar *buf;
      GList *glist;
      FileBrowserObject *o;
        FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

#ifdef PROG_LANGUAGE_ENGLISH
# define TITLE_DELETE_FAILED     "Delete Failed"
#endif
#ifdef PROG_LANGUAGE_SPANISH
# define TITLE_DELETE_FAILED     "Borre Fallado"
#endif
#ifdef PROG_LANGUAGE_FRENCH
# define TITLE_DELETE_FAILED     "Effacer Echoué"
#endif
#ifdef PROG_LANGUAGE_GERMAN
# define TITLE_DELETE_FAILED     "Löschen Sie Versagt"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
# define TITLE_DELETE_FAILED     "Cancellare Fallito"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
# define TITLE_DELETE_FAILED     "Stryk Failed"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
# define TITLE_DELETE_FAILED     "Anule Fracassado"
#endif

#define MESSAGE_DELETE_FAILED(s)        {       \
 CDialogSetTransientFor(fb->toplevel);          \
 CDialogGetResponse(                            \
  TITLE_DELETE_FAILED,                          \
  (s), NULL,                                    \
  CDIALOG_ICON_WARNING,                         \
  CDIALOG_BTNFLAG_OK,                           \
  CDIALOG_BTNFLAG_OK                            \
 );                                             \
 CDialogSetTransientFor(NULL);                  \
}

      if(CDialogIsQuery())
          return;

      /* Get selection. */
      glist = fb->selection;
      if(glist == NULL)
          return;

      FileBrowserSetBusy(fb, TRUE);

      /* Format confirmation message, if only one object is selected
       * then confirm with its name. Otherwise confirm the number of
       * objects to be deleted.
       */
      i = g_list_length(glist);
      if(i == 1)
      {
          i = (gint)glist->data;
          o = FileBrowserGetObject(fb, i);
          if((o != NULL) ? (o->name != NULL) : FALSE)
            buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"Are you sure you want to delete \"%s\"?",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"¿Usted está seguro que usted quiere borrar \"%s\"?",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Etes-vous sûr que vous voulez effacer \"%s\"?",
#endif
#ifdef PROG_LANGUAGE_GERMAN
"Sind Sie sicher Sie \"%s\" wollen löschen?",
#endif
#ifdef PROG_LANGUAGE_ITALIAN
"Lei sono sicuro che lei vuole cancellare \"%s\"?",
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
"Er De sikker De stryker \"%s\"?",
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
"Estão seguro quer anular \"%s\"?",
#endif
                o->name
            );
          else
            buf = STRDUP(
"Are you sure you want to delete this (unnamed) object?"
            );
      }
      else
      {
          buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"Delete %i selected objects?\n",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"¿Borra %i objetos escogidos?",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Efface %i objets choisis?",
#endif
#ifdef PROG_LANGUAGE_GERMAN
"Löschen Sie %i ausgewählte Objekte?",
#endif
#ifdef PROG_LANGUAGE_ITALIAN
"Cancella %i oggetti scelti?",
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
"Stryk %i valgte ut objekt?",
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
"Anule %i objetos selecionados?",
#endif
            i
          );
      }

      /* Confirm delete. */
      CDialogSetTransientFor(fb->toplevel);
      response = CDialogGetResponseIconData(
#ifdef PROG_LANGUAGE_ENGLISH
            "Confirm Delete",
#endif
#ifdef PROG_LANGUAGE_SPANISH
          "Confirme Borre",
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Confirmer Effacer",
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Bestätigen Sie Löscht",
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "Confermare Cancellare",
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Bekreft Delete",
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Confirme Anula",
#endif
          buf, NULL,
          (guint8 **)icon_trash_32x32_xpm,
          CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO,
          CDIALOG_BTNFLAG_NO
      );
        CDialogSetTransientFor(NULL);
      g_free(buf);
      if(response != CDIALOG_RESPONSE_YES)
      {
          FileBrowserSetBusy(fb, FALSE);
          return;
      }

      /* Iterate through selected objects. */
      while(glist != NULL)
      {
            i = (gint)glist->data;
          o = FileBrowserGetObject(fb, i);
          if((o != NULL) ? (o->full_path != NULL) : FALSE)
          {
            const gchar *full_path = o->full_path;
            if(ISLPATHDIR(full_path))
            {
                /* Delete directory */
                if(rmdir(full_path))
                    {
#ifdef PROG_LANGUAGE_ENGLISH
                        const gchar *name = o->name;
                        switch(errno)
                        {
                          case EPERM:
                            buf = g_strdup_printf(
"The filesystem that the directory \"%s\" is on does\n\
not permit the removal of directory objects.",
                                name
                            );
                            break;

                          case EACCES:
                            buf = g_strdup_printf(
"You do not have sufficient permission to delete \"%s\".",
                                name
                            );
                            break;

                          case ENAMETOOLONG:
                            buf = g_strdup_printf(
"The name of \"%s\" is too long.",
                                name
                            );
                            break;

                          case ENOENT:
                            buf = g_strdup_printf(
"A directory compoent of \"%s\" does not exist or\n\
is a dangling symbolic link.",
                                name
                            );
                            break;

                          case ENOTEMPTY:
                            buf = g_strdup_printf(
"The directory \"%s\" is not empty, you must\n\
make sure that the directory is empty before you can delete it.",
                                name
                            );
                            break;

                          case EBUSY:
                            buf = g_strdup_printf(
"The directory \"%s\" is currently in use and\n\
cannot be deleted.",
                                name
                            );
                            break;

                          case ENOMEM:
                            buf = STRDUP(
"The system is out of memory."
                            );
                            break;

                          case EROFS:
                            buf = g_strdup_printf(
"\"%s\" is located on a read-only filesystem.",
                                name
                            );
                            break;
#ifdef ELOOP
                          case ELOOP:
                            buf = g_strdup_printf(
"Too many symbolic links were encountered while resolving\n\
\"%s\".",
                                name
                            );
                            break;
#endif
                          default:
                            buf = g_strdup_printf(
"Unable to delete \"%s\".",
                                name
                            );
                            break;
                  }
#else
                        buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_SPANISH
"Incapaz de borrar \"%s\"."
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Incapable pour effacer \"%s\"."
#endif
#ifdef PROG_LANGUAGE_GERMAN
"Unfähig, \"%s\" zu löschen."
#endif
#ifdef PROG_LANGUAGE_ITALIAN
"Incapace per cancellare \"%s\"."
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
"Maktesløs stryke \"%s\"."
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
"Incapaz de anular \"%s\"."
#endif
                            ,
                            o->name
                        );
#endif
                        MESSAGE_DELETE_FAILED(buf);
                        g_free(buf);
                    }
                    else
                    {
                        objects_deleted++;
                    }
            }
            else
            {
                /* Delete object */
                if(unlink(full_path))
                {
#ifdef PROG_LANGUAGE_ENGLISH
                  const gchar *name = o->name;
                  switch(errno)
                  {
                    case EACCES:
                            buf = g_strdup_printf(
"You do not have sufficient permission to delete \"%s\".",
                                name
                            );
                      break;

                    case EPERM:
                            buf = g_strdup_printf(
"You do not have sufficient permission to delete \"%s\".",
                                name
                            );
                            break;

                    case ENAMETOOLONG:
                            buf = g_strdup_printf(
"The name of \"%s\" is too long.",
                                name
                            );
                            break;

                    case ENOENT:
                            buf = g_strdup_printf(
"A directory compoent of \"%s\" does not exist or\n\
is a dangling symbolic link.",
                                name
                            );
                            break;

                          case ENOTDIR:
                            buf = g_strdup_printf(
"A directory compoent of \"%s\" is not a directory.",
                                name
                            );
                            break;

                          case ENOMEM:
                            buf = STRDUP(
"The system is out of memory."
                            );
                            break;

                          case EROFS:
                            buf = g_strdup_printf(
"\"%s\" is located on a read-only filesystem.",
                                name
                            );
                            break;
#ifdef ELOOP
                          case ELOOP:
                            buf = g_strdup_printf(
"Too many symbolic links were encountered while resolving\n\
\"%s\".",
                                name
                            );
                            break;
#endif
                          case EIO:
                            buf = g_strdup_printf(
"An I/O error occured while attempting to delete \"%s\".",
                                name
                            );
                            break;

                    default:
                      buf = g_strdup_printf(
"Unable to delete \"%s\".",
                        name
                      );
                      break;
                  }
#else
                  buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_SPANISH
"Incapaz de borrar \"%s\"."
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Incapable pour effacer \"%s\"."
#endif
#ifdef PROG_LANGUAGE_GERMAN
"Unfähig, \"%s\" zu löschen."
#endif
#ifdef PROG_LANGUAGE_ITALIAN
"Incapace per cancellare \"%s\"."
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
"Maktesløs stryke \"%s\"."
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
"Incapaz de anular \"%s\"."
#endif
                      ,
                      o->name
                  );
#endif
                  MESSAGE_DELETE_FAILED(buf);
                  g_free(buf);
                }
                else
                {
                  objects_deleted++;
                }
            }
          }

          glist = glist->next;
      }

      /* Need to refresh everything due to delete. */
      FileBrowserRefreshCB(fb->refresh_btn, fb);

      FileBrowserSetBusy(fb, FALSE);

#undef MESSAGE_DELETE_FAILED
#undef TITLE_DELETE_FAILED
}

/*
 *    Entry enter callback.
 *
 *    This function is slightly different from the OK callback
 *    since it will switch to a directory if the single value
 *    is a directory.
 *
 *    This function will call the OK callback if there are
 *    multiple values specified (when it sees a ',' deliminator)
 *    or the single object is specified and (if it actually exists)
 *    does not lead to a directory.
 */
static void FileBrowserEntryEnterCB(GtkWidget *widget, gpointer data)
{
      gchar *s;
      GtkWidget *w;
      FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
            return;

      w = fb->entry;
      if(w == NULL)
          return;

      /* Get file name entry value */
      s = gtk_entry_get_text(GTK_ENTRY(w));
      if((s != NULL) ? (*s == '\0') : TRUE)
          return;

      s = STRDUP(s);    /* Make a copy of s */

      /* Filter specified? */
      if((strchr(s, '*') != NULL) || (strchr(s, '?') != NULL))
      {
          /* Reget listing with the newly specified filter */
          FileBrowserListUpdate(fb, s);
      }
      /* Multiple objects specified? */
      else if(strchr(s, ','))
      {
          /* Multiple objects selected, just call the OK callback. */
          FileBrowserOKCB(fb->ok_btn, fb);
      }
      /* Current directory specified? */
      else if(!strcmp(s, "."))
      {
          /* Do nothing. */
      }
      /* Parent directory specified? */
      else if(!strcmp(s, ".."))
      {
          /* Go to parent location. */
          const gchar *cur_location = FileBrowserGetLocation(fb);
          gchar *parent = STRDUP(GetParentDir(cur_location));
          FileBrowserSetLocation(fb, parent);
          g_free(parent);
      }
      /* Home directory specified? */
      else if(*s == '~')
      {
          /* Go to home directory. */
          FileBrowserSetLocation(fb, s);
      }
      else
      {
          /* All else assume single object specified, it may or may
           * not actually exist.
           */
          gchar *full_path;
          struct stat stat_buf;

          /* Check if the object specified is specified as a full
           * path and if it is not then prefix the current location
           * to it.
           */
          if(ISPATHABSOLUTE(s))
          {
            full_path = STRDUP(s);
          }
          else
          {
            const gchar *cur_location = FileBrowserGetLocation(fb);
            full_path = STRDUP(PrefixPaths(cur_location, s));
          }

          /* Check if the object specified by full_path exists and if
           * it leads to a directory.
           */
          if(stat(full_path, &stat_buf))
          {
            /* Cannot stat object (note that it may exist locally
             * as a symbolic link), the user may want to save as a
             * new object, so go ahead and call the OK callback on
             * this.
             */
            FileBrowserOKCB(fb->ok_btn, fb);
          }
          else
          {
            /* If the object specified by full_path is a directory
             * then go to that directory, otherwise call the OK
             * callback.
             */
            if(S_ISDIR(stat_buf.st_mode))
                FileBrowserSetLocation(fb, full_path);
            else
                FileBrowserOKCB(fb->ok_btn, fb);
          }

          g_free(full_path);
      }

      g_free(s);
}

/*
 *    File type list selected value changed callback.
 */
static void FileBrowserTypeListChangeCB(
      GtkWidget *widget, gpointer data, GList *glist
)
{
      const gchar *s, *ext;
      gchar *s2;
      GtkEntry *entry;
      GtkCombo *combo;
      fb_type_struct *t;
      FileBrowser *fb = FILE_BROWSER(data);
        if(fb == NULL)
          return;

      combo = (GtkCombo *)fb->type_combo;
      if(combo == NULL)
          return;

      entry = GTK_ENTRY(combo->entry);

      /* Get selected file type value */
      s = gtk_entry_get_text(entry);
      if((s != NULL) ? (*s == '\0') : TRUE)
          return;

      /* Parse the selected file type and store the values in the
       * current file type structure.
       *
       * The format of s will be:
       *
       *    "desc(ext)"
       *
       *    "Description of Format (*.ext1 *.ext2)"
       */
      t = &fb->cur_type;

      /* Get name */
      g_free(t->name);
      t->name = STRDUP(s);
      s2 = strrchr(t->name, '(');
      if(s2 != NULL)
      {
          if(s2 > t->name)
            s2--;
          while((s2 > t->name) && ISBLANK(*s2))
            s2--;
          *(s2 + 1) = '\0';
      }

      /* Get extensions list */
      g_free(t->ext);
      t->ext = NULL;

      ext = strrchr(s, '(');
      if(ext != NULL)
      {
          ext++;
            while(ISBLANK(*ext))
                ext++;

          t->ext = STRDUP(ext);
          s2 = strchr(t->ext, ')');
          if(s2 != NULL)
          {
            if(s2 > t->ext)
                s2--;
            while((s2 > t->ext) && ISBLANK(*s2))
                s2--;
            *(s2 + 1) = '\0';
          }
        }

      /* Update values that depend on file type changes only if the
       * file type change freeze count is 0.
       */
      if(fb->type_change_freeze_count > 0)
          return;

      fb->type_change_freeze_count++;

      /* Need to update list due to type change */
      FileBrowserListUpdate(fb, NULL);

      fb->type_change_freeze_count--;
}


/*
 *    Initializes the file browser.
 */
gint FileBrowserInit(void)
{
      gint border_major = 5, border_minor = 2;
      gpointer w_ptr1, w_ptr2;
      GList *glist;
      GdkWindow *window;
      GtkAdjustment *adj;
      GtkAccelGroup *accelgrp;
      GtkWidget *w, *parent, *parent2, *parent3, *parent4, *parent5;
      GtkEntry *entry;
      GtkCombo *combo;
      pulist_struct *pulist;
      FileBrowser *fb = &file_browser;


      /* Reset values. */
      fb->map_state = FALSE;
      fb->busy_count = 0;
      fb->accelgrp = accelgrp = gtk_accel_group_new();
      fb->cur_busy = gdk_cursor_new(GDK_WATCH);
      fb->cur_column_hresize = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
      fb->cur_location = NULL;
      fb->block_loop_level = 0;
      fb->user_response = FALSE;
      fb->list_format = FB_LIST_FORMAT_STANDARD;
      fb->list_format_toggle_freeze_count = 0;
      fb->column = NULL;
      fb->total_columns = 0;
      fb->type_change_freeze_count = 0;
        memset(&fb->cur_type, 0x00, sizeof(fb_type_struct));
      fb->selected_path = NULL;
      fb->total_selected_paths = 0;
      fb->icon = NULL;
      fb->total_icons = 0;
#ifdef __MSW__
      fb->uid = 0;
      fb->euid = 0;
      fb->gid = 0;
      fb->egid = 0;
#else
      fb->uid = getuid();
      fb->euid = geteuid();
        fb->gid = getgid();
        fb->egid = getegid();
#endif
#ifdef __MSW__
      fb->home_path = STRDUP("c:\\");
#else
      fb->home_path = STRDUP(getenv("HOME"));
#endif
      fb->drive_path = NULL;
      fb->total_drive_paths = 0;
      fb->focus_object = -1;
      fb->selection = fb->selection_end = NULL;
      fb->object = NULL;
      fb->total_objects = 0;
      fb->objects_per_row = 0;

        /* Toplevel. */
      fb->toplevel = w = gtk_window_new(GTK_WINDOW_DIALOG);
      gtk_widget_set_usize(w, FB_WIDTH, FB_HEIGHT);
        gtk_widget_realize(w);
        gtk_window_set_title(GTK_WINDOW(w), "Select File");
        window = w->window;
        if(window != NULL)
        {
            gdk_window_set_decorations(
                window,
                GDK_DECOR_TITLE | GDK_DECOR_MENU | GDK_DECOR_MINIMIZE
            );
            gdk_window_set_functions(
                window,
                GDK_FUNC_MOVE | GDK_FUNC_MINIMIZE | GDK_FUNC_CLOSE
            );
        }
        gtk_signal_connect(
            GTK_OBJECT(w), "delete_event",
            GTK_SIGNAL_FUNC(FileBrowserDeleteEventCB), fb
        );
        gtk_container_border_width(GTK_CONTAINER(w), 0);
        gtk_accel_group_attach(accelgrp, GTK_OBJECT(w));
        parent = w;

        /* Main vbox */
      fb->main_vbox = w = gtk_vbox_new(FALSE, border_major);
      gtk_container_border_width(GTK_CONTAINER(w), border_major);
        gtk_container_add(GTK_CONTAINER(parent), w);
        gtk_widget_show(w);
        parent = w;


      w = gtk_hbox_new(FALSE, border_major);
        gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
        gtk_widget_show(w);
        parent2 = w;


      /* Hbox for directory popup list label and drawing area*/
      w = gtk_hbox_new(FALSE, border_minor);
        gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
        gtk_widget_show(w);
        parent3 = w;

      w = gtk_label_new(
#ifdef PROG_LANGUAGE_ENGLISH
            "Location:"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Ubicación:"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "L'emplacement:"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Ort:"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "Posizione:"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Plassering:"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Localidade:"
#endif
      );
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
        gtk_widget_show(w);

      /* Hbox for Directory popup list and map button */
      w = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0);
        gtk_widget_show(w);
        parent4 = w;

      /* Directory popup list frame and drawing area */
      w = gtk_frame_new(NULL);
        gtk_widget_set_usize(w, -1, 20 + (2 * 2));
      gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
      gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0);
      gtk_widget_show(w);
      parent5 = w;
      /* Drawing area */
      fb->dir_pulist_da = w = gtk_drawing_area_new();
        GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS);
        gtk_widget_add_events(
            w,
          GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK |
          GDK_FOCUS_CHANGE_MASK |
            GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
            GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "expose_event",
            GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "focus_in_event",
            GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "focus_out_event",
            GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "button_press_event",
            GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "button_release_event",
            GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
        );
      gtk_container_add(GTK_CONTAINER(parent5), w);
        gtk_widget_show(w);

      /* Directory popup list button */
        fb->dir_pulist_btn = w = PUListNewMapButtonArrow(
          GTK_ARROW_DOWN, GTK_SHADOW_OUT,
            FileBrowserDirPUListMapCB, fb
        );
        gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
        gtk_widget_show(w);

      /* Directory popup list */
      fb->dir_pulist = pulist = PUListNew();

        /* Go To Parent */
        fb->goto_parent_btn = w = GUIButtonPixmap(
            (guint8 **)icon_folder_parent_20x20_xpm
        );
        gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(FileBrowserGoToParentCB), fb
        );
/*
        gtk_accel_group_add(
            accelgrp, GDK_BackSpace, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
 */
        GUISetWidgetTip(w,
#ifdef PROG_LANGUAGE_ENGLISH
            "Parent"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "El Padre"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Le Parent"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Elternteil"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "Il Genitore"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Parent"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Pai"
#endif
      );
        gtk_widget_show(w);

        /* New Directory */
        fb->new_directory_btn = w = GUIButtonPixmap(
            (guint8 **)icon_folder_closed_20x20_xpm
        );
        gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(FileBrowserNewDirectoryCB), fb
        );
/*
        gtk_accel_group_add(
            accelgrp, GDK_Insert, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
 */
        GUISetWidgetTip(w,
#ifdef PROG_LANGUAGE_ENGLISH
            "New Directory"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Guía Nueva"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Le Nouvel Annuaire"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Neues Verzeichnis"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "L'Elenco Nuovo"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "New Directory"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Novo Guia"
#endif
      );
        gtk_widget_show(w);

        /* Rename */
        fb->rename_btn = w = GUIButtonPixmap(
            (guint8 **)icon_rename_20x20_xpm
        );
        gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(FileBrowserRenameCB), fb
        );
/*
        gtk_accel_group_add(
            accelgrp, GDK_F2, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
 */
        GUISetWidgetTip(w,
#ifdef PROG_LANGUAGE_ENGLISH
            "Rename"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Reagrupa"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Renommer"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Um"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "Rinominare"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Rename"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Rename"
#endif
      );
        gtk_widget_show(w);

      /* Refresh */
        fb->refresh_btn = w = GUIButtonPixmap(
            (guint8 **)icon_reload_20x20_xpm
        );
      gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(FileBrowserRefreshCB), fb
        );
/*
        gtk_accel_group_add(
            accelgrp, GDK_F5, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
 */
      GUISetWidgetTip(w,
#ifdef PROG_LANGUAGE_ENGLISH
            "Refresh"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Refresca"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Rafraîchir"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Refresh"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "Rinfrescare"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Refresh"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Refresca-se"
#endif
      );
        gtk_widget_show(w);


      /* Hbox for list format buttons. */
        w = gtk_hbox_new(FALSE, 0);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
        gtk_widget_show(w);
        parent3 = w;

      fb->list_format_standard_tb = w = GUIToggleButtonPixmap(
          (guint8 **)icon_fb_list_standard_20x20_xpm
      );
        gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
        gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "toggled",
            GTK_SIGNAL_FUNC(FileBrowserListFormatToggleCB), fb
        );
        GUISetWidgetTip(w,
#ifdef PROG_LANGUAGE_ENGLISH
            "Standard Listing"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Lista Del Estándar"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Liste De Norme"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Standard Liste"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "L'Elenco Di Norma"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Standard List"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Lista De Padrão"
#endif
      );
        gtk_widget_show(w);

        fb->list_format_vertical_tb = w = GUIToggleButtonPixmap(
            (guint8 **)icon_fb_list_vertical_20x20_xpm
        );
        gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
        gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "toggled",
            GTK_SIGNAL_FUNC(FileBrowserListFormatToggleCB), fb
        );
        GUISetWidgetTip(w,
#ifdef PROG_LANGUAGE_ENGLISH
            "Vertical Listing"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Lista Vertical"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Liste Verticale"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Senkrechte Liste"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "L'Elenco Verticale"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Vertical List"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Lista Vertical"
#endif
        );
/* Don't offer the use of the vertical list anymore */
/*    gtk_widget_show(w); */

        fb->list_format_vertical_details_tb = w = GUIToggleButtonPixmap(
            (guint8 **)icon_fb_list_vertical_details_20x20_xpm
        );
        gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
        gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "toggled",
            GTK_SIGNAL_FUNC(FileBrowserListFormatToggleCB), fb
        );
        GUISetWidgetTip(w,
#ifdef PROG_LANGUAGE_ENGLISH
            "Detailed Listing"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Lista Detallada"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Liste Détaillée"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Ausführliche Liste"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "L'Elenco Dettagliato"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Detailed List"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Lista De Detailed"
#endif
      );
        gtk_widget_show(w);



      /* Table for list. */
      w = gtk_table_new(2, 2, FALSE);
      gtk_table_set_row_spacing(GTK_TABLE(w), 0, border_minor);
        gtk_table_set_col_spacing(GTK_TABLE(w), 0, border_minor);
      gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
        gtk_widget_show(w);
      parent2 = w;

      /* Frame and vbox with in for parenting the list header and
       * the list.
       */
        w = gtk_frame_new(NULL);
        gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
      gtk_table_attach(
          GTK_TABLE(parent2), w,
          0, 1, 0, 1,
          GTK_FILL | GTK_SHRINK | GTK_EXPAND,
            GTK_FILL | GTK_SHRINK | GTK_EXPAND,
          0, 0
      );
        gtk_widget_show(w);
        parent3 = w;
      w = gtk_vbox_new(FALSE, 0);
      gtk_container_add(GTK_CONTAINER(parent3), w);
      gtk_widget_show(w);
        parent3 = w;


      /* List header. */
      fb->list_header_da = w = gtk_drawing_area_new();
      gtk_widget_set_usize(w, FB_LIST_HEADER_WIDTH, FB_LIST_HEADER_HEIGHT);
        GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS);
        gtk_widget_add_events(
            w,
            GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK |
            GDK_FOCUS_CHANGE_MASK |
            GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
            GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
          GDK_POINTER_MOTION_MASK |
          GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "configure_event",
            GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "expose_event",
            GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "focus_in_event",
            GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "focus_out_event",
            GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "button_press_event",
            GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "button_release_event",
            GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "motion_notify_event",
            GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "enter_notify_event",
            GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "leave_notify_event",
            GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
        );
        gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
        gtk_widget_show(w);

      /* List. */
        fb->list_da = w = gtk_drawing_area_new();
      gtk_widget_set_usize(w, FB_LIST_WIDTH, FB_LIST_HEIGHT);
        GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS);
        gtk_widget_add_events(
            w,
            GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK |
          GDK_FOCUS_CHANGE_MASK |
            GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
            GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "configure_event",
            GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "expose_event",
            GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "focus_in_event",
            GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "focus_out_event",
            GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "key_press_event",
            GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "key_release_event",
            GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "button_press_event",
            GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
        );
        gtk_signal_connect(
            GTK_OBJECT(w), "button_release_event",
            GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
        );
        gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0);
        gtk_widget_show(w);
        if(w != NULL)
        {
            const GtkTargetEntry dnd_tar_types[] = {
                {"text/plain",          0,      0},
                {"text/uri-list",       0,      1},
                {"STRING",              0,      2}
            };
          const GtkTargetEntry dnd_src_types[] = {
            {"text/plain",          0,    0},
            {"text/uri-list", 0,    1},
            {"STRING",        0,    2}
          };
            GUIDNDSetTar(
                w,
                dnd_tar_types,
                sizeof(dnd_tar_types) / sizeof(GtkTargetEntry),
                GDK_ACTION_COPY,                /* Actions */
                GDK_ACTION_COPY,                /* Default action if same */
                GDK_ACTION_COPY,                /* Default action */
                FileBrowserDragDataReceivedCB,
                fb
            );
            gtk_signal_connect_after(
                GTK_OBJECT(w), "drag_motion",
                GTK_SIGNAL_FUNC(FileBrowserDragmotionCB), fb
            );
          GUIDNDSetSrc(
            w,
            dnd_src_types,
            sizeof(dnd_src_types) / sizeof(GtkTargetEntry),
            GDK_ACTION_COPY | GDK_ACTION_MOVE |
                GDK_ACTION_LINK,          /* Actions */
            GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,      /* Buttons */
            NULL,
            FileBrowserDragDataGetCB,
            FileBrowserDragDataDeleteCB,
            NULL,
            fb
          );
        }

        /* List vertical scrollbar */
      adj = (GtkAdjustment *)gtk_adjustment_new(
          0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
      );
        fb->list_vsb = w = gtk_vscrollbar_new(adj);
        gtk_table_attach(
            GTK_TABLE(parent2), w,
            1, 2, 0, 1,
          0,
          GTK_FILL | GTK_SHRINK | GTK_EXPAND,
            0, 0
        );
      gtk_signal_connect(
            GTK_OBJECT(adj), "value_changed",
            GTK_SIGNAL_FUNC(FileBrowserListScrollCB), fb
        );

        /* List horizontal scrollbar */
        adj = (GtkAdjustment *)gtk_adjustment_new(
            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
        );
        fb->list_hsb = w = gtk_hscrollbar_new(adj);
        gtk_table_attach(
            GTK_TABLE(parent2), w,
            0, 1, 1, 2,
            GTK_FILL | GTK_SHRINK | GTK_EXPAND,
            0,
            0, 0
        );
        gtk_signal_connect(
            GTK_OBJECT(adj), "value_changed",
            GTK_SIGNAL_FUNC(FileBrowserListScrollCB), fb
        );

      /* List right-click menu */
      if(TRUE)
      {
            GtkWidget *menu = (GtkWidget *)GUIMenuCreate();
          guint8 **icon;
            const gchar *label;
            gint accel_key;
            guint accel_mods;
            gpointer mclient_data = fb;
            void (*func_cb)(GtkWidget *w, gpointer);

#define DO_ADD_MENU_ITEM_LABEL      {           \
 w = GUIMenuItemCreate(                   \
  menu, GUI_MENU_ITEM_TYPE_LABEL, accelgrp,     \
  icon, label, accel_key, accel_mods, &w_ptr1,  \
  mclient_data, func_cb                   \
 );                                 \
}
#define DO_ADD_MENU_ITEM_CHECK      {           \
 w = GUIMenuItemCreate(                   \
  menu, GUI_MENU_ITEM_TYPE_CHECK, accelgrp,     \
  icon, label, accel_key, accel_mods, &w_ptr1,  \
  mclient_data, func_cb                   \
 );                                 \
}
#define DO_ADD_MENU_SEP {                 \
 w = GUIMenuItemCreate(                   \
  menu, GUI_MENU_ITEM_TYPE_SEPARATOR, NULL,     \
  NULL, NULL, 0, 0, NULL,                 \
  NULL, NULL                              \
 );                                 \
}

          icon = (u_int8_t **)icon_select_20x20_xpm;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "Select";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "Selecto";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Choisir";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Erlesen Benennen";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "Scegliere";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "Utvalgt";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Selecione";
#endif
            accel_key = GDK_Return;
            accel_mods = 0;
            func_cb = FileBrowserEntryEnterCB;
            DO_ADD_MENU_ITEM_LABEL
/*        fb->select_mi = w; */

          DO_ADD_MENU_SEP

            icon = (u_int8_t **)icon_folder_parent_20x20_xpm;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "Parent";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "El Padre";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Le Parent";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Elternteil";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "Il Genitore";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "Parent";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Pai";
#endif
            accel_key = GDK_BackSpace;
            accel_mods = 0;
            func_cb = FileBrowserGoToParentCB;
            DO_ADD_MENU_ITEM_LABEL
/*          fb->goto_parent_mi = w; */

            icon = (u_int8_t **)icon_reload_20x20_xpm;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "Refresh";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "Refresca";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Rafraîchir";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Refresh";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "Rinfrescare";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "Refresh";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Refresca-se";
#endif
            accel_key = GDK_F5;
            accel_mods = 0;
            func_cb = FileBrowserRefreshCB;
            DO_ADD_MENU_ITEM_LABEL
/*          fb->refresh_mi = w; */

          DO_ADD_MENU_SEP

            icon = (u_int8_t **)icon_folder_closed_20x20_xpm;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "New Directory";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "Guía Nueva";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Le Nouvel Annuaire";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Neues Verzeichnis";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "L'Elenco Nuovo";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "New Directory";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Novo Guia";
#endif
            accel_key = GDK_Insert;
            accel_mods = 0;
            func_cb = FileBrowserNewDirectoryCB;
            DO_ADD_MENU_ITEM_LABEL
/*          fb->new_directory_mi = w; */

            icon = (u_int8_t **)icon_rename_20x20_xpm;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "Rename";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "Reagrupa";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Renommer";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Um";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "Rinominare";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "Rename";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Rename";
#endif
            accel_key = GDK_F2;
            accel_mods = 0;
            func_cb = FileBrowserRenameCB;
            DO_ADD_MENU_ITEM_LABEL
/*          fb->rename_mi = w; */

            icon = (u_int8_t **)icon_chmod_20x20_xpm;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "Change Mode";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "Cambie El Modo";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Changer Le Mode";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Ändern Sie Modus";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "Cambiare Il Modo";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "Forandr Mode";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Mude Modo";
#endif
            accel_key = GDK_F9;
            accel_mods = 0;
            func_cb = FileBrowserCHModCB;
            DO_ADD_MENU_ITEM_LABEL
/*          fb->chmod_mi = w; */

            icon = (u_int8_t **)icon_cancel_20x20_xpm;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "Delete";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "Borra";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Efface";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Löschen";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "Cancella";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "Delete";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Anulam";
#endif
            accel_key = GDK_Delete;
            accel_mods = 0;
            func_cb = FileBrowserDeleteCB;
            DO_ADD_MENU_ITEM_LABEL
/*          fb->delete_mi = w; */

          DO_ADD_MENU_SEP

            icon = NULL;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "Select All";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "Escoge Todo";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Choisit Tout";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Wählen Alle";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "Sceglie Tutto Il";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "Velger Ut All";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Selecionam Todo";
#endif
            accel_key = 'a';
            accel_mods = GDK_CONTROL_MASK;
            func_cb = FileBrowserSelectAllCB;
            DO_ADD_MENU_ITEM_LABEL
/*          fb->select_all_mi = w; */

            icon = NULL;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "Unselect All";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "Unescoge Todo";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Desélectionner Tout";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Unselect Alle";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "L'unselect Tutta Il";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "Unselect All";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Unselect Todo";
#endif
            accel_key = 'u';
            accel_mods = GDK_CONTROL_MASK;
            func_cb = FileBrowserUnselectAllCB;
            DO_ADD_MENU_ITEM_LABEL
/*          fb->unselect_all_mi = w; */

            icon = NULL;
#ifdef PROG_LANGUAGE_ENGLISH
            label = "Invert Selection";
#endif
#ifdef PROG_LANGUAGE_SPANISH
            label = "Invierte Selección";
#endif
#ifdef PROG_LANGUAGE_FRENCH
            label = "Inverser Sélection";
#endif
#ifdef PROG_LANGUAGE_GERMAN
            label = "Auswahl Invertieren Aus";
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            label = "Invertire La Selezione";
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            label = "Inverterer Utvelging";
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            label = "Nverte Seleção";
#endif
            accel_key = 'i';
            accel_mods = GDK_CONTROL_MASK;
            func_cb = FileBrowserInvertSelectionCB;
            DO_ADD_MENU_ITEM_LABEL
/*          fb->invert_selection_mi = w; */




          fb->list_menu = menu;

#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_ITEM_CHECK
#undef DO_ADD_MENU_SEP
      }


      w = gtk_hseparator_new();
      gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
        gtk_widget_show(w);


      w = gtk_hbox_new(FALSE, border_major);
      gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
      gtk_widget_show(w);
      parent2 = w;

      /* Vbox for location entry and file type combo. */
      w = gtk_vbox_new(FALSE, border_minor);
      gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
      gtk_widget_show(w);
        parent3 = w;

      /* File name entry */
      w = GUIPromptBar(
          NULL,
#ifdef PROG_LANGUAGE_ENGLISH
            "File Name:"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Archive Nombre:"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Nom De Fichier:"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Dateiname:"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "Schedare Nome:"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Arkiver Name:"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Arquive Nome:"
#endif
            ,
          &w_ptr1, &w_ptr2
      );
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
        gtk_widget_show(w);
      fb->entry = w = GTK_WIDGET(w_ptr2);
      entry = GTK_ENTRY(w);
        gtk_signal_connect(
            GTK_OBJECT(w), "activate",
            GTK_SIGNAL_FUNC(FileBrowserEntryEnterCB), fb
        );
        GUISetWidgetTip(w,
#ifdef PROG_LANGUAGE_ENGLISH
          "Enter the name of the object, you may specify more than\
 one object (separate each name with a ',' character)"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Entre el nombre del objeto, usted puede especificar más\
 que un objeto (separa cada nombre con un ',' el carácter)"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Entrer le nom de l'objet, vous pouvez spécifier plus\
 qu'un objet (sépare chaque nom avec un ',' le caractère)"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Tragen Sie den Namen des Objekts, Sie können angeben\
 mehr als ein Objekt ein (trennen Sie jeden Namen mit einem ',' charakter)"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "Entrare il nome dell'oggetto, lei può specificare più\
 di un oggetto (separa ogni nome con un ',' il carattere)"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "Gå inn i navnet av objektet, De spesifiserer mere enn\
 et objekt (separere hver navn med et ',' karakter)"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Entre o nome do objeto, você pode especificar mais de\
 um objeto (separa cada nome com um ',' caráter)"
#endif
      );
      if(w != NULL)
      {
          const GtkTargetEntry dnd_tar_types[] = {
            {"text/plain",          0,    0},
            {"text/uri-list", 0,    1},
            {"STRING",        0,    2}
          };
          GUIDNDSetTar(
            w,
            dnd_tar_types,
            sizeof(dnd_tar_types) / sizeof(GtkTargetEntry),
            GDK_ACTION_COPY,        /* Actions */
            GDK_ACTION_COPY,        /* Default action if same */
            GDK_ACTION_COPY,        /* Default action */
            FileBrowserDragDataReceivedCB,
            fb
          );
          gtk_signal_connect_after(
            GTK_OBJECT(w), "drag_motion",
            GTK_SIGNAL_FUNC(FileBrowserDragmotionCB), fb
          );
      }

      /* File types combo */
      glist = NULL;
      glist = g_list_append(glist, STRDUP(FB_DEFAULT_TYPE_STR));
      w = GUIComboCreate(
#ifdef PROG_LANGUAGE_ENGLISH
            "File Type:"
#endif
#ifdef PROG_LANGUAGE_SPANISH
            "Tipo Archivo:"
#endif
#ifdef PROG_LANGUAGE_FRENCH
            "Type De Fichier:"
#endif
#ifdef PROG_LANGUAGE_GERMAN
            "Legt Typ Ab:"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
            "Tipo Di File:"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
            "File Type:"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
            "Tipo Arquivo:"
#endif
          ,
          (const gchar *)glist->data,
          glist,        /* Initial GList */
          100,          /* Maximum items */
          &w_ptr1,            /* GtkCombo return */
          fb,
          NULL,
          FileBrowserTypeListChangeCB
      );
      gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
      gtk_widget_show(w);

      g_list_foreach(glist, (GFunc)g_free, NULL);
      g_list_free(glist);
      combo = (GtkCombo *)w_ptr1;
      entry = GTK_ENTRY(combo->entry);
      fb->type_combo = GTK_WIDGET(combo);
      gtk_entry_set_editable(entry, FALSE);
        GUISetWidgetTip(
          GTK_WIDGET(entry),
#ifdef PROG_LANGUAGE_ENGLISH
"Select the type of file that you are looking for"
#endif
#ifdef PROG_LANGUAGE_SPANISH
"Escoja el tipo del archivo que usted buscan"
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Choisir le type de fichier que vous cherchez"
#endif
#ifdef PROG_LANGUAGE_GERMAN
"Wählen Sie die Art Akte, die Sie suchen aus"
#endif
#ifdef PROG_LANGUAGE_ITALIAN
"Scegliere il tipo di file che lei cercano"
#endif
#ifdef PROG_LANGUAGE_NORWEGIAN
"Velg ut typen arkivet som De leter etter"
#endif
#ifdef PROG_LANGUAGE_PORTUGUESE
"Selecione o tipo de arquivo que você procurar"
#endif
      );

        /* Vbox for ok and cancel buttons. */
        w = gtk_vbox_new(TRUE, border_minor);
        gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
        gtk_widget_show(w);
        parent3 = w;


      /* OK button */
      fb->ok_btn = w = GUIButtonPixmapLabelH(
            (u_int8_t **)icon_ok_20x20_xpm, "OK", &w_ptr1
        );
      fb->ok_btn_label = (GtkWidget *)w_ptr1;
        gtk_widget_set_usize(w, FB_BTN_WIDTH, FB_BTN_HEIGHT);
        GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
        gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(FileBrowserOKCB), fb
        );
/*
        gtk_accel_group_add(
            accelgrp, GDK_Return, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_accel_group_add(
            accelgrp, GDK_3270_Enter, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_accel_group_add(
            accelgrp, GDK_KP_Enter, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_accel_group_add(
            accelgrp, GDK_ISO_Enter, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
 */
      gtk_widget_show(w);

        /* Cancel button */
        fb->cancel_btn = w = GUIButtonPixmapLabelH(
            (u_int8_t **)icon_cancel_20x20_xpm, "Cancel", &w_ptr1
        );
        fb->cancel_btn_label = (GtkWidget *)w_ptr1;
        gtk_widget_set_usize(w, FB_BTN_WIDTH, FB_BTN_HEIGHT);
        GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
        gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, FALSE, 0);
        gtk_signal_connect(
            GTK_OBJECT(w), "clicked",
            GTK_SIGNAL_FUNC(FileBrowserCancelCB), fb
        );
        gtk_accel_group_add(
            accelgrp, GDK_Escape, 0, GTK_ACCEL_VISIBLE,
            GTK_OBJECT(w), "clicked"
        );
        gtk_widget_show(w);


      /* Load icons, the order is important because the index must
       * match FB_ICON_* values as indices.
       */
      if(fb->toplevel != NULL)
      {
          gint i = 0;
          gpointer icon_list[] = FB_ICON_DATA_LIST;
          while(icon_list[i] != NULL)
          {
            FileBrowserIconAppend(
                fb,
                (guint8 **)icon_list[i + 1],    /* XPM data */
                (const gchar *)icon_list[i + 0] /* Description */
            );
            i += 6;
          }
      }

      /* Force set of default list format. */
      fb->list_format = -1;
      FileBrowserSetListFormat(fb, FB_LIST_FORMAT_STANDARD);

      return(0);
}

/*
 *      Sets dialog to be a transient for the given toplevel window
 *      widget w. If w is NULL then no transient for will be unset.
 */
void FileBrowserSetTransientFor(GtkWidget *w)
{
        FileBrowser *fb = &file_browser;
        if(fb->toplevel != NULL)
        {
          GtkWidget *toplevel = fb->toplevel;

            if(w != NULL)
            {
                if(!GTK_IS_WINDOW(w))
                    return;

/* Since the file browser itself has popup windows that may need to be
 * set modal, we cannot set the file browser itself modal.
                gtk_window_set_modal(
                    GTK_WINDOW(toplevel), TRUE
                );
 */
                gtk_window_set_transient_for(
                    GTK_WINDOW(toplevel), GTK_WINDOW(w)
                );
            }
            else
            {
/*
                gtk_window_set_modal(
                    GTK_WINDOW(toplevel), FALSE
                );
 */
                gtk_window_set_transient_for(
                    GTK_WINDOW(toplevel), NULL
                );
            }
        }
}

/*
 *      Returns TRUE if currently blocking for query.
 */
gbool FileBrowserIsQuery(void) 
{
        FileBrowser *fb = &file_browser;

        if(fb->block_loop_level > 0)
            return(TRUE);
        else
            return(FALSE);
}

/*
 *      Ends query if any and returns a not available response.
 */
void FileBrowserBreakQuery(void)
{
        FileBrowser *fb = &file_browser;

      fb->user_response = FALSE;

        /* Break out of an additional blocking loops. */
        while(fb->block_loop_level > 0)
        {
            gtk_main_quit();
            fb->block_loop_level--;
        }
        fb->block_loop_level = 0;
}

/*
 *    Maps the file browser and sets up the inital values.
 *
 *    Returns TRUE if a path was selected or FALSE if user canceled.
 *
 *    For most values that are set NULL, the value is left unchanged.
 *    All given values are coppied.
 *
 *    If type is set to NULL however, then the type list on the file
 *    browser will be left empty.
 *
 *    All returned pointer values should be considered statically
 *    allocated. The returned pointer for type_rtn may point to
 *    a structure in the input type list.
 */
gbool FileBrowserGetResponse(
      const gchar *title,
      const gchar *ok_label, const gchar *cancel_label,
      const gchar *path,
      fb_type_struct **type, gint total_types,
        gchar ***path_rtn, gint *path_total_rtns,
      fb_type_struct **type_rtn
)
{
      GtkWidget *w, *toplevel;
      FileBrowser *fb = &file_browser;


        /* Do not handle response if already waiting for a response,
         * return with a not available response code.
         */
        if(fb->block_loop_level > 0)
      {
          if(path_rtn != NULL)
            *path_rtn = NULL;
          if(path_total_rtns != NULL)
            *path_total_rtns = 0;
          if(type_rtn != NULL)
            *type_rtn = NULL;

            return(FALSE);
      }

      /* Reset values. */
      fb->user_response = FALSE;

      g_free(fb->cur_type.name);
      g_free(fb->cur_type.ext);
      memset(&fb->cur_type, 0x00, sizeof(fb_type_struct));


      /* Reset returns. */
      if(path_rtn != NULL)
          *path_rtn = NULL;
      if(path_total_rtns != NULL)
          *path_total_rtns = 0;
      if(type_rtn != NULL)
          *type_rtn = NULL;


      toplevel = fb->toplevel;

      /* Reget drive paths. */
      StringFreeArray(fb->drive_path, fb->total_drive_paths);
      fb->drive_path = FileBrowserGetDrivePaths(&fb->total_drive_paths);

      /* Update title if specified. */
      if(title != NULL)
          gtk_window_set_title(GTK_WINDOW(toplevel), title);

      /* Update ok button label if specified. */
      w = fb->ok_btn_label;
      if((ok_label != NULL) && (w != NULL))
          gtk_label_set_text(GTK_LABEL(w), ok_label);

        /* Update cancel button label if specified. */
        w = fb->cancel_btn_label;
        if((cancel_label != NULL) && (w != NULL))
            gtk_label_set_text(GTK_LABEL(w), cancel_label);

      /* Set file types list if a new set of file types is given,
       * otherwise we keep the original file types list.
       */
      w = fb->type_combo;
      if((type != NULL) && (total_types > 0) && (w != NULL))
      {
          GtkCombo *combo = GTK_COMBO(w);
          GtkEntry *entry = GTK_ENTRY(combo->entry);
          GList *glist = NULL;
          const gchar *first_type_s = NULL;
          int i;
          gchar *s;
          const fb_type_struct *t;

          /* Freeze when changing file types so other things like
           * the list don't get messed with.
           */
          fb->type_change_freeze_count++;

          /* Iterate through all given file extension types and
           * generate a string list.
           */
          for(i = 0; i < total_types; i++)
          {
            t = type[i];
            if((t != NULL) ? (t->ext == NULL) : TRUE)
                continue;

            /* Generate string for this type. */
            if((t->name != NULL) ?
                (*t->name != '\0') : FALSE
            )
                s = g_strdup_printf(
                  "%s (%s)", t->name, t->ext
                );
            else
                s = STRDUP(t->ext);

            /* Add type string buf to the glist. */
            glist = g_list_append(glist, s);

            /* Record first type which is considered the initially
             * selected type.
             */
            if(i == 0)
            {
                fb_type_struct *t2 = &fb->cur_type;

                first_type_s = s;

                g_free(t2->name);
                t2->name = STRDUP(t->name);
                g_free(t2->ext);
                t2->ext = STRDUP(t->ext);
            }
          }

            /* Set first type text string to combo's entry. */
            if(first_type_s != NULL)
                gtk_entry_set_text(entry, first_type_s);

          /* Set file types string list to file type combo. */
          GUIComboSetList(combo, glist);
          glist = NULL;
          first_type_s = NULL;

          fb->type_change_freeze_count--;
      }

      FileBrowserSetBusy(fb, TRUE);

        /* Map file browser, this needs to be done first to allow the
       * proper realizing of sizes before other things can be updated.
       */
        FileBrowserMap();

      while(gtk_events_pending() > 0)
          gtk_main_iteration();

        /* Set initial path if the path is given, otherwise we keep the
         * original path and leave the list as it was.
         */
        if(path != NULL)
            FileBrowserSetLocation(fb, path);

      /* If no location was set then set the current location to be
       * the home directory.
       */
      if(fb->cur_location == NULL)
          FileBrowserSetLocation(fb, "~");

        FileBrowserSetBusy(fb, FALSE);

        /* Block GUI until response. */
      fb->block_loop_level++;
        gtk_main();

        /* Unmap file browser just in case it was not unmapped from
       * any of the callbacks.
       */
        FileBrowserUnmap();

        /* Break out of an additional blocking loops. */
        while(fb->block_loop_level > 0)
        {
            gtk_main_quit();
            fb->block_loop_level--;
        }
        fb->block_loop_level = 0;


      /* Begin setting returns. */

      /* Response path returns. */
      if(path_rtn != NULL)
          *path_rtn = fb->selected_path;
      if(path_total_rtns != NULL)
          *path_total_rtns = fb->total_selected_paths;

      /* File type. */
      if(type_rtn != NULL)
          *type_rtn = &fb->cur_type;

      return(fb->user_response);
}


/*
 *    Maps the file browser as needed.
 */
void FileBrowserMap(void)
{
        FileBrowser *fb = &file_browser;
      GtkWidget *w = fb->toplevel;
      gtk_widget_show_raise(w);
      fb->map_state = TRUE;
}

/*
 *    Unmaps the file browser as needed.
 */
void FileBrowserUnmap(void)
{
        FileBrowser *fb = &file_browser;
        GtkWidget *w = fb->toplevel;
        gtk_widget_hide(w);
        fb->map_state = FALSE;
}

/*
 *    Deallocates file browser resources.
 */
void FileBrowserShutdown(void)
{
      gint i;
      GdkCursor **cur;
      GtkWidget **w;
        FileBrowser *fb = &file_browser;


      /* Begin deleting values */

      /* Current file type */
        g_free(fb->cur_type.name);
        g_free(fb->cur_type.ext);
        memset(&fb->cur_type, 0x00, sizeof(fb_type_struct));

      /* Selected paths */
      for(i = 0; i < fb->total_selected_paths; i++)
          g_free(fb->selected_path[i]);
      g_free(fb->selected_path);
      fb->selected_path = NULL;
      fb->total_selected_paths = 0;

      /* Selectioned objects list */
      fb->focus_object = -1;
      g_list_free(fb->selection);
      fb->selection = fb->selection_end = NULL;

      /* Objects */
      for(i = 0; i < fb->total_objects; i++)
          FileBrowserObjectDestroyCB(fb->object[i]);
      g_free(fb->object);
      fb->object = NULL;
      fb->total_objects = 0;
      fb->objects_per_row = 0;

      /* Icons */
      for(i = 0; i < fb->total_icons; i++)
          FileBrowserIconDestroyCB(fb->icon[i]);
      g_free(fb->icon);
      fb->icon = NULL;
      fb->total_icons = 0;

      /* List columns */
      FileBrowserListColumnsClear(fb);

      /* Drive paths */
        StringFreeArray(fb->drive_path, fb->total_drive_paths);
      fb->drive_path = NULL;
      fb->total_drive_paths = 0;


        /* Break out of any additional blocking loops. */
        while(fb->block_loop_level > 0)
        {
            gtk_main_quit();
            fb->block_loop_level--;
        }
        fb->block_loop_level = 0;

#define DO_DESTROY_CURSOR     \
{ if(*cur != NULL) { gdk_cursor_destroy(*cur); *cur = NULL; } }
#define DO_DESTROY_WIDGET       \
{ if(*w != NULL) { gtk_widget_destroy(*w); *w = NULL; } }

      /* Begin destroying widgets. */

      PUListDelete(fb->dir_pulist);
      fb->dir_pulist = NULL;

        w = &fb->list_menu;
        DO_DESTROY_WIDGET

      w = &fb->dir_pulist_da;
      DO_DESTROY_WIDGET
      w = &fb->dir_pulist_btn;
        DO_DESTROY_WIDGET

        w = &fb->goto_parent_btn;
      DO_DESTROY_WIDGET
        w = &fb->new_directory_btn;
      DO_DESTROY_WIDGET
      w = &fb->rename_btn;
      DO_DESTROY_WIDGET
      w = &fb->refresh_btn;
        DO_DESTROY_WIDGET

      w = &fb->list_format_standard_tb;
      DO_DESTROY_WIDGET
      w = &fb->list_format_vertical_tb;
      DO_DESTROY_WIDGET
      w = &fb->list_format_vertical_details_tb;
      DO_DESTROY_WIDGET

      w = &fb->list_header_da;
      DO_DESTROY_WIDGET
        w = &fb->list_da;
        DO_DESTROY_WIDGET
        w = &fb->list_vsb;
        DO_DESTROY_WIDGET
        w = &fb->list_hsb;
        DO_DESTROY_WIDGET

      w = &fb->entry;
      DO_DESTROY_WIDGET
      w = &fb->type_combo;
      DO_DESTROY_WIDGET
        w = &fb->ok_btn;
        DO_DESTROY_WIDGET
        w = &fb->cancel_btn;
        DO_DESTROY_WIDGET

        w = &fb->main_vbox;
        DO_DESTROY_WIDGET
        w = &fb->toplevel;
        DO_DESTROY_WIDGET

      if(fb->list_pm != NULL)
      {
          gdk_pixmap_unref(fb->list_pm);
          fb->list_pm = NULL;
      }

      cur = &fb->cur_busy;
      DO_DESTROY_CURSOR
      cur = &fb->cur_column_hresize;
      DO_DESTROY_CURSOR

      if(fb->accelgrp != NULL)
      {
          gtk_accel_group_unref(fb->accelgrp);
          fb->accelgrp = NULL;
      }

#undef DO_DESTROY_WIDGET
#undef DO_DESTROY_CURSOR

      /* Delete other things here */
      g_free(fb->home_path);
      fb->home_path = NULL;

      g_free(fb->cur_location);
      fb->cur_location = NULL;

      /* Clear file browser structure. */
      memset(fb, 0x00, sizeof(FileBrowser));
}


/*
 *    Convience function to allocate a new file browser file extension
 *    type structure and append it to the given list. The given list
 *    will be modified and the index number of the newly allocated
 *    structure will be returned.
 *
 *    Can return -1 on error.
 */
gint FileBrowserTypeListNew(
      fb_type_struct ***list, gint *total,
        const gchar *ext,     /* Space separated list of extensions. */
        const gchar *name     /* Descriptive name. */
)
{
      gint n;
      fb_type_struct *fb_type_ptr;


      if((list == NULL) || (total == NULL))
          return(-1);

      /* Increase total. */
      if(*total < 0)
          *total = 0;
      n = *total;
      *total = n + 1;

      /* Allocate more pointers. */
      *list = (fb_type_struct **)g_realloc(
          *list,
          (*total) * sizeof(fb_type_struct *)
      );
      if(*list == NULL)
      {
          *total = 0;
          return(-1);
      }

      /* Allocate new structure. */
      fb_type_ptr = (fb_type_struct *)g_malloc0(sizeof(fb_type_struct));
      (*list)[n] = fb_type_ptr;
      if(fb_type_ptr == NULL)
      {
          *total = n;
          return(-1);
      }

      /* Set values. */
      if(ext != NULL)
          fb_type_ptr->ext = g_strdup(ext);
      if(name != NULL)
          fb_type_ptr->name = g_strdup(name);

      return(n);
}

/*
 *    Deletes a dynamically allocated file browser file extensions
 *    type list.
 */
void FileBrowserDeleteTypeList(
        fb_type_struct **t, gint total 
)
{
      gint i;
      fb_type_struct *t_ptr;

      for(i = 0; i < total; i++)
      {
          t_ptr = t[i];
          if(t_ptr == NULL)
            continue;

          g_free(t_ptr->ext);
          g_free(t_ptr->name);
          g_free(t_ptr);
      }
      g_free(t);
}

Generated by  Doxygen 1.6.0   Back to index