summaryrefslogtreecommitdiffstats
path: root/gtk2/kgtk2.c
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-09-09 20:27:19 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-09-09 20:27:19 +0000
commitc6ca83d07d95e076b09bd802f66ba72d363b0235 (patch)
treef13000febb0c9c5a5da621b4bba53ba3eace022e /gtk2/kgtk2.c
downloadkgtk-qt3-c6ca83d07d95e076b09bd802f66ba72d363b0235.tar.gz
kgtk-qt3-c6ca83d07d95e076b09bd802f66ba72d363b0235.zip
* Added kgtk-qt3
* Slight kpowersave message cleanup git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kgtk-qt3@1173604 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'gtk2/kgtk2.c')
-rw-r--r--gtk2/kgtk2.c2005
1 files changed, 2005 insertions, 0 deletions
diff --git a/gtk2/kgtk2.c b/gtk2/kgtk2.c
new file mode 100644
index 0000000..d0457c5
--- /dev/null
+++ b/gtk2/kgtk2.c
@@ -0,0 +1,2005 @@
+/************************************************************************
+ *
+ * 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"
+
+#ifndef KGTK_DLSYM_VERSION
+#define KGTK_DLSYM_VERSION "GLIBC_2.0"
+#endif
+
+/*
+#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,
+ APP_KINO
+} 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_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(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));
+ }
+
+ /*
+ 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_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 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], "%f", &dummy))
+ 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;
+ kgtkAppName=getAppName(appName);
+ useKde=NULL!=getenv("KDE_FULL_SESSION") && connectToKDialogD(kgtkAppName);
+ 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, "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
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+}
+
+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(combo))
+ return 1;
+ else
+ {
+ static void * (*realFunction)() = NULL;
+
+ if(!realFunction)
+ realFunction = (void *(*)()) real_dlsym(RTLD_NEXT, "gtk_combo_box_get_active");
+
+ rv=(int)realFunction(combo);
+ }
+
+ return rv;
+}
+
+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)
+ {
+ /* 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);
+#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)
+{
+#ifdef KGTK_DEBUG
+ printf("KGTK::handleGtkFileChooserButtonClicked\n");
+#endif
+ 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;
+
+#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(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");
+
+#ifdef KGTK_DEBUG
+ printf("KGTK::gtk_file_chooser_button_new\n");
+#endif
+
+ 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