diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-09-09 20:27:19 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-09-09 20:27:19 +0000 |
commit | c6ca83d07d95e076b09bd802f66ba72d363b0235 (patch) | |
tree | f13000febb0c9c5a5da621b4bba53ba3eace022e /gtk2 | |
download | kgtk-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')
-rw-r--r-- | gtk2/CMakeLists.txt | 22 | ||||
-rwxr-xr-x | gtk2/kgtk2-wrapper.cmake | 33 | ||||
-rw-r--r-- | gtk2/kgtk2.c | 2005 |
3 files changed, 2060 insertions, 0 deletions
diff --git a/gtk2/CMakeLists.txt b/gtk2/CMakeLists.txt new file mode 100644 index 0000000..36b3e3f --- /dev/null +++ b/gtk2/CMakeLists.txt @@ -0,0 +1,22 @@ +include(FindPkgConfig) + +pkg_check_modules(GTK gtk+-2.0>=2.6) + +if (GTK_FOUND) + message("** INFORMATION: Gtk2 LD_PRELOAD library will be built.") + + # set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) + set(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX} CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) + + include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/common ${CMAKE_BINARY_DIR} ${GTK_INCLUDE_DIRS}) + set(kgtk2_SRCS kgtk2.c) + add_library(kgtk2 SHARED ${kgtk2_SRCS}) + target_link_libraries(kgtk2 ${GTK_LDFLAGS} -lgthread-2.0 -lglib-2.0 -lc -ldl) + + install(TARGETS kgtk2 LIBRARY DESTINATION ${LIB_INSTALL_DIR}/kgtk ) + + configure_file (kgtk2-wrapper.cmake ${CMAKE_CURRENT_BINARY_DIR}/kgtk2-wrapper @ONLY) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/kgtk2-wrapper DESTINATION bin) +else (GTK_FOUND) + message("** ERROR : Could not locate Gtk2 headers, Gtk2 LD_PRELOAD library will not be built.") +endif (GTK_FOUND) diff --git a/gtk2/kgtk2-wrapper.cmake b/gtk2/kgtk2-wrapper.cmake new file mode 100755 index 0000000..46865b7 --- /dev/null +++ b/gtk2/kgtk2-wrapper.cmake @@ -0,0 +1,33 @@ +#!/bin/bash + +# +# This script is part of the KGtk package. +# +# (C) Craig Drummond, 2007 +# +# Craig.Drummond@lycos.co.uk +# +# -- +# Released under the GPL v2 or later +# -- +# + +if [ "`locale | grep 'LANG=' | grep -i 'utf-8' | wc -l`" = "0" ] ; then + export G_BROKEN_FILENAMES=1 +fi + +app=`basename $0` + +if [ "$app" = "kgtk2-wrapper" ] ; then + LD_PRELOAD=@CMAKE_INSTALL_PREFIX@/lib/kgtk/libkgtk2.so:$LD_PRELOAD "$@" +else + dir=`dirname $0` + oldPath=$PATH + PATH=`echo $PATH | sed s:$dir::g` + real=`which $app` + PATH=$oldPath + + if [ "$real" != "" ] && [ "`dirname $real`" != "$dir" ] ; then + LD_PRELOAD=@CMAKE_INSTALL_PREFIX@/lib@LIB_SUFFIX@/kgtk/libkgtk2.so:$LD_PRELOAD $real "$@" + fi +fi 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)>K_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 >k_file_chooser_get_filename; + + else if(0==strcmp(raw_name, "gtk_file_chooser_select_filename")) + return >k_file_chooser_select_filename; + + else if(0==strcmp(raw_name, "gtk_file_chooser_unselect_all")) + return >k_file_chooser_unselect_all; + + else if(0==strcmp(raw_name, "gtk_file_chooser_set_filename")) + return >k_file_chooser_set_filename; + + else if(0==strcmp(raw_name, "gtk_file_chooser_set_current_name")) + return >k_file_chooser_set_current_name; + + else if(0==strcmp(raw_name, "gtk_file_chooser_get_filenames")) + return >k_file_chooser_get_filenames; + + else if(0==strcmp(raw_name, "gtk_file_chooser_set_current_folder")) + return >k_file_chooser_set_current_folder; + + else if(0==strcmp(raw_name, "gtk_file_chooser_get_current_folder")) + return >k_file_chooser_get_current_folder; + + else if(0==strcmp(raw_name, "gtk_file_chooser_get_uri")) + return >k_file_chooser_get_uri; + + else if(0==strcmp(raw_name, "gtk_file_chooser_set_uri")) + return >k_file_chooser_set_uri; + + else if(0==strcmp(raw_name, "gtk_file_chooser_get_uris")) + return >k_file_chooser_get_uris; + + else if(0==strcmp(raw_name, "gtk_file_chooser_set_current_folder_uri")) + return >k_file_chooser_set_current_folder_uri; + + else if(0==strcmp(raw_name, "gtk_file_chooser_get_current_folder_uri")) + return >k_file_chooser_get_current_folder_uri; + + else if(0==strcmp(raw_name, "gtk_file_chooser_dialog_new")) + return >k_file_chooser_dialog_new; + + else if(0==strcmp(raw_name, "gtk_file_chooser_button_new")) + return >k_file_chooser_button_new; + +/* + else if(0==strcmp(raw_name, "gtk_init_check")) + return >k_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 |