/************************************************************************
 *
 * All dialogs opened are created and used modal.
 *
 ************************************************************************
 * (C) Craig Drummond, 2006
 ************************************************************************
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 ************************************************************************/

/*
  NOTES:

   1. Inkscape does not use the standard Gtk fileters to determine Save type
      ...it uses an extra combo, which we try to locate.
   2. Firefox. This has two filters with the same patterns - *.htm and *.html. We
      modify this so that one is *.htm and the other is *.html
   3. Glade-2 I noticed this crash a couple of times on loading - but not always.
      Not sure if this is a Glade problem or not...
*/

/*
TODO
  abiword: seems to call gtk_widget_show - but just overriding this cuases the dialog to
           appear twice, and then it still donest use result :-(
  Overload font picker!
  Overload normal old file selector? ie. in addtition to file chooser?
  Message boxes: Auto set alternative button order?
*/

/*
#define KGTK_DEBUG
*/

#define _GNU_SOURCE
#include <dlfcn.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <gdk/gdkx.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdarg.h>
#include "connect.h"
#include "config.h"
#include "ctype.h"
#include "stdbool.h"

#define BLACKLIST_UNKNOWN_GTK_APPS 0

/*
#define KGTK_DEBUG_DLSYM
*/

/*
 * For SWT apps (e.g. eclipse) we need to override dlsym.
 */
#ifdef HAVE_DL_SYM
extern void *_dl_sym(void *, const char *, void *);
#endif
static void * real_dlsym (void *handle, const char *name);

typedef enum
{
    APP_UNKNOWN,
    APP_GIMP,
    APP_INKSCAPE,
    APP_FIREFOX,
    APP_KINO,
    APP_BLACKLISTED
} Application;

static const char  *kgtkAppName=NULL;
static gboolean    useKde=FALSE;
static gboolean    kdialogdError=FALSE;
static GMainLoop   *kdialogdLoop=NULL;
static gchar       *kgtkFileFilter=NULL;
static Application kgtkApp=APP_UNKNOWN;

#define MAX_DATA_LEN 4096
#define MAX_FILTER_LEN 256
#define MAX_LINE_LEN 1024
#define MAX_APP_NAME_LEN 32

static char * kgtk_get_app_name(int pid)
{
    static char app_name[MAX_APP_NAME_LEN+1]="\0";

    int  procFile=-1;
    char cmdline[MAX_LINE_LEN+1];

    sprintf(cmdline, "/proc/%d/cmdline",pid);

    if(-1!=(procFile=open(cmdline, O_RDONLY)))
    {
        if(read(procFile, cmdline, MAX_LINE_LEN)>2)
        {
            int len=strlen(cmdline),
                pos=0;

            for(pos=len-1; pos>0 && cmdline[pos] && cmdline[pos]!='/'; --pos)
                ;

            if(pos>=0 && pos<len)
            {
                strncpy(app_name, &cmdline[pos ? pos+1 : 0], MAX_APP_NAME_LEN);
                app_name[MAX_APP_NAME_LEN]='\0';
            }
        }
        close(procFile);
    }

    return app_name;
}

/* Try to get name of application executable - either from argv[0], or /proc */
static const gchar *getAppName(const gchar *app)
{
    static const char *appName=NULL;

    if(!appName)
    {
        /* Is there an app name set?  - if not read from /proc */
        const gchar *a=app ? app : kgtk_get_app_name(getpid());
        gchar       *slash;

        /* Was the cmdline app java? if so, try to use its parent name - just in case
           its run from a shell script, etc. - e.g. as eclipse does */
        if(a && 0==strcmp(a, "java"))
            a=kgtk_get_app_name(getppid());

        if(a && a[0]=='\0')
            a=NULL;

        appName=a && (slash=strrchr(a, '/')) && '\0'!=slash[1]
                    ? &(slash[1])
                    : a ? a : "GtkApp";
    }

#ifdef KGTK_DEBUG
    printf("KGTK::getAppName: %s\n", appName);
#endif
    return appName;
}

static gboolean writeString(const char *s)
{
    unsigned int slen=strlen(s)+1;

    return writeBlock(kdialogdSocket, (char *)&slen, 4) &&
           writeBlock(kdialogdSocket, s, slen);
}

static gboolean writeBool(gboolean b)
{
    char bv=b ? 1 : 0;

    return writeBlock(kdialogdSocket, (char *)&bv, 1);
}

typedef struct
{
    GSList *res;
    gchar  *selFilter;
} KGtkData;

static gpointer kdialogdMain(gpointer data)
{
    KGtkData *d=(KGtkData*)data;
    char     buffer[MAX_DATA_LEN+1]={'\0'};
    int      num=0;

    if(readBlock(kdialogdSocket, (char *)&num, 4))
    {
        int n;

        for(n=0; n<num && !kdialogdError; ++n)
        {
            int size=0;

            if(readBlock(kdialogdSocket, (char *)&size, 4))
            {
                if(size>0)
                {
                    if(size<=MAX_DATA_LEN && readBlock(kdialogdSocket, buffer, size))
                    {
                        /*buffer[size-1]='\0'; */
                        if('/'==buffer[0])
                            d->res=g_slist_prepend(d->res, g_filename_from_utf8(buffer, -1, NULL, NULL, NULL));
                        else if(!(d->selFilter))
                            d->selFilter=g_strdup(buffer);
                    }
                    else
                        kdialogdError=TRUE;
                }
            }
            else
                kdialogdError=TRUE;
        }
    }
    else
        kdialogdError=TRUE;

    if(g_main_loop_is_running(kdialogdLoop))
        g_main_loop_quit(kdialogdLoop);

    return 0L;
}

static gboolean sendMessage(GtkWidget *widget, Operation op, GSList **res, gchar **selFilter,
                            const char *title, const char *p1, const char *p2, gboolean overWrite)
{
#ifdef KGTK_DEBUG
    printf("KGTK::sendMessage\n");
#endif

    if(connectToKDialogD(getAppName(kgtkAppName)))
    {
        char o=(char)op;
        int  xid=0;

        if(widget)
        {
            if(widget->parent)
            {
#ifdef KGTK_DEBUG
                printf("KGTK::Dialog has a parent!\n");
#endif
                xid=GDK_WINDOW_XID(gtk_widget_get_toplevel(widget->parent)->window);
            }

            /*
                Inkscape's 0.44 export bitmap filechooser is set to be transient for the main window, and
                not the export dialog. This makes the KDE dialog appear underneath it! So, for inkscape
                dont try to get the window transient for...

                ...might need to remove this whole section, if it starts to affect too many apps
            */
            if(!xid && APP_INKSCAPE!=kgtkApp && GTK_IS_WINDOW(widget))
            {
                GtkWindow *win=gtk_window_get_transient_for(GTK_WINDOW(widget));

#ifdef KGTK_DEBUG
                printf("KGTK::Get window transient for...\n");
#endif
                if(win && win->focus_widget)
                    xid=GDK_WINDOW_XID(gtk_widget_get_toplevel(win->focus_widget)->window);
            }
        }

        if(!xid)
        {
            GList *topWindows,
                  *node;
            int   prevX=0;

#ifdef KGTK_DEBUG
            printf("KGTK::No xid, need to traverse window list...\n");
#endif
            for(topWindows=node=gtk_window_list_toplevels(); node; node = node->next)
            {
                GtkWidget *w=node->data;

                if(w && GTK_IS_WIDGET(w) && w->window)
                {
                   if(gtk_window_has_toplevel_focus(GTK_WINDOW(w)) &&
                      gtk_window_is_active(GTK_WINDOW(w)))
                    {
                        /* If the currently active window is a popup - then assume it is a popup-menu,
                         * so use the previous window as the one to be transient for...*/
                        if(GTK_WINDOW_POPUP==GTK_WINDOW(w)->type && prevX)
                            xid=prevX;
                        else
                            xid=GDK_WINDOW_XID(w->window);
                        if(xid)
                            break;
                    }
                    else
                    {
                      prevX=GDK_WINDOW_XID(w->window);
                    }
                }
            }
            g_list_free(topWindows);
        }

        if(writeBlock(kdialogdSocket, &o, 1) &&
           writeBlock(kdialogdSocket, (char *)&xid, 4) &&
           writeString(title) &&
           (p1 ? writeString(p1) : true) &&
           (p2 ? writeString(p2) : true) &&
           (OP_FILE_SAVE==op ? writeBool(overWrite) : true))
        {
            GtkWidget *dlg=gtk_dialog_new();
            KGtkData  d;

            gtk_widget_set_name(dlg, "--kgtk-modal-dialog-hack--");
            d.res=NULL;
            d.selFilter=NULL;

            /* Create a tmporary, hidden, dialog so that the kde dialog appears as modal */
            g_object_ref(dlg);
            gtk_window_set_modal(GTK_WINDOW(dlg), TRUE);
            gtk_window_iconify(GTK_WINDOW(dlg));
            gtk_dialog_set_has_separator(GTK_DIALOG(dlg), FALSE);
            gtk_window_set_has_frame(GTK_WINDOW(dlg), FALSE);
            gtk_window_set_decorated(GTK_WINDOW(dlg), FALSE);
            gtk_window_set_keep_below(GTK_WINDOW(dlg), TRUE);
            gtk_window_set_opacity(GTK_WINDOW(dlg), 100);
            gtk_window_set_type_hint(GTK_WINDOW(dlg), GDK_WINDOW_TYPE_HINT_DOCK);
            gtk_widget_show(dlg);
            gtk_window_move(GTK_WINDOW(dlg), 32768, 32768);
            gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dlg), TRUE);
            gtk_window_set_skip_pager_hint(GTK_WINDOW(dlg), TRUE);
            kdialogdLoop = g_main_loop_new (NULL, FALSE);
            kdialogdError=FALSE;
            g_thread_new(NULL, &kdialogdMain, &d);

            GDK_THREADS_LEAVE();
            g_main_loop_run(kdialogdLoop);
            GDK_THREADS_ENTER();
            g_main_loop_unref(kdialogdLoop);
            kdialogdLoop = NULL;
            gtk_window_set_modal(GTK_WINDOW(dlg), FALSE);
            g_object_unref(dlg);
            gtk_widget_destroy(dlg);
            if(kdialogdError)
            {
                closeConnection();
                return FALSE;
            }
            if(d.res)
            {
                if(res)
                    *res=d.res;
                else
                    g_slist_free(d.res);
            }
            if(d.selFilter)
            {
                if(selFilter)
                    *selFilter=d.selFilter;
                else
                    g_free(d.selFilter);
            }
            return TRUE;
        }
    }

    return FALSE;
}

static gchar * firstEntry(GSList *files)
{
    gchar *file=NULL;

    if(files)
    {
        file=(gchar *)(files->data);
        files=g_slist_delete_link(files, files);
        if(files)
        {
            g_slist_foreach(files, (GFunc)g_free, NULL);
            g_slist_free(files);
            files=NULL;
        }
    }

    return file;
}

static const char * getTitle(const char *title, Operation op)
{
    if(title && strlen(title))
        return title;

    return ".";
}

static gboolean openKdeDialog(GtkWidget *widget, const char *title, const char *p1, const char *p2,
                              Operation op, GSList **res, gchar **selFilter, gboolean overWrite)
{
    gboolean rv=sendMessage(widget, op, res, selFilter, getTitle(title, op), p1, p2, overWrite);

     /* If we failed to talk to, or start kdialogd, then dont keep trying - just fall back to Gtk */
/*
     if(!rv) 
         useKde=FALSE;
*/

    return rv;
}

static void kgtkExit()
{
    if(useKde)
        closeConnection();
}

static gboolean isApp(const char *str, const char *app)
{
    /* Standard case... */
    if(0==strcmp(str, app))
        return TRUE;

    /* Autopackage'd app */
    #define AUTOPACKAGE_PROXY     ".proxy."
    #define AUTOPACKAGE_PROXY_LEN 7

    if(str==strstr(str, ".proxy.") && strlen(str)>AUTOPACKAGE_PROXY_LEN &&
       0==strcmp(&str[AUTOPACKAGE_PROXY_LEN], app))
        return TRUE;

    /* gimp and mozilla */
    {
    int app_len=strlen(app);

    if(strlen(str)>app_len && str==strstr(str, app) &&
       (0==memcmp(&str[app_len], "-2", 2) ||
        0==memcmp(&str[app_len], "-bin", 4)))
        return TRUE;
    }

    return FALSE;
}

static gboolean isMozApp(const char *app, const char *check)
{
    if(0==strcmp(app, check))
        return TRUE;
    else if(app==strstr(app, check))
    {
        int app_len=strlen(app),
            check_len=strlen(check);

        if(check_len+4 == app_len && 0==strcmp(&app[check_len], "-bin"))
            return TRUE;

        /* OK check for xulrunner-1.9 */
        {
        double dummy;
        if(app_len>(check_len+1) && 1==sscanf(&app[check_len+1], "%lf", &dummy))
            return TRUE;
        }
    }

    return FALSE;
}

static void determineAppName()
{
            const gchar *prg=getAppName(NULL);

            if(prg)
            {
#ifdef KGTK_DEBUG
                printf("KGTK::APP %s\n", prg);
#endif
                if(isApp(prg, "inkscape"))
                {
                    kgtkFileFilter="*.svg|Scalable Vector Graphic";
                    kgtkApp=APP_INKSCAPE;
#ifdef KGTK_DEBUG
                    printf("KGTK::Inkscape\n");
#endif
                }
                else if(isApp(prg, "gimp"))
                {
                    kgtkApp=APP_GIMP;
#ifdef KGTK_DEBUG
                    printf("KGTK::GIMP\n");
#endif
                }
                else if(isApp(prg, "kino"))
                {
                    kgtkApp=APP_KINO;
#ifdef KGTK_DEBUG
                    printf("KGTK::kino\n");
#endif
                }
                else if(isMozApp(prg, "firefox") || isMozApp(prg, "swiftfox") || isMozApp(prg, "iceweasel") || isMozApp(prg, "xulrunner"))
                {
                    kgtkApp=APP_FIREFOX;
#ifdef KGTK_DEBUG
                    printf("KGTK::Firefox\n");
#endif
                }
                //===========================================================================
                // BLACKLISTED APPS FOLLOW
                // These applications crash regularly under kgtk-qt3 for unknown reasons,
                // although bad programming within the blacklisted application is a good bet!
                //===========================================================================
                else if(isApp(prg, "audacity") || isApp(prg, "gnumeric"))
                {
                    kgtkApp=APP_BLACKLISTED;
                }
                else
                {
                    kgtkApp=APP_UNKNOWN;
                }
            }
}

static gboolean kgtkInit(const char *appName)
{
    static gboolean initialised=FALSE;
#ifdef KGTK_DEBUG
    printf("KGTK::kgtkInit %s\n", appName);
#endif
    if(!initialised)
    {
#ifdef KGTK_DEBUG
        printf("KGTK::Running under KDE? %d\n", NULL!=getenv("TDE_FULL_SESSION"));
#endif

        initialised=TRUE;
        kgtkAppName=getAppName(appName);
        useKde=NULL!=getenv("TDE_FULL_SESSION") && connectToKDialogD(kgtkAppName);
        if(useKde)
        {
            determineAppName();
            if (((kgtkApp != APP_UNKNOWN) || (!BLACKLIST_UNKNOWN_GTK_APPS)) && (kgtkApp != APP_BLACKLISTED)) {
                atexit(&kgtkExit);
            }
        }
    }

#ifdef KGTK_DEBUG
    printf("KGTK::kgtkInit useKde:%d\n", (useKde && (((kgtkApp != APP_UNKNOWN) || (!BLACKLIST_UNKNOWN_GTK_APPS))) && (kgtkApp != APP_BLACKLISTED)));
#endif

    return (useKde && (((kgtkApp != APP_UNKNOWN) || (!BLACKLIST_UNKNOWN_GTK_APPS)) && (kgtkApp != APP_BLACKLISTED)));
}

/* ......................... */

typedef struct _GtkFileSystem      GtkFileSystem;
typedef struct _GtkFilePath        GtkFilePath;
typedef struct _GtkFileSystemModel GtkFileSystemModel;
struct _GtkFileFilter
{
  GtkObject parent_instance;

  gchar *name;
  GSList *rules;

  GtkFileFilterFlags needed;
};

typedef enum {
  FILTER_RULE_PATTERN,
  FILTER_RULE_MIME_TYPE,
  FILTER_RULE_PIXBUF_FORMATS,
  FILTER_RULE_CUSTOM
} FilterRuleType;

struct _FilterRule
{
  FilterRuleType type;
  GtkFileFilterFlags needed;

  union {
    gchar *pattern;
    gchar *mime_type;
    GSList *pixbuf_formats;
    struct {
      GtkFileFilterFunc func;
      gpointer data;
      GDestroyNotify notify;
    } custom;
  } u;
};

#if GTK_CHECK_VERSION(2, 6, 0)
struct _GtkFileChooserButtonPrivate
{
  GtkWidget *dialog;
  GtkWidget *button;
  GtkWidget *image;
  GtkWidget *label;
  GtkWidget *combo_box;

  GtkCellRenderer *icon_cell;
  GtkCellRenderer *name_cell;

  GtkTreeModel *model;
  GtkTreeModel *filter_model;

  gchar *backend;
  GtkFileSystem *fs;
  GtkFilePath *old_path;

  gulong combo_box_changed_id;
  gulong dialog_file_activated_id;
  gulong dialog_folder_changed_id;
  gulong dialog_selection_changed_id;
  gulong fs_volumes_changed_id;
  gulong fs_bookmarks_changed_id;
};
#endif

/* TreeModel Columns */
enum
{
  ICON_COLUMN,
  DISPLAY_NAME_COLUMN,
  TYPE_COLUMN,
  DATA_COLUMN,
  NUM_COLUMNS
};

/* TreeModel Row Types */
typedef enum
{
  ROW_TYPE_SPECIAL,
  ROW_TYPE_VOLUME,
  ROW_TYPE_SHORTCUT,
  ROW_TYPE_BOOKMARK_SEPARATOR,
  ROW_TYPE_BOOKMARK,
  ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
  ROW_TYPE_CURRENT_FOLDER,
  ROW_TYPE_OTHER_SEPARATOR,
  ROW_TYPE_OTHER,

  ROW_TYPE_INVALID = -1
}
RowType;

static GtkWidget *
kgtk_file_chooser_dialog_new_valist (const gchar          *title,
                                    GtkWindow            *parent,
                                    GtkFileChooserAction  action,
                                    const gchar          *backend,
                                    const gchar          *first_button_text,
                                    va_list               varargs)
{
  GtkWidget *result;
  const char *button_text = first_button_text;
  gint response_id;

  result = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
                         "title", title,
                         "action", action,
                         "file-system-backend", backend,
                         NULL);

  if (parent)
    gtk_window_set_transient_for (GTK_WINDOW (result), parent);

  while (button_text)
    {
      response_id = va_arg (varargs, gint);
      gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id);
      button_text = va_arg (varargs, const gchar *);
    }

  return result;
}

void
kgtk_dialog_add_buttons_valist (    GtkDialog            *dialog,
                                    const gchar          *first_button_text,
                                    va_list               varargs)
{
  const char *button_text = first_button_text;
  gint response_id;

  while (button_text)
    {
      response_id = va_arg (varargs, gint);
      gtk_dialog_add_button (GTK_DIALOG (dialog), button_text, response_id);
      button_text = va_arg (varargs, const gchar *);
    }
}
/* ......................... */

gboolean gtk_init_check(int *argc, char ***argv)
{
    static gboolean (*realFunction)(int*, char***) = NULL;

    gboolean rv=FALSE;

    if(!realFunction)
        realFunction = (gboolean (*)(int*, char***)) real_dlsym(RTLD_NEXT, "gtk_init_check");

    rv=realFunction(argc, argv);
#ifdef KGTK_DEBUG
    printf("KGTK::gtk_init_check\n");
#endif
    if(rv)
        kgtkInit(argv && argc ? (*argv)[0] : NULL);
    return rv;
}

void gtk_init(int *argc, char ***argv)
{
    static void * (*realFunction)(int*, char***) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(int*, char***)) real_dlsym(RTLD_NEXT, "gtk_init");

    realFunction(argc, argv);
#ifdef KGTK_DEBUG
    printf("KGTK::gtk_init\n");
#endif
    kgtkInit(argv && argc ? (*argv)[0] : NULL);
}

/* Store a hash from widget pointer to folder/file list retried from KDialogD */
static GHashTable *fileDialogHash=NULL;

typedef struct
{
    gchar    *folder;
    gchar    *name;
    GSList   *files;
    int      ok,
             cancel;
    gboolean setOverWrite,
             doOverwrite;
} KGtkFileData;

static KGtkFileData * lookupHash(void *hash, gboolean create)
{
    KGtkFileData *rv=NULL;

#ifdef KGTK_DEBUG
    printf("KGTK::lookupHash %X\n", (int)hash);
#endif
    if(!fileDialogHash)
        fileDialogHash=g_hash_table_new(g_int_hash, g_int_equal);

    rv=(KGtkFileData *)g_hash_table_lookup(fileDialogHash, hash);

    if(!rv && create)
    {
        rv=(KGtkFileData *)malloc(sizeof(KGtkFileData));
        rv->folder=NULL;
        rv->files=NULL;
        rv->name=NULL;
        rv->ok=GTK_RESPONSE_OK;
        rv->cancel=GTK_RESPONSE_CANCEL;
        rv->setOverWrite=FALSE;
        rv->doOverwrite=FALSE;
        g_hash_table_insert(fileDialogHash, hash, rv);
        rv=g_hash_table_lookup(fileDialogHash, hash);
    }

    return rv;
}

static void freeHash(void *hash)
{
    KGtkFileData *data=NULL;

    if(!fileDialogHash)
        fileDialogHash=g_hash_table_new(g_int_hash, g_int_equal);

    data=(KGtkFileData *)g_hash_table_lookup(fileDialogHash, hash);

    if(data)
    {
        if(data->folder)
            g_free(data->folder);
        if(data->name)
            g_free(data->name);
        if(data->files)
        {
            g_slist_foreach(data->files, (GFunc)g_free, NULL);
            g_slist_free(data->files);
        }
        data->files=NULL;
        data->folder=NULL;
        data->name=NULL;
        g_hash_table_remove(fileDialogHash, hash);
    }
}

/* Some Gtk apps have filter pattern *.[Pp][Nn][Gg] - wherease TQt/KDE prefer *.png */
#define MAX_PATTERN_LEN 64
static gchar *modifyFilter(const char *filter)
{
    int         i=0;
    gboolean    brackets=FALSE;
    const char  *p;
    static char res[MAX_PATTERN_LEN+1];

    for(p=filter; p && *p && i<MAX_PATTERN_LEN; ++p)
        switch(*p)
        {
            case '[':
                p++;
                if(p)
                    res[i++]=tolower(*p);
                brackets=TRUE;
                break;
            case ']':
                brackets=FALSE;
                break;
            default:
                if(!brackets)
                    res[i++]=tolower(*p);
        }
    res[i++]='\0';
    return res;
}

static GtkWidget * getCombo(GtkWidget *widget)
{
    if(widget && GTK_IS_BOX(widget))
    {
        GList *child=GTK_BOX(widget)->children;

        for(; child; child=child->next)
        {
            GtkBoxChild *boxChild=(GtkBoxChild *)child->data;

            if(GTK_IS_COMBO_BOX(boxChild->widget))
                return boxChild->widget;
            else if(GTK_IS_BOX(boxChild->widget))
            {
                GtkWidget *box=getCombo(boxChild->widget);

                if(box)
                    return box;
            }
        }

    }

    return NULL;
}

static GString * getFilters(GtkDialog *dialog, GtkFileChooserAction act)
{
    GString *filter=NULL;
    GSList  *list=gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(dialog));

#ifdef KGTK_DEBUG
    printf("KGTK::Get list of filters...\n");
#endif

    if(list)
    {
        GSList *item;
        int    filterNum=0;

        filter=g_string_new("");
        for(item=list; item; item=g_slist_next(item), ++filterNum)
        {
            GtkFileFilter *f=(GtkFileFilter *)(item->data);

            if(f)
            {
                const gchar *name=gtk_file_filter_get_name(f);
                GSList      *rule=((struct _GtkFileFilter *)f)->rules;
                GString     *pattern=g_string_new("");

                for(; rule; rule=g_slist_next(rule))
                    switch(((struct _FilterRule *)rule->data)->type)
                    {
                        case FILTER_RULE_PATTERN:
                        {
                            const char *modPat=
                                         modifyFilter(((struct _FilterRule *)rule->data)->u.pattern);

                            /*
                               Firefox has:
                                  *.htm *.html | Web page complete
                                  *.htm *.html | HTML only
                                  *.txt *.text | Text

                               We modify this to have:
                                  *.htm        | Web page complete
                                  *.html       | HTML only
                                  *.txt *.text | Text
                            */

                            if(APP_FIREFOX!=kgtkApp || (strcmp(modPat, "*.html") ? filterNum!=1
                                                                                 : filterNum))
                            {
                                if(pattern->len)
                                    pattern=g_string_append(pattern, " ");
                                pattern=g_string_append(pattern, modPat);
                            }
                            break;
                        }
                        case FILTER_RULE_MIME_TYPE:
                            if(filter->len)
                                filter=g_string_append(filter, "\n");
                            filter=g_string_append(filter,
                                                   ((struct _FilterRule *)rule->data)->u.mime_type);
                            break;
                        default:
                            break;
                    }

                if(name && pattern && pattern->len)
                {
                    gchar *n=g_strdup(name),
                          *pat=strstr(n, " (*");

                    if(pat)
                        *pat='\0';

                    if(filter->len)
                        filter=g_string_append(filter, "\n");
                    filter=g_string_append(filter, pattern->str);
                    filter=g_string_append(filter, "|");
                    filter=g_string_append(filter, n);
                    g_free(n);
                }
                g_string_free(pattern, TRUE);
            }
        }
        g_slist_free(list);
    }

    if(!filter)
    {
        /* This is mainly the case for Inkscape save - but try for other apps too... */
        GtkWidget *combo=getCombo(gtk_file_chooser_get_extra_widget(GTK_FILE_CHOOSER(dialog)));

#ifdef KGTK_DEBUG
        printf("KGTK::No filters found, try to look for an extra combo widget...\n");
#endif

        if(combo)
        {
            int i;

            filter=g_string_new("");
            for(i=0; i<64; ++i)
            {
                gchar *text=NULL;

                gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
                if(i!=gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
                    break;

                text=gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));

                if(text)
                {
                    gchar *pat=strstr(text, " (*");

                    if(pat)
                    {
                        gchar *close=strstr(pat, ")");

                        *pat='\0';

                        if(close)
                            *close='\0';

                        pat+=2; /* Skip past " (" */

                        if(filter->len)
                            filter=g_string_append(filter, "\n");
                        filter=g_string_append(filter, pat);
                        filter=g_string_append(filter, "|");
                        filter=g_string_append(filter, text);
                    }
                    g_free(text);
                }
            }
        }
    }

    return filter;
}

static void setFilter(const gchar *filter, GtkDialog *dialog, GtkFileChooserAction act)
{
    gboolean found=FALSE;
    GSList   *list=gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(dialog));

#ifdef KGTK_DEBUG
    printf("KGTK::Need to locate filter:%s\n", filter ? filter : "Null");
#endif

    if(list)
    {
        GSList       *item;
        unsigned int flen=strlen(filter);
        int          filterNum=0;

        for(item=list; item && !found; item=g_slist_next(item), filterNum++)
        {
            GtkFileFilter *f=(GtkFileFilter *)(item->data);

            if(f)
            {
                GSList *rule=((struct _GtkFileFilter *)f)->rules;
                char   *start=NULL;

                for(; rule && !found; rule=g_slist_next(rule))
                    if(FILTER_RULE_PATTERN==((struct _FilterRule *)rule->data)->type)
                    {
                        char *filt=modifyFilter(((struct _FilterRule *)rule->data)->u.pattern);

                        /*
                           Firefox has:
                              *.htm *.html | Web page complete
                              *.htm *.html | HTML only
                              *.txt *.text | Text

                           We modify this to have:
                              *.htm        | Web page complete
                              *.html       | HTML only
                              *.txt *.text | Text
                        */

                        if((APP_FIREFOX!=kgtkApp || (strcmp(filt, "*.html") ? filterNum!=1
                                                                            : filterNum)) &&
                           (start=strstr(filter, filt)))
                        {
                            unsigned int slen=strlen(filt);

                            if(((start-filter)+slen)<=flen &&
                                (' '==start[slen] || '\t'==start[slen] ||
                                '\n'==start[slen] || '\0'==start[slen]))
                            {
#ifdef KGTK_DEBUG
                                printf("KGTK::FOUND FILTER\n");
#endif
                                found=TRUE;
                                gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), f);
                            }
                        }
                    }
            }
        }
        g_slist_free(list);
    }

    if(!found)
    {
        /* This is mainly the case for Inkscape save - but try for other apps too... */
        GtkWidget *combo=getCombo(gtk_file_chooser_get_extra_widget(GTK_FILE_CHOOSER(dialog)));

#ifdef KGTK_DEBUG
        printf("KGTK::No filters found, try to look for an extra combo widget...\n");
#endif
        if(combo)
        {
            int i,
                flen=strlen(filter);

            for(i=0; i<64; ++i)
            {
                gchar *text=NULL;

                gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
                if(i!=gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
                    break;

                text=gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));

                if(text)
                {
                    gchar *pat=strstr(text, filter);

                    if(pat)
                    {
                        if(pat>text && (' '==pat[-1] || '('==pat[-1]) &&
                           (' '==pat[flen] || ')'==pat[flen]))
                            return; /* found a match, so just return - filter is set */
                    }
                    g_free(text);
                }
            }

            /* No match :-( set to last filter... */
            for(i=0; i<64; ++i)
            {
                gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
                if(i!=gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
                    break;
            }
        }
    }
}

static GSList * addProtocols(GSList *files)
{
    GSList *item=files;

    for(; item; item=g_slist_next(item))
    {
        gchar *cur=item->data;
        item->data=g_filename_to_uri(item->data, NULL, NULL);
        g_free(cur);
    }

    return files;
}

void gtk_window_present(GtkWindow *window)
{
    static void * (*realFunction)(GtkWindow*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkWindow*)) real_dlsym(RTLD_NEXT, "gtk_window_present");

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_window_present %s %d\n", gtk_type_name(GTK_WIDGET_TYPE(window)),
           GTK_IS_FILE_CHOOSER(window));
#endif
    if(GTK_IS_FILE_CHOOSER(window)) /* || (APP_GIMP==kgtkApp &&
       0==strcmp(gtk_type_name(GTK_WIDGET_TYPE(window)), "GimpFileDialog")))*/
        gtk_dialog_run(GTK_DIALOG(window));
    else
        realFunction(window);
}

void gtk_widget_show(GtkWidget *widget)
{
    static void * (*realFunction)(GtkWidget*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkWidget*)) real_dlsym(RTLD_NEXT, "gtk_widget_show");

    if(widget && !GTK_IS_FILE_CHOOSER_BUTTON(widget) && GTK_IS_FILE_CHOOSER(widget))
    {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_widget_show %s %d\n", gtk_type_name(GTK_WIDGET_TYPE(widget)),
               GTK_IS_FILE_CHOOSER(widget));
#endif
        gtk_dialog_run(GTK_DIALOG(widget));
        GTK_OBJECT_FLAGS(widget)|=GTK_REALIZED;
    }
    else
        realFunction(widget);
}

void gtk_widget_hide(GtkWidget *widget)
{
    static void * (*realFunction)(GtkWidget*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkWidget*)) real_dlsym(RTLD_NEXT, "gtk_widget_hide");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_widget_hide Bypassing internal function\n"); fflush(stdout);
#endif
        realFunction(widget);
        return;
    }

    if(widget && !GTK_IS_FILE_CHOOSER_BUTTON(widget) && GTK_IS_FILE_CHOOSER(widget))
    {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_widget_hide %s %d\n", gtk_type_name(GTK_WIDGET_TYPE(widget)),
               GTK_IS_FILE_CHOOSER(widget));
#endif
        if(GTK_OBJECT_FLAGS(widget)&GTK_REALIZED)
            GTK_OBJECT_FLAGS(widget)-=GTK_REALIZED;
    }
    else
        realFunction(widget);
}

gboolean gtk_file_chooser_get_do_overwrite_confirmation(GtkFileChooser *widget)
{
    static gboolean (*realFunction)(GtkFileChooser*) = NULL;

    gboolean rv=FALSE;

    if(!realFunction)
        realFunction = (gboolean (*)(GtkFileChooser*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_get_do_overwrite_confirmation");

    if(realFunction)
    {
        KGtkFileData *data=lookupHash(widget, FALSE);

        if(data)
        {
            if(!data->setOverWrite)
            {
                data->setOverWrite=TRUE;
                data->doOverwrite=realFunction(widget);
            }
            rv=data->doOverwrite;
        }
        else
            rv=realFunction(widget);
    }

    return rv;
}

/* ext => called from app, not kgtk */
void kgtkFileChooserSetDoOverwriteConfirmation(GtkFileChooser *widget, gboolean v, gboolean ext)
{
    static void * (*realFunction)(GtkFileChooser*, gboolean) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkFileChooser*, gboolean)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_set_do_overwrite_confirmation");

    if(realFunction)
    {
        realFunction(widget, v);
        if(ext)
        {
            KGtkFileData *data=lookupHash(widget, FALSE);

            if(data)
            {
                data->setOverWrite=TRUE;
                data->doOverwrite=v;
            }
        }
    }
}

gboolean isOnFileChooser(GtkWidget *w)
{
    return  w
                ? GTK_IS_FILE_CHOOSER(w)
                    ? TRUE
                    : isOnFileChooser(w->parent)
                : FALSE;
}

int gtk_combo_box_get_active(GtkComboBox *combo)
{
    int rv=0;

    if(APP_KINO==kgtkApp && isOnFileChooser((GtkWidget*)combo))
        return 1;
    else
    {
        static gint (*realFunction)(GtkComboBox*) = NULL;

        if(!realFunction)
            realFunction = (gint (*)(GtkComboBox*)) real_dlsym(RTLD_NEXT, "gtk_combo_box_get_active");

        rv=realFunction(combo);
    }

    return rv;
}

gint gtk_dialog_run(GtkDialog *dialog)
{
    static gint (*realFunction)(GtkDialog*) = NULL;

    if(!realFunction)
        realFunction = (gint (*)(GtkDialog*)) real_dlsym(RTLD_NEXT, "gtk_dialog_run");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_dialog_run Bypassing internal function\n"); fflush(stdout);
#endif
        return realFunction(dialog);
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_dialog_run %s \n", dialog ? gtk_type_name(GTK_WIDGET_TYPE(dialog)) : "<null>");
#endif

    if(kgtkInit(NULL) && GTK_IS_FILE_CHOOSER(dialog))
    {
        static gboolean running=FALSE;

        KGtkFileData *data=lookupHash(dialog, TRUE);

#ifdef KGTK_DEBUG
        printf("KGTK::run file chooser, already running? %d\n", running);
#endif
        if(!running)
        {
            GtkFileChooserAction act=gtk_file_chooser_get_action(GTK_FILE_CHOOSER(dialog));
            gchar                *current=NULL,
                                 *selFilter=NULL;
            const gchar          *title=gtk_window_get_title(GTK_WINDOW(dialog));
            GString              *filter=NULL;
            gint                 resp=data->cancel;
            gboolean             origOverwrite=
                                     gtk_file_chooser_get_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog));

            running=TRUE;

            if(GTK_FILE_CHOOSER_ACTION_OPEN==act || GTK_FILE_CHOOSER_ACTION_SAVE==act)
                filter=getFilters(dialog, act);
            else /* GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER==act ||
                 GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER==act */
                if(NULL==(current=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog))))
                    current=gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));

            kgtkFileChooserSetDoOverwriteConfirmation(GTK_FILE_CHOOSER(dialog), FALSE, FALSE);

            switch(act)
            {
                case GTK_FILE_CHOOSER_ACTION_OPEN:
                {
#ifdef KGTK_DEBUG
                    printf("KGTK::run file chooser GTK_FILE_CHOOSER_ACTION_OPEN\n");
#endif
                    if(gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(dialog)))
                    {
                        GSList *files=NULL;

                        openKdeDialog(GTK_WIDGET(dialog), title ? title : "",
                                      data->folder ? data->folder : "",
                                      filter && filter->len
                                          ? filter->str
                                          : kgtkFileFilter
                                               ? kgtkFileFilter
                                               : "", OP_FILE_OPEN_MULTIPLE, &files,
                                      &selFilter, FALSE);

                        if(files)
                        {
                            GSList *c;

                            gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(dialog));
                            for(c=files; c; c=g_slist_next(c))
                                gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog),
                                                                 (gchar *)(c->data));
                            g_slist_foreach(files, (GFunc)g_free, NULL);
                            g_slist_free(files);

                            resp=data->ok;
                        }
                    }
                    else
                    {
                        gchar  *file=NULL;
                        GSList *res=NULL;

                        openKdeDialog(GTK_WIDGET(dialog), title ? title : "",
                                      data->folder ? data->folder : "",
                                      filter && filter->len
                                          ? filter->str
                                          : kgtkFileFilter
                                               ? kgtkFileFilter
                                               : "", OP_FILE_OPEN, &res, &selFilter, FALSE);
                        file=firstEntry(res);

                        if(file)
                        {
                            gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(dialog));
                            gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), file);
                            g_free(file);
                            resp=data->ok;
                        }
                    }
                    break;
                }
                case GTK_FILE_CHOOSER_ACTION_SAVE:
                {
                    gchar  *file=NULL;
                    GSList *res=NULL;

#ifdef KGTK_DEBUG
                    printf("KGTK::run file chooser GTK_FILE_CHOOSER_ACTION_SAVE\n");
#endif
                    if(data->name)
                    {
                        GString *cur=g_string_new(data->folder ? data->folder
                                                               : get_current_dir_name());

                        cur=g_string_append(cur, "/");
                        cur=g_string_append(cur, data->name);
                        current=g_string_free(cur, FALSE);
                    }

                    openKdeDialog(GTK_WIDGET(dialog), title ? title : "",
                                  current ? current : (data->folder ? data->folder : ""),
                                  filter && filter->len
                                          ? filter->str
                                          : kgtkFileFilter
                                               ? kgtkFileFilter
                                               : "", OP_FILE_SAVE, &res, &selFilter, origOverwrite);
                    file=firstEntry(res);

                    if(file)
                    {
                        /* Firefox crashes when we save to an existing name -> so just delete it first! */
                        if(APP_FIREFOX==kgtkApp && origOverwrite)
                        {
                            struct stat info;

                            if(0==lstat(file, &info))
                                unlink(file);
                        }
                        gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(dialog));
                        gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), file);
                        g_free(file);
                        resp=data->ok;
                    }
                    break;
                }
                case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
                case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
                {
                    GSList *res=NULL;
                    gchar  *folder=NULL;

#ifdef KGTK_DEBUG
                    if(GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER==act)
                        printf("KGTK::run file chooser GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER\n");
                    else
                        printf("KGTK::run file chooser GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER\n");
#endif
                    openKdeDialog(GTK_WIDGET(dialog), title ? title : "",
                                  data->folder ? data->folder : "", NULL,
                                  OP_FOLDER, &res, NULL, false);
                    folder=firstEntry(res);

                    if(folder)
                    {
                        gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), folder);
                        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), folder);
                        g_free(folder);
                        resp=data->ok;
                    }
                }
            }

            if(current)
                g_free(current);

            if(filter)
                g_string_free(filter, TRUE);

            if(selFilter)
            {
                setFilter(selFilter, dialog, act);
                g_free(selFilter);
            }

#ifdef KGTK_DEBUG
            printf("KGTK::RETURN RESP:%d\n", resp);
            printf("OK would be %d; Cancel would be %d\n", data->ok, data->cancel);
#endif
            g_signal_emit_by_name(dialog, "response", resp);
            running=FALSE;
            return resp;
        }
#ifdef KGTK_DEBUG
        printf("KGTK::ALREADY RUNNING SO RETURN RESP:%d\n", data->cancel);
#endif
        g_signal_emit_by_name(dialog, "response", data->cancel);
        return data->cancel;
    }
    return (gint)realFunction(dialog);
}

void gtk_widget_destroy(GtkWidget *widget)
{
    static void (*realFunction)(GtkWidget*) = NULL;

    if(!realFunction)
        realFunction = (void (*)(GtkWidget*)) real_dlsym(RTLD_NEXT, "gtk_widget_destroy");

    if(fileDialogHash && GTK_IS_FILE_CHOOSER(widget))
        freeHash(widget);

    realFunction(widget);
}

gchar * gtk_file_chooser_get_filename(GtkFileChooser *chooser)
{
    KGtkFileData *data=lookupHash(chooser, FALSE);

    static gchar * (*realFunction)(GtkFileChooser*) = NULL;

    if(!realFunction)
        realFunction = (gchar *(*)(GtkFileChooser*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_get_filename");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_file_chooser_get_filename Bypassing internal function\n"); fflush(stdout);
#endif
        return realFunction(chooser);
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_get_filename %d %s\n", data ? g_slist_length(data->files) : 12345,
           data && data->files && data->files->data ? data->files->data : "<>");
#endif
    return data && data->files && data->files->data ? g_strdup(data->files->data) : NULL;
}

gboolean gtk_file_chooser_select_filename(GtkFileChooser *chooser, const char *filename)
{
    KGtkFileData *data=lookupHash(chooser, TRUE);
    static gboolean (*realFunction)(GtkFileChooser*, const char*) = NULL;

    if(!realFunction)
        realFunction = (gboolean (*)(GtkFileChooser*, const char*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_select_filename");
    realFunction(chooser, filename);
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
        return FALSE;
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_select_filename %s, %d\n", filename,
           data ? g_slist_length(data->files) : 12345);
#endif
    if(data && filename)
    {
        GSList *c=NULL;

        for(c=data->files; c; c=g_slist_next(c))
            if(c->data && 0==strcmp((char *)(c->data), filename))
                break;

        if(!c)
        {
            gchar *folder=g_path_get_dirname(filename);

            data->files=g_slist_prepend(data->files, g_strdup(filename));

            if(folder && (!data->folder || strcmp(folder, data->folder)))
            {
                gtk_file_chooser_set_current_folder(chooser, folder);
                g_free(folder);
            }
        }
    }

    return TRUE;
}

void gtk_file_chooser_unselect_all(GtkFileChooser *chooser)
{
    KGtkFileData *data=lookupHash(chooser, TRUE);
    static void * (*realFunction)(GtkFileChooser*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkFileChooser*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_unselect_all");
    realFunction(chooser);
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
        return;
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_unselect_all %d\n", data ? g_slist_length(data->files) : 12345);
#endif
    if(data && data->files)
    {
        g_slist_foreach(data->files, (GFunc)g_free, NULL);
        g_slist_free(data->files);
        data->files=NULL;
    }
}

gboolean gtk_file_chooser_set_filename(GtkFileChooser *chooser, const char *filename)
{
    KGtkFileData *data=lookupHash(chooser, TRUE);
    static void * (*realFunction)(GtkFileChooser*, const char*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkFileChooser*, const char*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_set_filename");
    realFunction(chooser, filename);
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
        return FALSE;
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_set_filename %s %d\n", filename,
           data ? g_slist_length(data->files) : 12345);
#endif
    if(data && filename)
    {
        gchar *folder=g_path_get_dirname(filename),
              *name=g_path_get_basename(filename);

        if(data->files)
        {
            g_slist_foreach(data->files, (GFunc)g_free, NULL);
            g_slist_free(data->files);
            data->files=NULL;
        }

        data->files=g_slist_prepend(data->files, g_strdup(filename));

        if(name && (!data->name || strcmp(name, data->name)))
            gtk_file_chooser_set_current_name(chooser, name);
        if(name)
            g_free(name);
        if(folder && (!data->folder || strcmp(folder, data->folder)))
            gtk_file_chooser_set_current_folder(chooser, folder);
        if(folder)
            g_free(folder);
    }

    return TRUE;
}

void gtk_file_chooser_set_current_name(GtkFileChooser *chooser, const char *filename)
{
    KGtkFileData         *data=lookupHash(chooser, TRUE);
    GtkFileChooserAction act=gtk_file_chooser_get_action(chooser);

    if ((GTK_FILE_CHOOSER_ACTION_SAVE==act || GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER==act) ||
           ((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED))
    {
        static void * (*realFunction)(GtkFileChooser*, const char*) = NULL;

        if(!realFunction)
            realFunction = (void *(*)(GtkFileChooser*, const char*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_set_current_name");
        realFunction(chooser, filename);
        if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
            return;
        }
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_set_current_name %s %d\n", filename,
           data ? g_slist_length(data->files) : 12345);
#endif
    if(data && filename)
    {
        if(data->name)
            g_free(data->name);
        data->name=g_strdup(filename);
    }
}

GSList * gtk_file_chooser_get_filenames(GtkFileChooser *chooser)
{
    KGtkFileData *data=lookupHash(chooser, FALSE);
    GSList       *rv=NULL;

    static void * (*realFunction)(GtkFileChooser*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkFileChooser*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_get_filenames");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_file_chooser_get_filenames Bypassing internal function\n"); fflush(stdout);
#endif
        return realFunction(chooser);
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_get_filenames %d\n", data ? g_slist_length(data->files) : 12345);
#endif
    if(data && data->files)
    {
        GSList *item=data->files;

        for(; item; item=g_slist_next(item))
        {
#ifdef KGTK_DEBUG
            printf("KGTK::FILE:%s\n", item->data);
#endif
            if(item->data)
                rv=g_slist_prepend(rv, g_strdup(item->data));
        }
    }
#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_get_filenames END\n");
#endif
    return rv;
}

gboolean gtk_file_chooser_set_current_folder(GtkFileChooser *chooser, const gchar *folder)
{
    KGtkFileData *data=lookupHash(chooser, TRUE);
    static void * (*realFunction)(GtkFileChooser*, const gchar*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkFileChooser*, const gchar*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_set_current_folder");
    realFunction(chooser, folder);
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
        return FALSE;
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_set_current_folder %s %d\n", folder,
           data ? g_slist_length(data->files) : 12345);
#endif
    if(data && folder)
    {
        if(data->folder)
            g_free(data->folder);
        data->folder=g_strdup(folder);
    }
        g_signal_emit_by_name(chooser, "current-folder-changed", 0);

    return TRUE;
}

gchar * gtk_file_chooser_get_current_folder(GtkFileChooser *chooser)
{
    KGtkFileData *data=lookupHash(chooser, FALSE);

    static void * (*realFunction)(GtkFileChooser*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkFileChooser*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_get_current_folder");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_file_chooser_get_current_folder Bypassing internal function\n"); fflush(stdout);
#endif
        return realFunction(chooser);
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_get_current_folder %d\n",
           data ? g_slist_length(data->files) : 12345);
#endif
    if(!data)
    {
        gtk_file_chooser_set_current_folder(chooser, get_current_dir_name());
        data=g_hash_table_lookup(fileDialogHash, chooser);
    }

    return data && data->folder ? g_strdup(data->folder) : NULL;
}

gchar * gtk_file_chooser_get_uri(GtkFileChooser *chooser)
{
    static void * (*realFunction)(GtkFileChooser*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkFileChooser*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_get_uri");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_file_chooser_get_uri Bypassing internal function\n"); fflush(stdout);
#endif
        return realFunction(chooser);
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_get_uri\n");
#endif
    gchar *filename=gtk_file_chooser_get_filename(chooser);

    if(filename)
    {
        gchar *uri=g_filename_to_uri(filename, NULL, NULL);

        g_free(filename);
        return uri;
    }

    return NULL;
}

gboolean gtk_file_chooser_set_uri(GtkFileChooser *chooser, const char *uri)
{
    static gboolean (*realFunction)(GtkFileChooser*, const char *) = NULL;

    if(!realFunction)
        realFunction = (gboolean (*)(GtkFileChooser*, const char *)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_set_uri");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_file_chooser_set_uri Bypassing internal function\n"); fflush(stdout);
#endif
        return realFunction(chooser, uri);
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_set_uri\n");
#endif

    gchar    *file=g_filename_from_uri(uri, NULL, NULL);
    gboolean rv=FALSE;

    if(file)
    {
        rv=gtk_file_chooser_set_filename(chooser, file);

        g_free(file);
    }
    return rv;
}

GSList * gtk_file_chooser_get_uris(GtkFileChooser *chooser)
{
    static void * (*realFunction)(GtkFileChooser*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkFileChooser*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_get_uris");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_file_chooser_get_uris Bypassing internal function\n"); fflush(stdout);
#endif
        return realFunction(chooser);
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_get_uris\n");
#endif
    return addProtocols(gtk_file_chooser_get_filenames(chooser));
}

gboolean gtk_file_chooser_set_current_folder_uri(GtkFileChooser *chooser, const gchar *uri)
{
    static gboolean (*realFunction)(GtkFileChooser*, const gchar*) = NULL;

    if(!realFunction)
        realFunction = (gboolean (*)(GtkFileChooser*, const gchar*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_set_current_folder_uri");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_file_chooser_set_current_folder_uri Bypassing internal function\n"); fflush(stdout);
#endif
        return realFunction(chooser, uri);
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_set_current_folder_uri\n");
#endif
    gchar    *folder=g_filename_from_uri(uri, NULL, NULL);
    gboolean rv=FALSE;

    if(folder)
    {
        rv=gtk_file_chooser_set_current_folder(chooser, folder);

        g_free(folder);
    }
    return rv;
}

gchar * gtk_file_chooser_get_current_folder_uri(GtkFileChooser *chooser)
{
    static gchar * (*realFunction)(GtkFileChooser*) = NULL;

    if(!realFunction)
        realFunction = (gchar *(*)(GtkFileChooser*)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_get_current_folder_uri");

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_file_chooser_get_current_folder_uri Bypassing internal function\n"); fflush(stdout);
#endif
        return realFunction(chooser);
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_get_current_folder_uri\n");
#endif

    gchar *folder=gtk_file_chooser_get_current_folder(chooser);

    if(folder)
    {
        gchar *uri=g_filename_to_uri(folder, NULL, NULL);

        g_free(folder);
        return uri;
    }

    return NULL;
}

void g_signal_stop_emission_by_name(gpointer instance, const gchar *detailed_signal)
{
    static void * (*realFunction)(gpointer, const gchar*) = NULL;

    if(!realFunction)
        realFunction = (void *(*)(gpointer, const gchar*)) real_dlsym(RTLD_NEXT, "g_signal_stop_emission_by_name");

#ifdef KGTK_DEBUG
    printf("KGTK::g_signal_stop_emission_by_name %s  %s (check)\n", gtk_type_name(GTK_WIDGET_TYPE(instance)), detailed_signal);
#endif

    if(kgtkApp!=APP_GIMP || !GTK_IS_FILE_CHOOSER(instance) || strcmp(detailed_signal, "response"))
        realFunction(instance, detailed_signal);
#ifdef KGTK_DEBUG
    else
        printf("KGTK::g_signal_stop_emission_by_name %s  %s\n", gtk_type_name(GTK_WIDGET_TYPE(instance)), detailed_signal);
#endif
}

GtkWidget * gtk_dialog_add_button(GtkDialog *dialog, const gchar *button_text, gint response_id)
{
    static void * (*realFunction)(GtkDialog*, const gchar*, gint) = NULL;

    GtkWidget    *dlg=NULL;
    KGtkFileData *data=NULL;

    if(!realFunction)
        realFunction = (void *(*)(GtkDialog*, const gchar*, gint)) real_dlsym(RTLD_NEXT, "gtk_dialog_add_button");
    dlg = realFunction(dialog, button_text, response_id);

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED) && realFunction) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_dialog_add_button Bypassing internal function\n"); fflush(stdout);
#endif
        return dlg;
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_dialog_add_button\n");
#endif
    data=lookupHash(dialog, TRUE);

    if(button_text && (0==strcmp(button_text, GTK_STOCK_CANCEL) || 0==strcmp(button_text, GTK_STOCK_CLOSE) ||
                0==strcmp(button_text, GTK_STOCK_QUIT) || 0==strcmp(button_text, GTK_STOCK_NO))) {
        data->cancel=response_id;
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_dialog_add_button Overriding data->cancel: %d\n", data->cancel);
#endif
    }
    else if(button_text && (0==strcmp(button_text, GTK_STOCK_OK) || 0==strcmp(button_text, GTK_STOCK_OPEN) ||
                     0==strcmp(button_text, GTK_STOCK_SAVE) || 0==strcmp(button_text, GTK_STOCK_YES))) {
        data->ok=response_id;
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_dialog_add_button Overriding data->ok: %d\n", data->ok);
#endif
    }

    return NULL;
}

void gtk_dialog_add_buttons(GtkDialog *dialog, const gchar *first_button_text, ...)
{
    KGtkFileData *data=NULL;
    const char   *text=first_button_text;
    gint         id;
    va_list      varargs;

    va_start(varargs, first_button_text);
    kgtk_dialog_add_buttons_valist(dialog, first_button_text, varargs);
    va_end(varargs);

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED)) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_dialog_add_buttons Bypassing internal function\n"); fflush(stdout);
#endif
        return;
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_dialog_add_buttons\n");
#endif
    data=lookupHash(dialog, TRUE);
    va_start(varargs, first_button_text);
    while(text)
    {
        id = va_arg(varargs, gint);

        if(text && (0==strcmp(text, GTK_STOCK_CANCEL) || 0==strcmp(text, GTK_STOCK_CLOSE) ||
                    0==strcmp(text, GTK_STOCK_QUIT) || 0==strcmp(text, GTK_STOCK_NO))) {
            data->cancel=id;
#ifdef KGTK_DEBUG
            printf("KGTK::gtk_dialog_add_buttons Overriding data->cancel: %d\n", data->cancel);
#endif
        }
        else if(text && (0==strcmp(text, GTK_STOCK_OK) || 0==strcmp(text, GTK_STOCK_OPEN) ||
                         0==strcmp(text, GTK_STOCK_SAVE) || 0==strcmp(text, GTK_STOCK_YES))) {
            data->ok=id;
#ifdef KGTK_DEBUG
            printf("KGTK::gtk_dialog_add_buttons Overriding data->ok: %d\n", data->ok);
#endif
        }
      text=va_arg(varargs, const gchar *);
    }
}

GtkWidget * gtk_file_chooser_dialog_new(const gchar *title, GtkWindow *parent,
                             GtkFileChooserAction action, const gchar *first_button_text,
                             ...)
{
    GtkWidget    *dlg=NULL;
    KGtkFileData *data=NULL;
    const char   *text=first_button_text;
    gint         id;
    va_list      varargs;

    va_start(varargs, first_button_text);
    dlg=kgtk_file_chooser_dialog_new_valist(title, parent, action, NULL, first_button_text, varargs);
    va_end(varargs);

    determineAppName();
    if (((kgtkApp == APP_UNKNOWN && BLACKLIST_UNKNOWN_GTK_APPS) || kgtkApp == APP_BLACKLISTED)) {
#ifdef KGTK_DEBUG
        printf("KGTK::gtk_file_chooser_dialog_new Bypassing internal function\n"); fflush(stdout);
#endif
        return dlg;
    }

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_dialog_new\n");
#endif
    data=lookupHash(dlg, TRUE);
    va_start(varargs, first_button_text);
    while(text)
    {
        id = va_arg(varargs, gint);

        if(text && (0==strcmp(text, GTK_STOCK_CANCEL) || 0==strcmp(text, GTK_STOCK_CLOSE) ||
                    0==strcmp(text, GTK_STOCK_QUIT) || 0==strcmp(text, GTK_STOCK_NO))) {
            data->cancel=id;
#ifdef KGTK_DEBUG
            printf("KGTK::gtk_file_chooser_dialog_new Overriding data->cancel: %d\n", data->cancel);
#endif
        }
        else if(text && (0==strcmp(text, GTK_STOCK_OK) || 0==strcmp(text, GTK_STOCK_OPEN) ||
                         0==strcmp(text, GTK_STOCK_SAVE) || 0==strcmp(text, GTK_STOCK_YES))) {
            data->ok=id;
#ifdef KGTK_DEBUG
            printf("KGTK::gtk_file_chooser_dialog_new Overriding data->ok: %d\n", data->ok);
#endif
        }
      text=va_arg(varargs, const gchar *);
    }
    va_end(varargs);
    return dlg;
}

#if GTK_CHECK_VERSION(2, 6, 0)
static void handleGtkFileChooserButtonClicked(GtkButton *button, gpointer user_data)
{
#ifdef KGTK_DEBUG
    printf("KGTK::handleGtkFileChooserButtonClicked\n");
#endif
    gtk_dialog_run((GtkDialog*)GTK_FILE_CHOOSER_BUTTON(user_data)->priv->dialog);
}

static void handleGtkFileChooserComboChanged(GtkComboBox *combo_box, gpointer user_data)
{
    static gboolean handle=TRUE;
    GtkTreeIter iter;

#ifdef KGTK_DEBUG
    printf("KGTK::handleGtkFileChooserComboChanged (handle:%d)\n", handle);
#endif
    if(!handle)
        return;

    if(gtk_combo_box_get_active_iter (combo_box, &iter))
    {
        GtkFileChooserButtonPrivate *priv=GTK_FILE_CHOOSER_BUTTON(user_data)->priv;
        gchar type=ROW_TYPE_INVALID;

        gtk_tree_model_get(priv->filter_model, &iter, TYPE_COLUMN, &type, -1);

        if(ROW_TYPE_OTHER==type)
            gtk_dialog_run((GtkDialog*)GTK_FILE_CHOOSER_BUTTON(user_data)->priv->dialog);
        else
        {
            g_signal_handler_unblock(priv->combo_box, priv->combo_box_changed_id);
            handle=FALSE;
            g_signal_emit_by_name(priv->combo_box, "changed");
            handle=TRUE;
            g_signal_handler_block(priv->combo_box, priv->combo_box_changed_id);
        }
    }
}

GtkWidget * gtk_file_chooser_button_new(const gchar *title, GtkFileChooserAction action)
{
    static void * (*realFunction)(const gchar*, GtkFileChooserAction) = NULL;

    GtkWidget *button=NULL;

    if(!realFunction)
        realFunction = (void *(*)(const gchar*, GtkFileChooserAction)) real_dlsym(RTLD_NEXT, "gtk_file_chooser_button_new");

#ifdef KGTK_DEBUG
    printf("KGTK::gtk_file_chooser_button_new\n");
#endif

    if(kgtkInit(NULL))
    {
        GtkFileChooserButtonPrivate *priv=NULL;

        button=(GtkWidget*)realFunction(title, action);
        priv=GTK_FILE_CHOOSER_BUTTON(button)->priv;

        if(priv->button)
        {
            g_signal_handlers_disconnect_matched(priv->button,
                                                 G_SIGNAL_MATCH_DATA,0, 0, NULL, NULL, button);

            g_signal_connect(priv->button, "clicked",
                             G_CALLBACK(handleGtkFileChooserButtonClicked),
                             GTK_FILE_CHOOSER_BUTTON(button));
        }
        if(priv->combo_box)
        {
            g_signal_handler_block(priv->combo_box, priv->combo_box_changed_id);

            g_signal_connect(priv->combo_box, "changed",
                             G_CALLBACK(handleGtkFileChooserComboChanged),
                             GTK_FILE_CHOOSER_BUTTON(button));
        }
    }
    return button;
}
#endif

static gboolean isGtk(const char *str)
{
    return 'g'==str[0] && 't'==str[1] && 'k'==str[2] && '_'==str[3];
}

static void * kgtk_get_fnptr(const char *raw_name)
{
    if(raw_name && isGtk(raw_name) && kgtkInit(NULL))
    {
        if(0==strcmp(raw_name, "gtk_file_chooser_get_filename"))
            return &gtk_file_chooser_get_filename;

        else if(0==strcmp(raw_name, "gtk_file_chooser_select_filename"))
            return &gtk_file_chooser_select_filename;

        else if(0==strcmp(raw_name, "gtk_file_chooser_unselect_all"))
            return &gtk_file_chooser_unselect_all;

        else if(0==strcmp(raw_name, "gtk_file_chooser_set_filename"))
            return &gtk_file_chooser_set_filename;

        else if(0==strcmp(raw_name, "gtk_file_chooser_set_current_name"))
            return &gtk_file_chooser_set_current_name;

        else if(0==strcmp(raw_name, "gtk_file_chooser_get_filenames"))
            return &gtk_file_chooser_get_filenames;

        else if(0==strcmp(raw_name, "gtk_file_chooser_set_current_folder"))
            return &gtk_file_chooser_set_current_folder;

        else if(0==strcmp(raw_name, "gtk_file_chooser_get_current_folder"))
            return &gtk_file_chooser_get_current_folder;

        else if(0==strcmp(raw_name, "gtk_file_chooser_get_uri"))
            return &gtk_file_chooser_get_uri;

        else if(0==strcmp(raw_name, "gtk_file_chooser_set_uri"))
            return &gtk_file_chooser_set_uri;

        else if(0==strcmp(raw_name, "gtk_file_chooser_get_uris"))
            return &gtk_file_chooser_get_uris;

        else if(0==strcmp(raw_name, "gtk_file_chooser_set_current_folder_uri"))
            return &gtk_file_chooser_set_current_folder_uri;

        else if(0==strcmp(raw_name, "gtk_file_chooser_get_current_folder_uri"))
            return &gtk_file_chooser_get_current_folder_uri;

        else if(0==strcmp(raw_name, "gtk_file_chooser_dialog_new"))
            return &gtk_file_chooser_dialog_new;

        else if(0==strcmp(raw_name, "gtk_dialog_add_button"))
            return &gtk_dialog_add_button;

        else if(0==strcmp(raw_name, "gtk_dialog_add_buttons"))
            return &gtk_dialog_add_buttons;

        else if(0==strcmp(raw_name, "gtk_file_chooser_button_new"))
            return &gtk_file_chooser_button_new;

/*
        else if(0==strcmp(raw_name, "gtk_init_check"))
            return &gtk_init_check;
*/
    }

    return NULL;
}

const gchar * kgtk_g_module_check_init(GModule *module)
{
    return gtk_check_version(GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION - GTK_INTERFACE_AGE);
}

/* Mozilla specific */
void * PR_FindFunctionSymbol(struct PR_LoadLibrary *lib, const char *raw_name)
{
    static void * (*realFunction)(struct PR_LoadLibrary*, const char*) = NULL;

    void *rv=NULL;

    if(!realFunction)
        realFunction = (void *(*)(struct PR_LoadLibrary*, const char*)) real_dlsym(RTLD_NEXT, "PR_FindFunctionSymbol");

#ifdef KGTK_DEBUG_DLSYM
    printf("KGTK::PR_FindFunctionSymbol : %s\n", raw_name);
#endif

    rv=kgtk_get_fnptr(raw_name);

    if(!rv)
    {
        if (0==strcmp(raw_name, "g_module_check_init"))
            rv=&kgtk_g_module_check_init;
        else if (isGtk(raw_name))
            rv=real_dlsym(RTLD_NEXT, raw_name);
    }

#ifdef KGTK_DEBUG_DLSYM
    printf("KGTK::PR_FindFunctionSymbol found? %d\n", (rv || realFunction) ? 1 : 0);
#endif

    if ((rv != NULL) || (realFunction != NULL))
        return rv ? rv : realFunction(lib, raw_name);
    else
        return NULL;
}

/* Overriding dlsym is required for SWT - which dlsym's the gtk_file_chooser functions! */
static void * real_dlsym(void *handle, const char *name)
{
  static void * (*realFunction)(void*, const char*) = NULL;

#ifdef KGTK_DEBUG_DLSYM
  printf("KGTK::real_dlsym : %s\n", name);
#endif

  if (!realFunction)
  {
    // Get the real dlsym function
#ifdef HAVE_DL_SYM
    realFunction = _dl_sym(RTLD_NEXT, "dlsym", dlsym);
#else
    realFunction = dlvsym(RTLD_NEXT, "dlsym", "GLIBC_2.2.5");
#endif
  }

  if (realFunction)
    return realFunction(handle, name);
  else
  {
    printf("kgtk-qt3 gtk2 real_dlsymc() realFunction not found!!\n");
    return NULL;
  }
}

void * dlsym(void *handle, const char *name)
{
    // Need this so _dl_sym will be able to find the next dlsym, i.e. the real dlsym!
    if (!strcmp(name, "dlsym"))
    {
      return (void*)dlsym;
    }

    void *rv=NULL;

#ifdef KGTK_DEBUG_DLSYM
    printf("KGTK::dlsym : (%04X) %s\n", (int)handle, name);
#endif
    rv=kgtk_get_fnptr(name);

    if(!rv)
        rv=real_dlsym(handle, name);

    if(!rv && 0==strcmp(name, "g_module_check_init"))
        rv=&kgtk_g_module_check_init;

#ifdef KGTK_DEBUG_DLSYM
    printf("KGTK::dlsym found? %d\n", rv ? 1 : 0);
#endif
    return rv;
}
