/************************************************************************
 *
 * 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 _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"

#ifndef KGTK_DLSYM_VERSION
#define KGTK_DLSYM_VERSION "GLIBC_2.0"
#endif

/*
#define KGTK_DEBUG
*/
/*
#define KGTK_DEBUG_DLSYM
*/

/*
 * For SWT apps (e.g. eclipse) we need to override dlsym, but we can only do this if
 * dlvsym is present in libdl. dlvsym is needed so that we can access the real dlsym
 * as well as our fake dlsym
 */
#ifdef HAVE_DLVSYM
static void * real_dlsym (void *handle, const char *name);
#else
#define real_dlsym(A, B) dlsym(A, B)
#endif

typedef enum
{
    APP_ANY,
    APP_GIMP,
    APP_INKSCAPE,
    APP_FIREFOX
} Application;

static gboolean    useKde=FALSE;
static gboolean    kdialogdError=FALSE;
static GMainLoop   *kdialogdLoop=NULL;
static gchar       *kgtkFileFilter=NULL;
static Application kgtkApp=APP_ANY;

#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=(struct 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(NULL)))
    {
        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));
            }

            /*
                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_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_create(&kdialogdMain, &d, FALSE, NULL);

            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 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("KDE_FULL_SESSION"));
#endif

        initialised=TRUE;
        useKde=NULL!=getenv("KDE_FULL_SESSION") && connectToKDialogD(getAppName(appName));
        if(useKde)
        {
            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, "firefox") || isApp(prg, "swiftfox") || isApp(prg, "iceweasel"))
                {
                    kgtkApp=APP_FIREFOX;
#ifdef KGTK_DEBUG
                    printf("KGTK::Firefox\n");
#endif
                }
            }

            if(!g_threads_got_initialized)
                g_thread_init(NULL);
            atexit(&kgtkExit);
        }
    }

#ifdef KGTK_DEBUG
    printf("KGTK::kgtkInit useKde:%d\n", useKde);
#endif

    return useKde;
}

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

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;
}
/* ......................... */

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

    gboolean rv=FALSE;

    if(!realFunction)
        realFunction = (void *(*)()) 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)() = NULL;

    if(!realFunction)
        realFunction = (void *(*)()) 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 Qt/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)() = NULL;

    if(!realFunction)
        realFunction = (void *(*)()) 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)() = NULL;

    if(!realFunction)
        realFunction = (void *(*)()) 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)() = NULL;

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

    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 void * (*realFunction)() = NULL;

    gboolean rv=FALSE;

    if(!realFunction)
        realFunction = (void *(*)()) 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=(gboolean) realFunction(widget);
            }
            rv=data->doOverwrite;
        }
        else
            rv=(gboolean) realFunction(widget);
    }

    return rv;
}

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

    if(!realFunction)
        realFunction = (void *(*)()) 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;
            }
        }
    }
}

void gtk_file_chooser_set_do_overwrite_confirmation(GtkFileChooser *widget, gboolean v)
{
    kgtkFileChooserSetDoOverwriteConfirmation(widget, v, TRUE);
}

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

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

#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)
                    {
                        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);
#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 realFunction(dialog);
}

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

    if(!realFunction)
        realFunction = (void *(*)()) 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);

#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 void * (*realFunction)() = NULL;

    if(!realFunction)
        realFunction = (void *(*)()) real_dlsym(RTLD_NEXT, "gtk_file_chooser_select_filename");
    realFunction(chooser, filename);

#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)() = NULL;

    if(!realFunction)
        realFunction = (void *(*)()) real_dlsym(RTLD_NEXT, "gtk_file_chooser_unselect_all");
    realFunction(chooser);

#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)() = NULL;

    if(!realFunction)
        realFunction = (void *(*)()) real_dlsym(RTLD_NEXT, "gtk_file_chooser_set_filename");
    realFunction(chooser, filename);

#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)
    {
        static void * (*realFunction)() = NULL;

        if(!realFunction)
            realFunction = (void *(*)()) real_dlsym(RTLD_NEXT, "gtk_file_chooser_set_current_name");
        realFunction(chooser, filename);
    }

#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;

#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)() = NULL;

    if(!realFunction)
        realFunction = (void *(*)()) real_dlsym(RTLD_NEXT, "gtk_file_chooser_set_current_folder");
    realFunction(chooser, folder);

#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);

#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)
{
#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)
{
#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)
{
#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)
{
#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)
{
#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)() = NULL;

    if(!realFunction)
        realFunction = (void *(*)()) 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_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);

#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;
        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;
      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)
{
    gtk_dialog_run(GTK_FILE_CHOOSER_BUTTON(user_data)->priv->dialog);
}

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

    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(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)() = NULL;

    GtkWidget *button=NULL;

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

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

        button=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_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)() = NULL;

    void *rv=NULL;

    if(!realFunction)
        realFunction = (void *(*)()) 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);
    }

    return rv ? rv : realFunction(lib, raw_name);
}

#ifdef HAVE_DLVSYM
/* 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)() = NULL;

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

    if (!realFunction)
    {
        void *ldHandle=dlopen("libdl.so", RTLD_NOW);

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

        if(ldHandle)
        {
            static const char * versions[]={KGTK_DLSYM_VERSION, "GLIBC_2.3", "GLIBC_2.2.5",
                                            "GLIBC_2.2", "GLIBC_2.1", "GLIBC_2.0", NULL};

            int i;

            for(i=0; versions[i] && !realFunction; ++i)
                realFunction=dlvsym(ldHandle, "dlsym", versions[i]);
        }
    }

    return realFunction(handle, name);
}

void * dlsym(void *handle, const char *name)
{
    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;
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1