1/*
2 * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25#include <dlfcn.h>
26#include <setjmp.h>
27#include <X11/Xlib.h>
28#include <limits.h>
29#include <stdio.h>
30#include <string.h>
31#include "gtk2_interface.h"
32#include "java_awt_Transparency.h"
33#include "jvm_md.h"
34#include "sizecalc.h"
35#include <jni_util.h>
36#include "awt.h"
37
38#define GTK_TYPE_BORDER                 ((*fp_gtk_border_get_type)())
39
40#define G_TYPE_FUNDAMENTAL_SHIFT        (2)
41#define G_TYPE_MAKE_FUNDAMENTAL(x)      ((GType) ((x) << G_TYPE_FUNDAMENTAL_SHIFT))
42
43#define CONV_BUFFER_SIZE 128
44
45#define NO_SYMBOL_EXCEPTION 1
46
47static void *gtk2_libhandle = NULL;
48static void *gthread_libhandle = NULL;
49
50static jmp_buf j;
51
52/* Widgets */
53static GtkWidget *gtk2_widget = NULL;
54static GtkWidget *gtk2_window = NULL;
55static GtkFixed  *gtk2_fixed  = NULL;
56
57/* Paint system */
58static GdkPixmap *gtk2_white_pixmap = NULL;
59static GdkPixmap *gtk2_black_pixmap = NULL;
60static GdkPixbuf *gtk2_white_pixbuf = NULL;
61static GdkPixbuf *gtk2_black_pixbuf = NULL;
62static int gtk2_pixbuf_width = 0;
63static int gtk2_pixbuf_height = 0;
64
65/* Static buffer for conversion from java.lang.String to UTF-8 */
66static char convertionBuffer[CONV_BUFFER_SIZE];
67
68static gboolean new_combo = TRUE;
69const char ENV_PREFIX[] = "GTK_MODULES=";
70
71
72static GtkWidget *gtk2_widgets[_GTK_WIDGET_TYPE_SIZE];
73
74/*************************
75 * Glib function pointers
76 *************************/
77
78static gboolean (*fp_g_main_context_iteration)(GMainContext *context,
79                                             gboolean may_block);
80
81static GValue*      (*fp_g_value_init)(GValue *value, GType g_type);
82static gboolean     (*fp_g_type_is_a)(GType type, GType is_a_type);
83static gboolean     (*fp_g_value_get_boolean)(const GValue *value);
84static gchar        (*fp_g_value_get_char)(const GValue *value);
85static guchar       (*fp_g_value_get_uchar)(const GValue *value);
86static gint         (*fp_g_value_get_int)(const GValue *value);
87static guint        (*fp_g_value_get_uint)(const GValue *value);
88static glong        (*fp_g_value_get_long)(const GValue *value);
89static gulong       (*fp_g_value_get_ulong)(const GValue *value);
90static gint64       (*fp_g_value_get_int64)(const GValue *value);
91static guint64      (*fp_g_value_get_uint64)(const GValue *value);
92static gfloat       (*fp_g_value_get_float)(const GValue *value);
93static gdouble      (*fp_g_value_get_double)(const GValue *value);
94static const gchar* (*fp_g_value_get_string)(const GValue *value);
95static gint         (*fp_g_value_get_enum)(const GValue *value);
96static guint        (*fp_g_value_get_flags)(const GValue *value);
97static GParamSpec*  (*fp_g_value_get_param)(const GValue *value);
98static gpointer*    (*fp_g_value_get_boxed)(const GValue *value);
99static gpointer*    (*fp_g_value_get_pointer)(const GValue *value);
100static GObject*     (*fp_g_value_get_object)(const GValue *value);
101static GParamSpec*  (*fp_g_param_spec_int)(const gchar *name,
102        const gchar *nick, const gchar *blurb,
103        gint minimum, gint maximum, gint default_value,
104        GParamFlags flags);
105static void         (*fp_g_object_get)(gpointer object,
106                                       const gchar* fpn, ...);
107static void         (*fp_g_object_set)(gpointer object,
108                                       const gchar *first_property_name,
109                                       ...);
110/************************
111 * GDK function pointers
112 ************************/
113static GdkPixmap *(*fp_gdk_pixmap_new)(GdkDrawable *drawable,
114        gint width, gint height, gint depth);
115static GdkGC *(*fp_gdk_gc_new)(GdkDrawable*);
116static void (*fp_gdk_rgb_gc_set_foreground)(GdkGC*, guint32);
117static void (*fp_gdk_draw_rectangle)(GdkDrawable*, GdkGC*, gboolean,
118        gint, gint, gint, gint);
119static GdkPixbuf *(*fp_gdk_pixbuf_new)(GdkColorspace colorspace,
120        gboolean has_alpha, int bits_per_sample, int width, int height);
121static void (*fp_gdk_drawable_get_size)(GdkDrawable *drawable,
122        gint* width, gint* height);
123
124/************************
125 * Gtk function pointers
126 ************************/
127static gboolean (*fp_gtk_init_check)(int* argc, char** argv);
128
129/* Painting */
130static void (*fp_gtk_paint_hline)(GtkStyle* style, GdkWindow* window,
131        GtkStateType state_type, GdkRectangle* area, GtkWidget* widget,
132        const gchar* detail, gint x1, gint x2, gint y);
133static void (*fp_gtk_paint_vline)(GtkStyle* style, GdkWindow* window,
134        GtkStateType state_type, GdkRectangle* area, GtkWidget* widget,
135        const gchar* detail, gint y1, gint y2, gint x);
136static void (*fp_gtk_paint_shadow)(GtkStyle* style, GdkWindow* window,
137        GtkStateType state_type, GtkShadowType shadow_type,
138        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
139        gint x, gint y, gint width, gint height);
140static void (*fp_gtk_paint_arrow)(GtkStyle* style, GdkWindow* window,
141        GtkStateType state_type, GtkShadowType shadow_type,
142        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
143        GtkArrowType arrow_type, gboolean fill, gint x, gint y,
144        gint width, gint height);
145static void (*fp_gtk_paint_diamond)(GtkStyle* style, GdkWindow* window,
146        GtkStateType state_type, GtkShadowType shadow_type,
147        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
148        gint x, gint y, gint width, gint height);
149static void (*fp_gtk_paint_box)(GtkStyle* style, GdkWindow* window,
150        GtkStateType state_type, GtkShadowType shadow_type,
151        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
152        gint x, gint y, gint width, gint height);
153static void (*fp_gtk_paint_flat_box)(GtkStyle* style, GdkWindow* window,
154        GtkStateType state_type, GtkShadowType shadow_type,
155        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
156        gint x, gint y, gint width, gint height);
157static void (*fp_gtk_paint_check)(GtkStyle* style, GdkWindow* window,
158        GtkStateType state_type, GtkShadowType shadow_type,
159        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
160        gint x, gint y, gint width, gint height);
161static void (*fp_gtk_paint_option)(GtkStyle* style, GdkWindow* window,
162        GtkStateType state_type, GtkShadowType shadow_type,
163        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
164        gint x, gint y, gint width, gint height);
165static void (*fp_gtk_paint_box_gap)(GtkStyle* style, GdkWindow* window,
166        GtkStateType state_type, GtkShadowType shadow_type,
167        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
168        gint x, gint y, gint width, gint height,
169        GtkPositionType gap_side, gint gap_x, gint gap_width);
170static void (*fp_gtk_paint_extension)(GtkStyle* style, GdkWindow* window,
171        GtkStateType state_type, GtkShadowType shadow_type,
172        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
173        gint x, gint y, gint width, gint height, GtkPositionType gap_side);
174static void (*fp_gtk_paint_focus)(GtkStyle* style, GdkWindow* window,
175        GtkStateType state_type, GdkRectangle* area, GtkWidget* widget,
176        const gchar* detail, gint x, gint y, gint width, gint height);
177static void (*fp_gtk_paint_slider)(GtkStyle* style, GdkWindow* window,
178        GtkStateType state_type, GtkShadowType shadow_type,
179        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
180        gint x, gint y, gint width, gint height, GtkOrientation orientation);
181static void (*fp_gtk_paint_handle)(GtkStyle* style, GdkWindow* window,
182        GtkStateType state_type, GtkShadowType shadow_type,
183        GdkRectangle* area, GtkWidget* widget, const gchar* detail,
184        gint x, gint y, gint width, gint height, GtkOrientation orientation);
185static void (*fp_gtk_paint_expander)(GtkStyle* style, GdkWindow* window,
186        GtkStateType state_type, GdkRectangle* area, GtkWidget* widget,
187        const gchar* detail, gint x, gint y, GtkExpanderStyle expander_style);
188static void (*fp_gtk_style_apply_default_background)(GtkStyle* style,
189        GdkWindow* window, gboolean set_bg, GtkStateType state_type,
190        GdkRectangle* area, gint x, gint y, gint width, gint height);
191
192/* Widget creation */
193static GtkWidget* (*fp_gtk_arrow_new)(GtkArrowType arrow_type,
194                                      GtkShadowType shadow_type);
195static GtkWidget* (*fp_gtk_button_new)();
196static GtkWidget* (*fp_gtk_check_button_new)();
197static GtkWidget* (*fp_gtk_check_menu_item_new)();
198static GtkWidget* (*fp_gtk_color_selection_dialog_new)(const gchar* title);
199static GtkWidget* (*fp_gtk_combo_box_new)();
200static GtkWidget* (*fp_gtk_combo_box_entry_new)();
201static GtkWidget* (*fp_gtk_entry_new)();
202static GtkWidget* (*fp_gtk_fixed_new)();
203static GtkWidget* (*fp_gtk_handle_box_new)();
204static GtkWidget* (*fp_gtk_hpaned_new)();
205static GtkWidget* (*fp_gtk_vpaned_new)();
206static GtkWidget* (*fp_gtk_hscale_new)(GtkAdjustment* adjustment);
207static GtkWidget* (*fp_gtk_vscale_new)(GtkAdjustment* adjustment);
208static GtkWidget* (*fp_gtk_hscrollbar_new)(GtkAdjustment* adjustment);
209static GtkWidget* (*fp_gtk_vscrollbar_new)(GtkAdjustment* adjustment);
210static GtkWidget* (*fp_gtk_hseparator_new)();
211static GtkWidget* (*fp_gtk_vseparator_new)();
212static GtkWidget* (*fp_gtk_image_new)();
213static GtkWidget* (*fp_gtk_label_new)(const gchar* str);
214static GtkWidget* (*fp_gtk_menu_new)();
215static GtkWidget* (*fp_gtk_menu_bar_new)();
216static GtkWidget* (*fp_gtk_menu_item_new)();
217static GtkWidget* (*fp_gtk_notebook_new)();
218static GtkWidget* (*fp_gtk_progress_bar_new)();
219static GtkWidget* (*fp_gtk_progress_bar_set_orientation)(
220        GtkProgressBar *pbar,
221        GtkProgressBarOrientation orientation);
222static GtkWidget* (*fp_gtk_radio_button_new)(GSList *group);
223static GtkWidget* (*fp_gtk_radio_menu_item_new)(GSList *group);
224static GtkWidget* (*fp_gtk_scrolled_window_new)(GtkAdjustment *hadjustment,
225        GtkAdjustment *vadjustment);
226static GtkWidget* (*fp_gtk_separator_menu_item_new)();
227static GtkWidget* (*fp_gtk_separator_tool_item_new)();
228static GtkWidget* (*fp_gtk_text_view_new)();
229static GtkWidget* (*fp_gtk_toggle_button_new)();
230static GtkWidget* (*fp_gtk_toolbar_new)();
231static GtkWidget* (*fp_gtk_tree_view_new)();
232static GtkWidget* (*fp_gtk_viewport_new)(GtkAdjustment *hadjustment,
233        GtkAdjustment *vadjustment);
234static GtkWidget* (*fp_gtk_window_new)(GtkWindowType type);
235static GtkWidget* (*fp_gtk_dialog_new)();
236static GtkWidget* (*fp_gtk_spin_button_new)(GtkAdjustment *adjustment,
237        gdouble climb_rate, guint digits);
238static GtkWidget* (*fp_gtk_frame_new)(const gchar *label);
239
240/* Other widget operations */
241static GtkObject* (*fp_gtk_adjustment_new)(gdouble value,
242        gdouble lower, gdouble upper, gdouble step_increment,
243        gdouble page_increment, gdouble page_size);
244static void (*fp_gtk_container_add)(GtkContainer *window, GtkWidget *widget);
245static void (*fp_gtk_menu_shell_append)(GtkMenuShell *menu_shell,
246        GtkWidget *child);
247static void (*fp_gtk_menu_item_set_submenu)(GtkMenuItem *menu_item,
248        GtkWidget *submenu);
249static void (*fp_gtk_widget_realize)(GtkWidget *widget);
250static GdkPixbuf* (*fp_gtk_widget_render_icon)(GtkWidget *widget,
251        const gchar *stock_id, GtkIconSize size, const gchar *detail);
252static void (*fp_gtk_widget_set_name)(GtkWidget *widget, const gchar *name);
253static void (*fp_gtk_widget_set_parent)(GtkWidget *widget, GtkWidget *parent);
254static void (*fp_gtk_widget_set_direction)(GtkWidget *widget,
255        GtkTextDirection direction);
256static void (*fp_gtk_widget_style_get)(GtkWidget *widget,
257        const gchar *first_property_name, ...);
258static void (*fp_gtk_widget_class_install_style_property)(
259        GtkWidgetClass* class, GParamSpec *pspec);
260static GParamSpec* (*fp_gtk_widget_class_find_style_property)(
261        GtkWidgetClass* class, const gchar* property_name);
262static void (*fp_gtk_widget_style_get_property)(GtkWidget* widget,
263        const gchar* property_name, GValue* value);
264static char* (*fp_pango_font_description_to_string)(
265        const PangoFontDescription* fd);
266static GtkSettings* (*fp_gtk_settings_get_default)();
267static GtkSettings* (*fp_gtk_widget_get_settings)(GtkWidget *widget);
268static GType        (*fp_gtk_border_get_type)();
269static void (*fp_gtk_arrow_set)(GtkWidget* arrow,
270                                GtkArrowType arrow_type,
271                                GtkShadowType shadow_type);
272static void (*fp_gtk_widget_size_request)(GtkWidget *widget,
273                                          GtkRequisition *requisition);
274static GtkAdjustment* (*fp_gtk_range_get_adjustment)(GtkRange* range);
275
276/* Method bodies */
277
278static void throw_exception(JNIEnv *env, const char* name, const char* message)
279{
280    jclass class = (*env)->FindClass(env, name);
281
282    if (class != NULL)
283        (*env)->ThrowNew(env, class, message);
284
285    (*env)->DeleteLocalRef(env, class);
286}
287
288/* This is a workaround for the bug:
289 * http://sourceware.org/bugzilla/show_bug.cgi?id=1814
290 * (dlsym/dlopen clears dlerror state)
291 * This bug is specific to Linux, but there is no harm in
292 * applying this workaround on Solaris as well.
293 */
294static void* dl_symbol(const char* name)
295{
296    void* result = dlsym(gtk2_libhandle, name);
297    if (!result)
298        longjmp(j, NO_SYMBOL_EXCEPTION);
299
300    return result;
301}
302
303static void* dl_symbol_gthread(const char* name)
304{
305    void* result = dlsym(gthread_libhandle, name);
306    if (!result)
307        longjmp(j, NO_SYMBOL_EXCEPTION);
308
309    return result;
310}
311
312gboolean gtk2_check(const char* lib_name, gboolean load)
313{
314    if (gtk2_libhandle != NULL) {
315        /* We've already successfully opened the GTK libs, so return true. */
316        return TRUE;
317    } else {
318        void *lib = NULL;
319
320#ifdef RTLD_NOLOAD
321        /* Just check if gtk libs are already in the process space */
322        lib = dlopen(lib_name, RTLD_LAZY | RTLD_NOLOAD);
323        if (!load || lib != NULL) {
324            return lib != NULL;
325        }
326#else
327#ifdef _AIX
328        /* On AIX we could implement this with the help of loadquery(L_GETINFO, ..)  */
329        /* (see reload_table() in hotspot/src/os/aix/vm/loadlib_aix.cpp) but it is   */
330        /* probably not worth it because most AIX servers don't have GTK libs anyway */
331#endif
332#endif
333
334        lib = dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL);
335        if (lib == NULL) {
336            return FALSE;
337        }
338
339        fp_gtk_check_version = dlsym(lib, "gtk_check_version");
340        /* Check for GTK 2.2+ */
341        if (!fp_gtk_check_version(2, 2, 0)) {
342            return TRUE;
343        }
344
345        // 8048289: workaround for https://bugzilla.gnome.org/show_bug.cgi?id=733065
346        // dlclose(lib);
347
348        return FALSE;
349    }
350}
351
352#define ADD_SUPPORTED_ACTION(actionStr) \
353do { \
354    jfieldID fld_action = (*env)->GetStaticFieldID(env, cls_action, actionStr, "Ljava/awt/Desktop$Action;"); \
355    if (!(*env)->ExceptionCheck(env)) { \
356        jobject action = (*env)->GetStaticObjectField(env, cls_action, fld_action); \
357        (*env)->CallBooleanMethod(env, supportedActions, mid_arrayListAdd, action); \
358    } else { \
359        (*env)->ExceptionClear(env); \
360    } \
361} while(0);
362
363
364static void update_supported_actions(JNIEnv *env) {
365    GVfs * (*fp_g_vfs_get_default) (void);
366    const gchar * const * (*fp_g_vfs_get_supported_uri_schemes) (GVfs * vfs);
367    const gchar * const * schemes = NULL;
368
369    jclass cls_action = (*env)->FindClass(env, "java/awt/Desktop$Action");
370    CHECK_NULL(cls_action);
371    jclass cls_xDesktopPeer = (*env)->FindClass(env, "sun/awt/X11/XDesktopPeer");
372    CHECK_NULL(cls_xDesktopPeer);
373    jfieldID fld_supportedActions = (*env)->GetStaticFieldID(env, cls_xDesktopPeer, "supportedActions", "Ljava/util/List;");
374    CHECK_NULL(fld_supportedActions);
375    jobject supportedActions = (*env)->GetStaticObjectField(env, cls_xDesktopPeer, fld_supportedActions);
376
377    jclass cls_arrayList = (*env)->FindClass(env, "java/util/ArrayList");
378    CHECK_NULL(cls_arrayList);
379    jmethodID mid_arrayListAdd = (*env)->GetMethodID(env, cls_arrayList, "add", "(Ljava/lang/Object;)Z");
380    CHECK_NULL(mid_arrayListAdd);
381    jmethodID mid_arrayListClear = (*env)->GetMethodID(env, cls_arrayList, "clear", "()V");
382    CHECK_NULL(mid_arrayListClear);
383
384    (*env)->CallVoidMethod(env, supportedActions, mid_arrayListClear);
385
386    ADD_SUPPORTED_ACTION("OPEN");
387
388    /**
389     * gtk_show_uri() documentation says:
390     *
391     * > you need to install gvfs to get support for uri schemes such as http://
392     * > or ftp://, as only local files are handled by GIO itself.
393     *
394     * So OPEN action was safely added here.
395     * However, it looks like Solaris 11 have gvfs support only for 32-bit
396     * applications only by default.
397     */
398
399    fp_g_vfs_get_default = dl_symbol("g_vfs_get_default");
400    fp_g_vfs_get_supported_uri_schemes = dl_symbol("g_vfs_get_supported_uri_schemes");
401    dlerror();
402
403    if (fp_g_vfs_get_default && fp_g_vfs_get_supported_uri_schemes) {
404        GVfs * vfs = fp_g_vfs_get_default();
405        schemes = vfs ? fp_g_vfs_get_supported_uri_schemes(vfs) : NULL;
406        if (schemes) {
407            int i = 0;
408            while (schemes[i]) {
409                if (strcmp(schemes[i], "http") == 0) {
410                    ADD_SUPPORTED_ACTION("BROWSE");
411                    ADD_SUPPORTED_ACTION("MAIL");
412                    break;
413                }
414                i++;
415            }
416        }
417    } else {
418#ifdef DEBUG
419        fprintf(stderr, "Cannot load g_vfs_get_supported_uri_schemes\n");
420#endif /* DEBUG */
421    }
422
423}
424/**
425 * Functions for awt_Desktop.c
426 */
427static gboolean gtk2_show_uri_load(JNIEnv *env) {
428     gboolean success = FALSE;
429     dlerror();
430     const char *gtk_version = fp_gtk_check_version(2, 14, 0);
431     if (gtk_version != NULL) {
432         // The gtk_show_uri is available from GTK+ 2.14
433#ifdef DEBUG
434         fprintf (stderr, "The version of GTK is %s. "
435             "The gtk_show_uri function is supported "
436             "since GTK+ 2.14.\n", gtk_version);
437#endif /* DEBUG */
438     } else {
439         // Loading symbols only if the GTK version is 2.14 and higher
440         fp_gtk_show_uri = dl_symbol("gtk_show_uri");
441         const char *dlsym_error = dlerror();
442         if (dlsym_error) {
443#ifdef DEBUG
444             fprintf (stderr, "Cannot load symbol: %s \n", dlsym_error);
445#endif /* DEBUG */
446         } else if (fp_gtk_show_uri == NULL) {
447#ifdef DEBUG
448             fprintf(stderr, "dlsym(gtk_show_uri) returned NULL\n");
449#endif /* DEBUG */
450        } else {
451            gtk->gtk_show_uri = fp_gtk_show_uri;
452            update_supported_actions(env);
453            success = TRUE;
454        }
455     }
456     return success;
457}
458
459/**
460 * Functions for sun_awt_X11_GtkFileDialogPeer.c
461 */
462static void gtk2_file_chooser_load()
463{
464    fp_gtk_file_chooser_get_filename = dl_symbol(
465            "gtk_file_chooser_get_filename");
466    fp_gtk_file_chooser_dialog_new = dl_symbol("gtk_file_chooser_dialog_new");
467    fp_gtk_file_chooser_set_current_folder = dl_symbol(
468            "gtk_file_chooser_set_current_folder");
469    fp_gtk_file_chooser_set_filename = dl_symbol(
470            "gtk_file_chooser_set_filename");
471    fp_gtk_file_chooser_set_current_name = dl_symbol(
472            "gtk_file_chooser_set_current_name");
473    fp_gtk_file_filter_add_custom = dl_symbol("gtk_file_filter_add_custom");
474    fp_gtk_file_chooser_set_filter = dl_symbol("gtk_file_chooser_set_filter");
475    fp_gtk_file_chooser_get_type = dl_symbol("gtk_file_chooser_get_type");
476    fp_gtk_file_filter_new = dl_symbol("gtk_file_filter_new");
477    if (fp_gtk_check_version(2, 8, 0) == NULL) {
478        fp_gtk_file_chooser_set_do_overwrite_confirmation = dl_symbol(
479                "gtk_file_chooser_set_do_overwrite_confirmation");
480    }
481    fp_gtk_file_chooser_set_select_multiple = dl_symbol(
482            "gtk_file_chooser_set_select_multiple");
483    fp_gtk_file_chooser_get_current_folder = dl_symbol(
484            "gtk_file_chooser_get_current_folder");
485    fp_gtk_file_chooser_get_filenames = dl_symbol(
486            "gtk_file_chooser_get_filenames");
487    fp_gtk_g_slist_length = dl_symbol("g_slist_length");
488    fp_gdk_x11_drawable_get_xid = dl_symbol("gdk_x11_drawable_get_xid");
489}
490
491GtkApi* gtk2_load(JNIEnv *env, const char* lib_name)
492{
493    gboolean result;
494    int i;
495    int (*handler)();
496    int (*io_handler)();
497    char *gtk_modules_env;
498
499    gtk2_libhandle = dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL);
500    if (gtk2_libhandle == NULL) {
501        return FALSE;
502    }
503
504    gthread_libhandle = dlopen(GTHREAD_LIB_VERSIONED, RTLD_LAZY | RTLD_LOCAL);
505    if (gthread_libhandle == NULL) {
506        gthread_libhandle = dlopen(GTHREAD_LIB, RTLD_LAZY | RTLD_LOCAL);
507        if (gthread_libhandle == NULL)
508            return FALSE;
509    }
510
511    if (setjmp(j) == 0)
512    {
513        fp_gtk_check_version = dl_symbol("gtk_check_version");
514        /* Check for GTK 2.2+ */
515        if (fp_gtk_check_version(2, 2, 0)) {
516            longjmp(j, NO_SYMBOL_EXCEPTION);
517        }
518
519        /* GLib */
520        fp_glib_check_version = dlsym(gtk2_libhandle, "glib_check_version");
521        if (!fp_glib_check_version) {
522            dlerror();
523        }
524        fp_g_free = dl_symbol("g_free");
525        fp_g_object_unref = dl_symbol("g_object_unref");
526
527        fp_g_main_context_iteration =
528            dl_symbol("g_main_context_iteration");
529
530        fp_g_value_init = dl_symbol("g_value_init");
531        fp_g_type_is_a = dl_symbol("g_type_is_a");
532
533        fp_g_value_get_boolean = dl_symbol("g_value_get_boolean");
534        fp_g_value_get_char = dl_symbol("g_value_get_char");
535        fp_g_value_get_uchar = dl_symbol("g_value_get_uchar");
536        fp_g_value_get_int = dl_symbol("g_value_get_int");
537        fp_g_value_get_uint = dl_symbol("g_value_get_uint");
538        fp_g_value_get_long = dl_symbol("g_value_get_long");
539        fp_g_value_get_ulong = dl_symbol("g_value_get_ulong");
540        fp_g_value_get_int64 = dl_symbol("g_value_get_int64");
541        fp_g_value_get_uint64 = dl_symbol("g_value_get_uint64");
542        fp_g_value_get_float = dl_symbol("g_value_get_float");
543        fp_g_value_get_double = dl_symbol("g_value_get_double");
544        fp_g_value_get_string = dl_symbol("g_value_get_string");
545        fp_g_value_get_enum = dl_symbol("g_value_get_enum");
546        fp_g_value_get_flags = dl_symbol("g_value_get_flags");
547        fp_g_value_get_param = dl_symbol("g_value_get_param");
548        fp_g_value_get_boxed = dl_symbol("g_value_get_boxed");
549        fp_g_value_get_pointer = dl_symbol("g_value_get_pointer");
550        fp_g_value_get_object = dl_symbol("g_value_get_object");
551        fp_g_param_spec_int = dl_symbol("g_param_spec_int");
552        fp_g_object_get = dl_symbol("g_object_get");
553        fp_g_object_set = dl_symbol("g_object_set");
554
555        /* GDK */
556        fp_gdk_get_default_root_window =
557            dl_symbol("gdk_get_default_root_window");
558        fp_gdk_pixmap_new = dl_symbol("gdk_pixmap_new");
559        fp_gdk_pixbuf_get_from_drawable =
560            dl_symbol("gdk_pixbuf_get_from_drawable");
561        fp_gdk_pixbuf_scale_simple =
562            dl_symbol("gdk_pixbuf_scale_simple");
563        fp_gdk_gc_new = dl_symbol("gdk_gc_new");
564        fp_gdk_rgb_gc_set_foreground =
565            dl_symbol("gdk_rgb_gc_set_foreground");
566        fp_gdk_draw_rectangle = dl_symbol("gdk_draw_rectangle");
567        fp_gdk_drawable_get_size = dl_symbol("gdk_drawable_get_size");
568
569        /* Pixbuf */
570        fp_gdk_pixbuf_new = dl_symbol("gdk_pixbuf_new");
571        fp_gdk_pixbuf_new_from_file =
572                dl_symbol("gdk_pixbuf_new_from_file");
573        fp_gdk_pixbuf_get_width = dl_symbol("gdk_pixbuf_get_width");
574        fp_gdk_pixbuf_get_height = dl_symbol("gdk_pixbuf_get_height");
575        fp_gdk_pixbuf_get_pixels = dl_symbol("gdk_pixbuf_get_pixels");
576        fp_gdk_pixbuf_get_rowstride =
577                dl_symbol("gdk_pixbuf_get_rowstride");
578        fp_gdk_pixbuf_get_has_alpha =
579                dl_symbol("gdk_pixbuf_get_has_alpha");
580        fp_gdk_pixbuf_get_bits_per_sample =
581                dl_symbol("gdk_pixbuf_get_bits_per_sample");
582        fp_gdk_pixbuf_get_n_channels =
583                dl_symbol("gdk_pixbuf_get_n_channels");
584        fp_gdk_pixbuf_get_colorspace =
585                dl_symbol("gdk_pixbuf_get_colorspace");
586
587        /* GTK painting */
588        fp_gtk_init_check = dl_symbol("gtk_init_check");
589        fp_gtk_paint_hline = dl_symbol("gtk_paint_hline");
590        fp_gtk_paint_vline = dl_symbol("gtk_paint_vline");
591        fp_gtk_paint_shadow = dl_symbol("gtk_paint_shadow");
592        fp_gtk_paint_arrow = dl_symbol("gtk_paint_arrow");
593        fp_gtk_paint_diamond = dl_symbol("gtk_paint_diamond");
594        fp_gtk_paint_box = dl_symbol("gtk_paint_box");
595        fp_gtk_paint_flat_box = dl_symbol("gtk_paint_flat_box");
596        fp_gtk_paint_check = dl_symbol("gtk_paint_check");
597        fp_gtk_paint_option = dl_symbol("gtk_paint_option");
598        fp_gtk_paint_box_gap = dl_symbol("gtk_paint_box_gap");
599        fp_gtk_paint_extension = dl_symbol("gtk_paint_extension");
600        fp_gtk_paint_focus = dl_symbol("gtk_paint_focus");
601        fp_gtk_paint_slider = dl_symbol("gtk_paint_slider");
602        fp_gtk_paint_handle = dl_symbol("gtk_paint_handle");
603        fp_gtk_paint_expander = dl_symbol("gtk_paint_expander");
604        fp_gtk_style_apply_default_background =
605                dl_symbol("gtk_style_apply_default_background");
606
607        /* GTK widgets */
608        fp_gtk_arrow_new = dl_symbol("gtk_arrow_new");
609        fp_gtk_button_new = dl_symbol("gtk_button_new");
610        fp_gtk_spin_button_new = dl_symbol("gtk_spin_button_new");
611        fp_gtk_check_button_new = dl_symbol("gtk_check_button_new");
612        fp_gtk_check_menu_item_new =
613                dl_symbol("gtk_check_menu_item_new");
614        fp_gtk_color_selection_dialog_new =
615                dl_symbol("gtk_color_selection_dialog_new");
616        fp_gtk_entry_new = dl_symbol("gtk_entry_new");
617        fp_gtk_fixed_new = dl_symbol("gtk_fixed_new");
618        fp_gtk_handle_box_new = dl_symbol("gtk_handle_box_new");
619        fp_gtk_image_new = dl_symbol("gtk_image_new");
620        fp_gtk_hpaned_new = dl_symbol("gtk_hpaned_new");
621        fp_gtk_vpaned_new = dl_symbol("gtk_vpaned_new");
622        fp_gtk_hscale_new = dl_symbol("gtk_hscale_new");
623        fp_gtk_vscale_new = dl_symbol("gtk_vscale_new");
624        fp_gtk_hscrollbar_new = dl_symbol("gtk_hscrollbar_new");
625        fp_gtk_vscrollbar_new = dl_symbol("gtk_vscrollbar_new");
626        fp_gtk_hseparator_new = dl_symbol("gtk_hseparator_new");
627        fp_gtk_vseparator_new = dl_symbol("gtk_vseparator_new");
628        fp_gtk_label_new = dl_symbol("gtk_label_new");
629        fp_gtk_menu_new = dl_symbol("gtk_menu_new");
630        fp_gtk_menu_bar_new = dl_symbol("gtk_menu_bar_new");
631        fp_gtk_menu_item_new = dl_symbol("gtk_menu_item_new");
632        fp_gtk_menu_item_set_submenu =
633                dl_symbol("gtk_menu_item_set_submenu");
634        fp_gtk_notebook_new = dl_symbol("gtk_notebook_new");
635        fp_gtk_progress_bar_new =
636            dl_symbol("gtk_progress_bar_new");
637        fp_gtk_progress_bar_set_orientation =
638            dl_symbol("gtk_progress_bar_set_orientation");
639        fp_gtk_radio_button_new =
640            dl_symbol("gtk_radio_button_new");
641        fp_gtk_radio_menu_item_new =
642            dl_symbol("gtk_radio_menu_item_new");
643        fp_gtk_scrolled_window_new =
644            dl_symbol("gtk_scrolled_window_new");
645        fp_gtk_separator_menu_item_new =
646            dl_symbol("gtk_separator_menu_item_new");
647        fp_gtk_text_view_new = dl_symbol("gtk_text_view_new");
648        fp_gtk_toggle_button_new =
649            dl_symbol("gtk_toggle_button_new");
650        fp_gtk_toolbar_new = dl_symbol("gtk_toolbar_new");
651        fp_gtk_tree_view_new = dl_symbol("gtk_tree_view_new");
652        fp_gtk_viewport_new = dl_symbol("gtk_viewport_new");
653        fp_gtk_window_new = dl_symbol("gtk_window_new");
654        fp_gtk_window_present = dl_symbol("gtk_window_present");
655        fp_gtk_window_move = dl_symbol("gtk_window_move");
656        fp_gtk_window_resize = dl_symbol("gtk_window_resize");
657
658          fp_gtk_dialog_new = dl_symbol("gtk_dialog_new");
659        fp_gtk_frame_new = dl_symbol("gtk_frame_new");
660
661        fp_gtk_adjustment_new = dl_symbol("gtk_adjustment_new");
662        fp_gtk_container_add = dl_symbol("gtk_container_add");
663        fp_gtk_menu_shell_append =
664            dl_symbol("gtk_menu_shell_append");
665        fp_gtk_widget_realize = dl_symbol("gtk_widget_realize");
666        fp_gtk_widget_destroy = dl_symbol("gtk_widget_destroy");
667        fp_gtk_widget_render_icon =
668            dl_symbol("gtk_widget_render_icon");
669        fp_gtk_widget_set_name =
670            dl_symbol("gtk_widget_set_name");
671        fp_gtk_widget_set_parent =
672            dl_symbol("gtk_widget_set_parent");
673        fp_gtk_widget_set_direction =
674            dl_symbol("gtk_widget_set_direction");
675        fp_gtk_widget_style_get =
676            dl_symbol("gtk_widget_style_get");
677        fp_gtk_widget_class_install_style_property =
678            dl_symbol("gtk_widget_class_install_style_property");
679        fp_gtk_widget_class_find_style_property =
680            dl_symbol("gtk_widget_class_find_style_property");
681        fp_gtk_widget_style_get_property =
682            dl_symbol("gtk_widget_style_get_property");
683        fp_pango_font_description_to_string =
684            dl_symbol("pango_font_description_to_string");
685        fp_gtk_settings_get_default =
686            dl_symbol("gtk_settings_get_default");
687        fp_gtk_widget_get_settings =
688            dl_symbol("gtk_widget_get_settings");
689        fp_gtk_border_get_type =  dl_symbol("gtk_border_get_type");
690        fp_gtk_arrow_set = dl_symbol("gtk_arrow_set");
691        fp_gtk_widget_size_request =
692            dl_symbol("gtk_widget_size_request");
693        fp_gtk_range_get_adjustment =
694            dl_symbol("gtk_range_get_adjustment");
695
696        fp_gtk_widget_hide = dl_symbol("gtk_widget_hide");
697        fp_gtk_main_quit = dl_symbol("gtk_main_quit");
698        fp_g_signal_connect_data = dl_symbol("g_signal_connect_data");
699        fp_gtk_widget_show = dl_symbol("gtk_widget_show");
700        fp_gtk_main = dl_symbol("gtk_main");
701
702        fp_g_path_get_dirname = dl_symbol("g_path_get_dirname");
703
704        /**
705         * GLib thread system
706         */
707        if (GLIB_CHECK_VERSION(2, 20, 0)) {
708            fp_g_thread_get_initialized = dl_symbol_gthread("g_thread_get_initialized");
709        }
710        fp_g_thread_init = dl_symbol_gthread("g_thread_init");
711        fp_gdk_threads_init = dl_symbol("gdk_threads_init");
712        fp_gdk_threads_enter = dl_symbol("gdk_threads_enter");
713        fp_gdk_threads_leave = dl_symbol("gdk_threads_leave");
714
715        /**
716         * Functions for sun_awt_X11_GtkFileDialogPeer.c
717         */
718        if (fp_gtk_check_version(2, 4, 0) == NULL) {
719            // The current GtkFileChooser is available from GTK+ 2.4
720            gtk2_file_chooser_load();
721        }
722
723        /* Some functions may be missing in pre-2.4 GTK.
724           We handle them specially here.
725         */
726        fp_gtk_combo_box_new = dlsym(gtk2_libhandle, "gtk_combo_box_new");
727        if (fp_gtk_combo_box_new == NULL) {
728            fp_gtk_combo_box_new = dl_symbol("gtk_combo_new");
729        }
730
731        fp_gtk_combo_box_entry_new =
732            dlsym(gtk2_libhandle, "gtk_combo_box_entry_new");
733        if (fp_gtk_combo_box_entry_new == NULL) {
734            fp_gtk_combo_box_entry_new = dl_symbol("gtk_combo_new");
735            new_combo = FALSE;
736        }
737
738        fp_gtk_separator_tool_item_new =
739            dlsym(gtk2_libhandle, "gtk_separator_tool_item_new");
740        if (fp_gtk_separator_tool_item_new == NULL) {
741            fp_gtk_separator_tool_item_new =
742                dl_symbol("gtk_vseparator_new");
743        }
744
745        fp_g_list_append = dl_symbol("g_list_append");
746        fp_g_list_free = dl_symbol("g_list_free");
747        fp_g_list_free_full = dl_symbol("g_list_free_full");
748    }
749    /* Now we have only one kind of exceptions: NO_SYMBOL_EXCEPTION
750     * Otherwise we can check the return value of setjmp method.
751     */
752    else
753    {
754        dlclose(gtk2_libhandle);
755        gtk2_libhandle = NULL;
756
757        dlclose(gthread_libhandle);
758        gthread_libhandle = NULL;
759
760        return FALSE;
761    }
762
763    /*
764     * Strip the AT-SPI GTK_MODULEs if present
765     */
766    gtk_modules_env = getenv ("GTK_MODULES");
767
768    if (gtk_modules_env && strstr (gtk_modules_env, "atk-bridge") ||
769        gtk_modules_env && strstr (gtk_modules_env, "gail"))
770    {
771        /* the new env will be smaller than the old one */
772        gchar *s, *new_env = SAFE_SIZE_STRUCT_ALLOC(malloc,
773                sizeof(ENV_PREFIX), 1, strlen (gtk_modules_env));
774
775        if (new_env != NULL )
776        {
777            /* careful, strtok modifies its args */
778            gchar *tmp_env = strdup (gtk_modules_env);
779            strcpy(new_env, ENV_PREFIX);
780
781            /* strip out 'atk-bridge' and 'gail' */
782            size_t PREFIX_LENGTH = strlen(ENV_PREFIX);
783            while (s = strtok(tmp_env, ":"))
784            {
785                if ((!strstr (s, "atk-bridge")) && (!strstr (s, "gail")))
786                {
787                    if (strlen (new_env) > PREFIX_LENGTH) {
788                        new_env = strcat (new_env, ":");
789                    }
790                    new_env = strcat(new_env, s);
791                }
792                if (tmp_env)
793                {
794                    free (tmp_env);
795                    tmp_env = NULL; /* next call to strtok arg1==NULL */
796                }
797            }
798            putenv (new_env);
799            free (new_env);
800            free (tmp_env);
801        }
802    }
803
804    /*
805     * GTK should be initialized with gtk_init_check() before use.
806     *
807     * gtk_init_check installs its own error handlers. It is critical that
808     * we preserve error handler set from AWT. Otherwise we'll crash on
809     * BadMatch errors which we would normally ignore. The IO error handler
810     * is preserved here, too, just for consistency.
811    */
812    AWT_LOCK();
813    handler = XSetErrorHandler(NULL);
814    io_handler = XSetIOErrorHandler(NULL);
815
816    if (fp_gtk_check_version(2, 2, 0) == NULL) {
817
818        // Calling g_thread_init() multiple times leads to crash on GLib < 2.24
819        // We can use g_thread_get_initialized () but it is available only for
820        // GLib >= 2.20.
821        gboolean is_g_thread_get_initialized = FALSE;
822        if (GLIB_CHECK_VERSION(2, 20, 0)) {
823            is_g_thread_get_initialized = fp_g_thread_get_initialized();
824        }
825
826        if (!is_g_thread_get_initialized) {
827            fp_g_thread_init(NULL);
828        }
829
830        //According the GTK documentation, gdk_threads_init() should be
831        //called before gtk_init() or gtk_init_check()
832        fp_gdk_threads_init();
833    }
834    result = (*fp_gtk_init_check)(NULL, NULL);
835
836    XSetErrorHandler(handler);
837    XSetIOErrorHandler(io_handler);
838    AWT_UNLOCK();
839
840    /* Initialize widget array. */
841    for (i = 0; i < _GTK_WIDGET_TYPE_SIZE; i++)
842    {
843        gtk2_widgets[i] = NULL;
844    }
845    if (result) {
846        GtkApi* gtk = (GtkApi*)malloc(sizeof(GtkApi));
847        gtk2_init(gtk);
848        return gtk;
849    }
850    return NULL;
851}
852
853int gtk2_unload()
854{
855    int i;
856    char *gtk2_error;
857
858    if (!gtk2_libhandle)
859        return TRUE;
860
861    /* Release painting objects */
862    if (gtk2_white_pixmap != NULL) {
863        (*fp_g_object_unref)(gtk2_white_pixmap);
864        (*fp_g_object_unref)(gtk2_black_pixmap);
865        (*fp_g_object_unref)(gtk2_white_pixbuf);
866        (*fp_g_object_unref)(gtk2_black_pixbuf);
867        gtk2_white_pixmap = gtk2_black_pixmap =
868            gtk2_white_pixbuf = gtk2_black_pixbuf = NULL;
869    }
870    gtk2_pixbuf_width = 0;
871    gtk2_pixbuf_height = 0;
872
873    if (gtk2_window != NULL) {
874        /* Destroying toplevel widget will destroy all contained widgets */
875        (*fp_gtk_widget_destroy)(gtk2_window);
876
877        /* Unset some static data so they get reinitialized on next load */
878        gtk2_window = NULL;
879    }
880
881    dlerror();
882    dlclose(gtk2_libhandle);
883    dlclose(gthread_libhandle);
884    if ((gtk2_error = dlerror()) != NULL)
885    {
886        return FALSE;
887    }
888    return TRUE;
889}
890
891/* Dispatch all pending events from the GTK event loop.
892 * This is needed to catch theme change and update widgets' style.
893 */
894static void flush_gtk_event_loop()
895{
896    while( (*fp_g_main_context_iteration)(NULL, FALSE));
897}
898
899/*
900 * Initialize components of containment hierarchy. This creates a GtkFixed
901 * inside a GtkWindow. All widgets get realized.
902 */
903static void init_containers()
904{
905    if (gtk2_window == NULL)
906    {
907        gtk2_window = (*fp_gtk_window_new)(GTK_WINDOW_TOPLEVEL);
908        gtk2_fixed = (GtkFixed *)(*fp_gtk_fixed_new)();
909        (*fp_gtk_container_add)((GtkContainer*)gtk2_window,
910                                (GtkWidget *)gtk2_fixed);
911        (*fp_gtk_widget_realize)(gtk2_window);
912        (*fp_gtk_widget_realize)((GtkWidget *)gtk2_fixed);
913    }
914}
915
916/*
917 * Ensure everything is ready for drawing an element of the specified width
918 * and height.
919 *
920 * We should somehow handle translucent images. GTK can draw to X Drawables
921 * only, which don't support alpha. When we retrieve the image back from
922 * the server, translucency information is lost. There're several ways to
923 * work around this:
924 * 1) Subclass GdkPixmap and cache translucent objects on client side. This
925 * requires us to implement parts of X server drawing logic on client side.
926 * Many X requests can potentially be "translucent"; e.g. XDrawLine with
927 * fill=tile and a translucent tile is a "translucent" operation, whereas
928 * XDrawLine with fill=solid is an "opaque" one. Moreover themes can (and some
929 * do) intermix transparent and opaque operations which makes caching even
930 * more problematic.
931 * 2) Use Xorg 32bit ARGB visual when available. GDK has no native support
932 * for it (as of version 2.6). Also even in JDS 3 Xorg does not support
933 * these visuals by default, which makes optimizing for them pointless.
934 * We can consider doing this at a later point when ARGB visuals become more
935 * popular.
936 * 3') GTK has plans to use Cairo as its graphical backend (presumably in
937 * 2.8), and Cairo supports alpha. With it we could also get rid of the
938 * unnecessary round trip to server and do all the drawing on client side.
939 * 4) For now we draw to two different pixmaps and restore alpha channel by
940 * comparing results. This can be optimized by using subclassed pixmap and
941 * doing the second drawing only if necessary.
942*/
943static void gtk2_init_painting(JNIEnv *env, gint width, gint height)
944{
945    GdkGC *gc;
946    GdkPixbuf *white, *black;
947
948    init_containers();
949
950    if (gtk2_pixbuf_width < width || gtk2_pixbuf_height < height)
951    {
952        white = (*fp_gdk_pixbuf_new)(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
953        black = (*fp_gdk_pixbuf_new)(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
954
955        if (white == NULL || black == NULL)
956        {
957            snprintf(convertionBuffer, CONV_BUFFER_SIZE, "Couldn't create pixbuf of size %dx%d", width, height);
958            throw_exception(env, "java/lang/RuntimeException", convertionBuffer);
959            fp_gdk_threads_leave();
960            return;
961        }
962
963        if (gtk2_white_pixmap != NULL) {
964            /* free old stuff */
965            (*fp_g_object_unref)(gtk2_white_pixmap);
966            (*fp_g_object_unref)(gtk2_black_pixmap);
967            (*fp_g_object_unref)(gtk2_white_pixbuf);
968            (*fp_g_object_unref)(gtk2_black_pixbuf);
969        }
970
971        gtk2_white_pixmap = (*fp_gdk_pixmap_new)(gtk2_window->window, width, height, -1);
972        gtk2_black_pixmap = (*fp_gdk_pixmap_new)(gtk2_window->window, width, height, -1);
973
974        gtk2_white_pixbuf = white;
975        gtk2_black_pixbuf = black;
976
977        gtk2_pixbuf_width = width;
978        gtk2_pixbuf_height = height;
979    }
980
981    /* clear the pixmaps */
982    gc = (*fp_gdk_gc_new)(gtk2_white_pixmap);
983    (*fp_gdk_rgb_gc_set_foreground)(gc, 0xffffff);
984    (*fp_gdk_draw_rectangle)(gtk2_white_pixmap, gc, TRUE, 0, 0, width, height);
985    (*fp_g_object_unref)(gc);
986
987    gc = (*fp_gdk_gc_new)(gtk2_black_pixmap);
988    (*fp_gdk_rgb_gc_set_foreground)(gc, 0x000000);
989    (*fp_gdk_draw_rectangle)(gtk2_black_pixmap, gc, TRUE, 0, 0, width, height);
990    (*fp_g_object_unref)(gc);
991}
992
993/*
994 * Restore image from white and black pixmaps and copy it into destination
995 * buffer. This method compares two pixbufs taken from white and black
996 * pixmaps and decodes color and alpha components. Pixbufs are RGB without
997 * alpha, destination buffer is ABGR.
998 *
999 * The return value is the transparency type of the resulting image, either
1000 * one of java_awt_Transparency_OPAQUE, java_awt_Transparency_BITMASK, and
1001 * java_awt_Transparency_TRANSLUCENT.
1002 */
1003static gint gtk2_copy_image(gint *dst, gint width, gint height)
1004{
1005    gint i, j, r, g, b;
1006    guchar *white, *black;
1007    gint stride, padding;
1008    gboolean is_opaque = TRUE;
1009    gboolean is_bitmask = TRUE;
1010
1011    (*fp_gdk_pixbuf_get_from_drawable)(gtk2_white_pixbuf, gtk2_white_pixmap,
1012            NULL, 0, 0, 0, 0, width, height);
1013    (*fp_gdk_pixbuf_get_from_drawable)(gtk2_black_pixbuf, gtk2_black_pixmap,
1014            NULL, 0, 0, 0, 0, width, height);
1015
1016    white = (*fp_gdk_pixbuf_get_pixels)(gtk2_white_pixbuf);
1017    black = (*fp_gdk_pixbuf_get_pixels)(gtk2_black_pixbuf);
1018    stride = (*fp_gdk_pixbuf_get_rowstride)(gtk2_black_pixbuf);
1019    padding = stride - width * 4;
1020
1021    for (i = 0; i < height; i++) {
1022        for (j = 0; j < width; j++) {
1023            int r1 = *white++;
1024            int r2 = *black++;
1025            int alpha = 0xff + r2 - r1;
1026
1027            switch (alpha) {
1028                case 0:       /* transparent pixel */
1029                    r = g = b = 0;
1030                    black += 3;
1031                    white += 3;
1032                    is_opaque = FALSE;
1033                    break;
1034
1035                case 0xff:    /* opaque pixel */
1036                    r = r2;
1037                    g = *black++;
1038                    b = *black++;
1039                    black++;
1040                    white += 3;
1041                    break;
1042
1043                default:      /* translucent pixel */
1044                    r = 0xff * r2 / alpha;
1045                    g = 0xff * *black++ / alpha;
1046                    b = 0xff * *black++ / alpha;
1047                    black++;
1048                    white += 3;
1049                    is_opaque = FALSE;
1050                    is_bitmask = FALSE;
1051                    break;
1052            }
1053
1054            *dst++ = (alpha << 24 | r << 16 | g << 8 | b);
1055        }
1056
1057        white += padding;
1058        black += padding;
1059    }
1060    return is_opaque ? java_awt_Transparency_OPAQUE :
1061                       (is_bitmask ? java_awt_Transparency_BITMASK :
1062                                     java_awt_Transparency_TRANSLUCENT);
1063}
1064
1065static void
1066gtk2_set_direction(GtkWidget *widget, GtkTextDirection dir)
1067{
1068    /*
1069     * Some engines (inexplicably) look at the direction of the widget's
1070     * parent, so we need to set the direction of both the widget and its
1071     * parent.
1072     */
1073    (*fp_gtk_widget_set_direction)(widget, dir);
1074    if (widget->parent != NULL) {
1075        (*fp_gtk_widget_set_direction)(widget->parent, dir);
1076    }
1077}
1078
1079/*
1080 * Initializes the widget to correct state for some engines.
1081 * This is a pure empirical method.
1082 */
1083static void init_toggle_widget(WidgetType widget_type, gint synth_state)
1084{
1085    gboolean is_active = ((synth_state & SELECTED) != 0);
1086
1087    if (widget_type == RADIO_BUTTON ||
1088        widget_type == CHECK_BOX ||
1089        widget_type == TOGGLE_BUTTON) {
1090        ((GtkToggleButton*)gtk2_widget)->active = is_active;
1091    }
1092
1093    if ((synth_state & FOCUSED) != 0) {
1094        ((GtkObject*)gtk2_widget)->flags |= GTK_HAS_FOCUS;
1095    } else {
1096        ((GtkObject*)gtk2_widget)->flags &= ~GTK_HAS_FOCUS;
1097    }
1098
1099    if ((synth_state & MOUSE_OVER) != 0 && (synth_state & PRESSED) == 0 ||
1100           (synth_state & FOCUSED) != 0 && (synth_state & PRESSED) != 0) {
1101        gtk2_widget->state = GTK_STATE_PRELIGHT;
1102    } else if ((synth_state & DISABLED) != 0) {
1103        gtk2_widget->state = GTK_STATE_INSENSITIVE;
1104    } else {
1105        gtk2_widget->state = is_active ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL;
1106    }
1107}
1108
1109/* GTK state_type filter */
1110static GtkStateType get_gtk_state_type(WidgetType widget_type, gint synth_state)
1111{
1112    GtkStateType result = GTK_STATE_NORMAL;
1113
1114    if ((synth_state & DISABLED) != 0) {
1115        result = GTK_STATE_INSENSITIVE;
1116    } else if ((synth_state & PRESSED) != 0) {
1117        result = GTK_STATE_ACTIVE;
1118    } else if ((synth_state & MOUSE_OVER) != 0) {
1119        result = GTK_STATE_PRELIGHT;
1120    }
1121    return result;
1122}
1123
1124/* GTK shadow_type filter */
1125static GtkShadowType get_gtk_shadow_type(WidgetType widget_type, gint synth_state)
1126{
1127    GtkShadowType result = GTK_SHADOW_OUT;
1128
1129    if ((synth_state & SELECTED) != 0) {
1130        result = GTK_SHADOW_IN;
1131    }
1132    return result;
1133}
1134
1135
1136static GtkWidget* gtk2_get_arrow(GtkArrowType arrow_type, GtkShadowType shadow_type)
1137{
1138    GtkWidget *arrow = NULL;
1139    if (NULL == gtk2_widgets[_GTK_ARROW_TYPE])
1140    {
1141        gtk2_widgets[_GTK_ARROW_TYPE] = (*fp_gtk_arrow_new)(arrow_type, shadow_type);
1142        (*fp_gtk_container_add)((GtkContainer *)gtk2_fixed, gtk2_widgets[_GTK_ARROW_TYPE]);
1143        (*fp_gtk_widget_realize)(gtk2_widgets[_GTK_ARROW_TYPE]);
1144    }
1145    arrow = gtk2_widgets[_GTK_ARROW_TYPE];
1146
1147    (*fp_gtk_arrow_set)(arrow, arrow_type, shadow_type);
1148    return arrow;
1149}
1150
1151static GtkAdjustment* create_adjustment()
1152{
1153    return (GtkAdjustment *)
1154            (*fp_gtk_adjustment_new)(50.0, 0.0, 100.0, 10.0, 20.0, 20.0);
1155}
1156
1157/**
1158 * Returns a pointer to the cached native widget for the specified widget
1159 * type.
1160 */
1161static GtkWidget *gtk2_get_widget(WidgetType widget_type)
1162{
1163    gboolean init_result = FALSE;
1164    GtkWidget *result = NULL;
1165    switch (widget_type)
1166    {
1167        case BUTTON:
1168        case TABLE_HEADER:
1169            if (init_result = (NULL == gtk2_widgets[_GTK_BUTTON_TYPE]))
1170            {
1171                gtk2_widgets[_GTK_BUTTON_TYPE] = (*fp_gtk_button_new)();
1172            }
1173            result = gtk2_widgets[_GTK_BUTTON_TYPE];
1174            break;
1175        case CHECK_BOX:
1176            if (init_result = (NULL == gtk2_widgets[_GTK_CHECK_BUTTON_TYPE]))
1177            {
1178                gtk2_widgets[_GTK_CHECK_BUTTON_TYPE] =
1179                    (*fp_gtk_check_button_new)();
1180            }
1181            result = gtk2_widgets[_GTK_CHECK_BUTTON_TYPE];
1182            break;
1183        case CHECK_BOX_MENU_ITEM:
1184            if (init_result = (NULL == gtk2_widgets[_GTK_CHECK_MENU_ITEM_TYPE]))
1185            {
1186                gtk2_widgets[_GTK_CHECK_MENU_ITEM_TYPE] =
1187                    (*fp_gtk_check_menu_item_new)();
1188            }
1189            result = gtk2_widgets[_GTK_CHECK_MENU_ITEM_TYPE];
1190            break;
1191        /************************************************************
1192         *    Creation a dedicated color chooser is dangerous because
1193         * it deadlocks the EDT
1194         ************************************************************/
1195/*        case COLOR_CHOOSER:
1196            if (init_result =
1197                    (NULL == gtk2_widgets[_GTK_COLOR_SELECTION_DIALOG_TYPE]))
1198            {
1199                gtk2_widgets[_GTK_COLOR_SELECTION_DIALOG_TYPE] =
1200                    (*fp_gtk_color_selection_dialog_new)(NULL);
1201            }
1202            result = gtk2_widgets[_GTK_COLOR_SELECTION_DIALOG_TYPE];
1203            break;*/
1204        case COMBO_BOX:
1205            if (init_result = (NULL == gtk2_widgets[_GTK_COMBO_BOX_TYPE]))
1206            {
1207                gtk2_widgets[_GTK_COMBO_BOX_TYPE] =
1208                    (*fp_gtk_combo_box_new)();
1209            }
1210            result = gtk2_widgets[_GTK_COMBO_BOX_TYPE];
1211            break;
1212        case COMBO_BOX_ARROW_BUTTON:
1213            if (init_result =
1214                    (NULL == gtk2_widgets[_GTK_COMBO_BOX_ARROW_BUTTON_TYPE]))
1215            {
1216                gtk2_widgets[_GTK_COMBO_BOX_ARROW_BUTTON_TYPE] =
1217                     (*fp_gtk_toggle_button_new)();
1218            }
1219            result = gtk2_widgets[_GTK_COMBO_BOX_ARROW_BUTTON_TYPE];
1220            break;
1221        case COMBO_BOX_TEXT_FIELD:
1222            if (init_result =
1223                    (NULL == gtk2_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE]))
1224            {
1225                result = gtk2_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE] =
1226                     (*fp_gtk_entry_new)();
1227            }
1228            result = gtk2_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE];
1229            break;
1230        case DESKTOP_ICON:
1231        case INTERNAL_FRAME_TITLE_PANE:
1232        case LABEL:
1233            if (init_result = (NULL == gtk2_widgets[_GTK_LABEL_TYPE]))
1234            {
1235                gtk2_widgets[_GTK_LABEL_TYPE] =
1236                    (*fp_gtk_label_new)(NULL);
1237            }
1238            result = gtk2_widgets[_GTK_LABEL_TYPE];
1239            break;
1240        case DESKTOP_PANE:
1241        case PANEL:
1242        case ROOT_PANE:
1243            if (init_result = (NULL == gtk2_widgets[_GTK_CONTAINER_TYPE]))
1244            {
1245                /* There is no constructor for a container type.  I've
1246                 * chosen GtkFixed container since it has a default
1247                 * constructor.
1248                 */
1249                gtk2_widgets[_GTK_CONTAINER_TYPE] =
1250                    (*fp_gtk_fixed_new)();
1251            }
1252            result = gtk2_widgets[_GTK_CONTAINER_TYPE];
1253            break;
1254        case EDITOR_PANE:
1255        case TEXT_AREA:
1256        case TEXT_PANE:
1257            if (init_result = (NULL == gtk2_widgets[_GTK_TEXT_VIEW_TYPE]))
1258            {
1259                gtk2_widgets[_GTK_TEXT_VIEW_TYPE] =
1260                    (*fp_gtk_text_view_new)();
1261            }
1262            result = gtk2_widgets[_GTK_TEXT_VIEW_TYPE];
1263            break;
1264        case FORMATTED_TEXT_FIELD:
1265        case PASSWORD_FIELD:
1266        case TEXT_FIELD:
1267            if (init_result = (NULL == gtk2_widgets[_GTK_ENTRY_TYPE]))
1268            {
1269                gtk2_widgets[_GTK_ENTRY_TYPE] =
1270                    (*fp_gtk_entry_new)();
1271            }
1272            result = gtk2_widgets[_GTK_ENTRY_TYPE];
1273            break;
1274        case HANDLE_BOX:
1275            if (init_result = (NULL == gtk2_widgets[_GTK_HANDLE_BOX_TYPE]))
1276            {
1277                gtk2_widgets[_GTK_HANDLE_BOX_TYPE] =
1278                    (*fp_gtk_handle_box_new)();
1279            }
1280            result = gtk2_widgets[_GTK_HANDLE_BOX_TYPE];
1281            break;
1282        case HSCROLL_BAR:
1283        case HSCROLL_BAR_BUTTON_LEFT:
1284        case HSCROLL_BAR_BUTTON_RIGHT:
1285        case HSCROLL_BAR_TRACK:
1286        case HSCROLL_BAR_THUMB:
1287            if (init_result = (NULL == gtk2_widgets[_GTK_HSCROLLBAR_TYPE]))
1288            {
1289                gtk2_widgets[_GTK_HSCROLLBAR_TYPE] =
1290                    (*fp_gtk_hscrollbar_new)(create_adjustment());
1291            }
1292            result = gtk2_widgets[_GTK_HSCROLLBAR_TYPE];
1293            break;
1294        case HSEPARATOR:
1295            if (init_result = (NULL == gtk2_widgets[_GTK_HSEPARATOR_TYPE]))
1296            {
1297                gtk2_widgets[_GTK_HSEPARATOR_TYPE] =
1298                    (*fp_gtk_hseparator_new)();
1299            }
1300            result = gtk2_widgets[_GTK_HSEPARATOR_TYPE];
1301            break;
1302        case HSLIDER:
1303        case HSLIDER_THUMB:
1304        case HSLIDER_TRACK:
1305            if (init_result = (NULL == gtk2_widgets[_GTK_HSCALE_TYPE]))
1306            {
1307                gtk2_widgets[_GTK_HSCALE_TYPE] =
1308                    (*fp_gtk_hscale_new)(NULL);
1309            }
1310            result = gtk2_widgets[_GTK_HSCALE_TYPE];
1311            break;
1312        case HSPLIT_PANE_DIVIDER:
1313        case SPLIT_PANE:
1314            if (init_result = (NULL == gtk2_widgets[_GTK_HPANED_TYPE]))
1315            {
1316                gtk2_widgets[_GTK_HPANED_TYPE] = (*fp_gtk_hpaned_new)();
1317            }
1318            result = gtk2_widgets[_GTK_HPANED_TYPE];
1319            break;
1320        case IMAGE:
1321            if (init_result = (NULL == gtk2_widgets[_GTK_IMAGE_TYPE]))
1322            {
1323                gtk2_widgets[_GTK_IMAGE_TYPE] = (*fp_gtk_image_new)();
1324            }
1325            result = gtk2_widgets[_GTK_IMAGE_TYPE];
1326            break;
1327        case INTERNAL_FRAME:
1328            if (init_result = (NULL == gtk2_widgets[_GTK_WINDOW_TYPE]))
1329            {
1330                gtk2_widgets[_GTK_WINDOW_TYPE] =
1331                    (*fp_gtk_window_new)(GTK_WINDOW_TOPLEVEL);
1332            }
1333            result = gtk2_widgets[_GTK_WINDOW_TYPE];
1334            break;
1335        case TOOL_TIP:
1336            if (init_result = (NULL == gtk2_widgets[_GTK_TOOLTIP_TYPE]))
1337            {
1338                result = (*fp_gtk_window_new)(GTK_WINDOW_TOPLEVEL);
1339                (*fp_gtk_widget_set_name)(result, "gtk-tooltips");
1340                gtk2_widgets[_GTK_TOOLTIP_TYPE] = result;
1341            }
1342            result = gtk2_widgets[_GTK_TOOLTIP_TYPE];
1343            break;
1344        case LIST:
1345        case TABLE:
1346        case TREE:
1347        case TREE_CELL:
1348            if (init_result = (NULL == gtk2_widgets[_GTK_TREE_VIEW_TYPE]))
1349            {
1350                gtk2_widgets[_GTK_TREE_VIEW_TYPE] =
1351                    (*fp_gtk_tree_view_new)();
1352            }
1353            result = gtk2_widgets[_GTK_TREE_VIEW_TYPE];
1354            break;
1355        case TITLED_BORDER:
1356            if (init_result = (NULL == gtk2_widgets[_GTK_FRAME_TYPE]))
1357            {
1358                gtk2_widgets[_GTK_FRAME_TYPE] = fp_gtk_frame_new(NULL);
1359            }
1360            result = gtk2_widgets[_GTK_FRAME_TYPE];
1361            break;
1362        case POPUP_MENU:
1363            if (init_result = (NULL == gtk2_widgets[_GTK_MENU_TYPE]))
1364            {
1365                gtk2_widgets[_GTK_MENU_TYPE] =
1366                    (*fp_gtk_menu_new)();
1367            }
1368            result = gtk2_widgets[_GTK_MENU_TYPE];
1369            break;
1370        case MENU:
1371        case MENU_ITEM:
1372        case MENU_ITEM_ACCELERATOR:
1373            if (init_result = (NULL == gtk2_widgets[_GTK_MENU_ITEM_TYPE]))
1374            {
1375                gtk2_widgets[_GTK_MENU_ITEM_TYPE] =
1376                    (*fp_gtk_menu_item_new)();
1377            }
1378            result = gtk2_widgets[_GTK_MENU_ITEM_TYPE];
1379            break;
1380        case MENU_BAR:
1381            if (init_result = (NULL == gtk2_widgets[_GTK_MENU_BAR_TYPE]))
1382            {
1383                gtk2_widgets[_GTK_MENU_BAR_TYPE] =
1384                    (*fp_gtk_menu_bar_new)();
1385            }
1386            result = gtk2_widgets[_GTK_MENU_BAR_TYPE];
1387            break;
1388        case COLOR_CHOOSER:
1389        case OPTION_PANE:
1390            if (init_result = (NULL == gtk2_widgets[_GTK_DIALOG_TYPE]))
1391            {
1392                gtk2_widgets[_GTK_DIALOG_TYPE] =
1393                    (*fp_gtk_dialog_new)();
1394            }
1395            result = gtk2_widgets[_GTK_DIALOG_TYPE];
1396            break;
1397        case POPUP_MENU_SEPARATOR:
1398            if (init_result =
1399                    (NULL == gtk2_widgets[_GTK_SEPARATOR_MENU_ITEM_TYPE]))
1400            {
1401                gtk2_widgets[_GTK_SEPARATOR_MENU_ITEM_TYPE] =
1402                    (*fp_gtk_separator_menu_item_new)();
1403            }
1404            result = gtk2_widgets[_GTK_SEPARATOR_MENU_ITEM_TYPE];
1405            break;
1406        case HPROGRESS_BAR:
1407            if (init_result = (NULL == gtk2_widgets[_GTK_HPROGRESS_BAR_TYPE]))
1408            {
1409                gtk2_widgets[_GTK_HPROGRESS_BAR_TYPE] =
1410                    (*fp_gtk_progress_bar_new)();
1411            }
1412            result = gtk2_widgets[_GTK_HPROGRESS_BAR_TYPE];
1413            break;
1414        case VPROGRESS_BAR:
1415            if (init_result = (NULL == gtk2_widgets[_GTK_VPROGRESS_BAR_TYPE]))
1416            {
1417                gtk2_widgets[_GTK_VPROGRESS_BAR_TYPE] =
1418                    (*fp_gtk_progress_bar_new)();
1419                /*
1420                 * Vertical JProgressBars always go bottom-to-top,
1421                 * regardless of the ComponentOrientation.
1422                 */
1423                (*fp_gtk_progress_bar_set_orientation)(
1424                    (GtkProgressBar *)gtk2_widgets[_GTK_VPROGRESS_BAR_TYPE],
1425                    GTK_PROGRESS_BOTTOM_TO_TOP);
1426            }
1427            result = gtk2_widgets[_GTK_VPROGRESS_BAR_TYPE];
1428            break;
1429        case RADIO_BUTTON:
1430            if (init_result = (NULL == gtk2_widgets[_GTK_RADIO_BUTTON_TYPE]))
1431            {
1432                gtk2_widgets[_GTK_RADIO_BUTTON_TYPE] =
1433                    (*fp_gtk_radio_button_new)(NULL);
1434            }
1435            result = gtk2_widgets[_GTK_RADIO_BUTTON_TYPE];
1436            break;
1437        case RADIO_BUTTON_MENU_ITEM:
1438            if (init_result =
1439                    (NULL == gtk2_widgets[_GTK_RADIO_MENU_ITEM_TYPE]))
1440            {
1441                gtk2_widgets[_GTK_RADIO_MENU_ITEM_TYPE] =
1442                    (*fp_gtk_radio_menu_item_new)(NULL);
1443            }
1444            result = gtk2_widgets[_GTK_RADIO_MENU_ITEM_TYPE];
1445            break;
1446        case SCROLL_PANE:
1447            if (init_result =
1448                    (NULL == gtk2_widgets[_GTK_SCROLLED_WINDOW_TYPE]))
1449            {
1450                gtk2_widgets[_GTK_SCROLLED_WINDOW_TYPE] =
1451                    (*fp_gtk_scrolled_window_new)(NULL, NULL);
1452            }
1453            result = gtk2_widgets[_GTK_SCROLLED_WINDOW_TYPE];
1454            break;
1455        case SPINNER:
1456        case SPINNER_ARROW_BUTTON:
1457        case SPINNER_TEXT_FIELD:
1458            if (init_result = (NULL == gtk2_widgets[_GTK_SPIN_BUTTON_TYPE]))
1459            {
1460                result = gtk2_widgets[_GTK_SPIN_BUTTON_TYPE] =
1461                    (*fp_gtk_spin_button_new)(NULL, 0, 0);
1462            }
1463            result = gtk2_widgets[_GTK_SPIN_BUTTON_TYPE];
1464            break;
1465        case TABBED_PANE:
1466        case TABBED_PANE_TAB_AREA:
1467        case TABBED_PANE_CONTENT:
1468        case TABBED_PANE_TAB:
1469            if (init_result = (NULL == gtk2_widgets[_GTK_NOTEBOOK_TYPE]))
1470            {
1471                gtk2_widgets[_GTK_NOTEBOOK_TYPE] =
1472                    (*fp_gtk_notebook_new)(NULL);
1473            }
1474            result = gtk2_widgets[_GTK_NOTEBOOK_TYPE];
1475            break;
1476        case TOGGLE_BUTTON:
1477            if (init_result = (NULL == gtk2_widgets[_GTK_TOGGLE_BUTTON_TYPE]))
1478            {
1479                gtk2_widgets[_GTK_TOGGLE_BUTTON_TYPE] =
1480                    (*fp_gtk_toggle_button_new)(NULL);
1481            }
1482            result = gtk2_widgets[_GTK_TOGGLE_BUTTON_TYPE];
1483            break;
1484        case TOOL_BAR:
1485        case TOOL_BAR_DRAG_WINDOW:
1486            if (init_result = (NULL == gtk2_widgets[_GTK_TOOLBAR_TYPE]))
1487            {
1488                gtk2_widgets[_GTK_TOOLBAR_TYPE] =
1489                    (*fp_gtk_toolbar_new)(NULL);
1490            }
1491            result = gtk2_widgets[_GTK_TOOLBAR_TYPE];
1492            break;
1493        case TOOL_BAR_SEPARATOR:
1494            if (init_result =
1495                    (NULL == gtk2_widgets[_GTK_SEPARATOR_TOOL_ITEM_TYPE]))
1496            {
1497                gtk2_widgets[_GTK_SEPARATOR_TOOL_ITEM_TYPE] =
1498                    (*fp_gtk_separator_tool_item_new)();
1499            }
1500            result = gtk2_widgets[_GTK_SEPARATOR_TOOL_ITEM_TYPE];
1501            break;
1502        case VIEWPORT:
1503            if (init_result = (NULL == gtk2_widgets[_GTK_VIEWPORT_TYPE]))
1504            {
1505                GtkAdjustment *adjustment = create_adjustment();
1506                gtk2_widgets[_GTK_VIEWPORT_TYPE] =
1507                    (*fp_gtk_viewport_new)(adjustment, adjustment);
1508            }
1509            result = gtk2_widgets[_GTK_VIEWPORT_TYPE];
1510            break;
1511        case VSCROLL_BAR:
1512        case VSCROLL_BAR_BUTTON_UP:
1513        case VSCROLL_BAR_BUTTON_DOWN:
1514        case VSCROLL_BAR_TRACK:
1515        case VSCROLL_BAR_THUMB:
1516            if (init_result = (NULL == gtk2_widgets[_GTK_VSCROLLBAR_TYPE]))
1517            {
1518                gtk2_widgets[_GTK_VSCROLLBAR_TYPE] =
1519                    (*fp_gtk_vscrollbar_new)(create_adjustment());
1520            }
1521            result = gtk2_widgets[_GTK_VSCROLLBAR_TYPE];
1522            break;
1523        case VSEPARATOR:
1524            if (init_result = (NULL == gtk2_widgets[_GTK_VSEPARATOR_TYPE]))
1525            {
1526                gtk2_widgets[_GTK_VSEPARATOR_TYPE] =
1527                    (*fp_gtk_vseparator_new)();
1528            }
1529            result = gtk2_widgets[_GTK_VSEPARATOR_TYPE];
1530            break;
1531        case VSLIDER:
1532        case VSLIDER_THUMB:
1533        case VSLIDER_TRACK:
1534            if (init_result = (NULL == gtk2_widgets[_GTK_VSCALE_TYPE]))
1535            {
1536                gtk2_widgets[_GTK_VSCALE_TYPE] =
1537                    (*fp_gtk_vscale_new)(NULL);
1538            }
1539            result = gtk2_widgets[_GTK_VSCALE_TYPE];
1540            /*
1541             * Vertical JSliders start at the bottom, while vertical
1542             * GtkVScale widgets start at the top (by default), so to fix
1543             * this we set the "inverted" flag to get the Swing behavior.
1544             */
1545            ((GtkRange*)result)->inverted = 1;
1546            break;
1547        case VSPLIT_PANE_DIVIDER:
1548            if (init_result = (NULL == gtk2_widgets[_GTK_VPANED_TYPE]))
1549            {
1550                gtk2_widgets[_GTK_VPANED_TYPE] = (*fp_gtk_vpaned_new)();
1551            }
1552            result = gtk2_widgets[_GTK_VPANED_TYPE];
1553            break;
1554        default:
1555            result = NULL;
1556            break;
1557    }
1558
1559    if (result != NULL && init_result)
1560    {
1561        if (widget_type == RADIO_BUTTON_MENU_ITEM ||
1562                widget_type == CHECK_BOX_MENU_ITEM ||
1563                widget_type == MENU_ITEM ||
1564                widget_type == MENU ||
1565                widget_type == POPUP_MENU_SEPARATOR)
1566        {
1567            GtkWidget *menu = gtk2_get_widget(POPUP_MENU);
1568            (*fp_gtk_menu_shell_append)((GtkMenuShell *)menu, result);
1569        }
1570        else if (widget_type == POPUP_MENU)
1571        {
1572            GtkWidget *menu_bar = gtk2_get_widget(MENU_BAR);
1573            GtkWidget *root_menu = (*fp_gtk_menu_item_new)();
1574            (*fp_gtk_menu_item_set_submenu)((GtkMenuItem*)root_menu, result);
1575            (*fp_gtk_menu_shell_append)((GtkMenuShell *)menu_bar, root_menu);
1576        }
1577        else if (widget_type == COMBO_BOX_ARROW_BUTTON ||
1578                 widget_type == COMBO_BOX_TEXT_FIELD)
1579        {
1580            /*
1581            * We add a regular GtkButton/GtkEntry to a GtkComboBoxEntry
1582            * in order to trick engines into thinking it's a real combobox
1583            * arrow button/text field.
1584            */
1585            GtkWidget *combo = (*fp_gtk_combo_box_entry_new)();
1586
1587            if (new_combo && widget_type == COMBO_BOX_ARROW_BUTTON) {
1588                (*fp_gtk_widget_set_parent)(result, combo);
1589                ((GtkBin*)combo)->child = result;
1590            } else {
1591                (*fp_gtk_container_add)((GtkContainer *)combo, result);
1592            }
1593            (*fp_gtk_container_add)((GtkContainer *)gtk2_fixed, combo);
1594        }
1595        else if (widget_type != TOOL_TIP &&
1596                 widget_type != INTERNAL_FRAME &&
1597                 widget_type != OPTION_PANE)
1598        {
1599            (*fp_gtk_container_add)((GtkContainer *)gtk2_fixed, result);
1600        }
1601        (*fp_gtk_widget_realize)(result);
1602    }
1603    return result;
1604}
1605
1606void gtk2_paint_arrow(WidgetType widget_type, GtkStateType state_type,
1607        GtkShadowType shadow_type, const gchar *detail,
1608        gint x, gint y, gint width, gint height,
1609        GtkArrowType arrow_type, gboolean fill)
1610{
1611    static int w, h;
1612    static GtkRequisition size;
1613
1614    if (widget_type == COMBO_BOX_ARROW_BUTTON || widget_type == TABLE)
1615        gtk2_widget = gtk2_get_arrow(arrow_type, shadow_type);
1616    else
1617        gtk2_widget = gtk2_get_widget(widget_type);
1618
1619    switch (widget_type)
1620    {
1621        case SPINNER_ARROW_BUTTON:
1622            x = 1;
1623            y = ((arrow_type == GTK_ARROW_UP) ? 2 : 0);
1624            height -= 2;
1625            width -= 3;
1626
1627            w = width / 2;
1628            w -= w % 2 - 1;
1629            h = (w + 1) / 2;
1630            break;
1631
1632        case HSCROLL_BAR_BUTTON_LEFT:
1633        case HSCROLL_BAR_BUTTON_RIGHT:
1634        case VSCROLL_BAR_BUTTON_UP:
1635        case VSCROLL_BAR_BUTTON_DOWN:
1636            w = width / 2;
1637            h = height / 2;
1638            break;
1639
1640        case COMBO_BOX_ARROW_BUTTON:
1641        case TABLE:
1642            x = 1;
1643            (*fp_gtk_widget_size_request)(gtk2_widget, &size);
1644            w = size.width - ((GtkMisc*)gtk2_widget)->xpad * 2;
1645            h = size.height - ((GtkMisc*)gtk2_widget)->ypad * 2;
1646            w = h = MIN(MIN(w, h), MIN(width,height)) * 0.7;
1647            break;
1648
1649        default:
1650            w = width;
1651            h = height;
1652            break;
1653    }
1654    x += (width - w) / 2;
1655    y += (height - h) / 2;
1656
1657    (*fp_gtk_paint_arrow)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1658            shadow_type, NULL, gtk2_widget, detail, arrow_type, fill,
1659            x, y, w, h);
1660    (*fp_gtk_paint_arrow)(gtk2_widget->style, gtk2_black_pixmap, state_type,
1661            shadow_type, NULL, gtk2_widget, detail, arrow_type, fill,
1662            x, y, w, h);
1663}
1664
1665static void gtk2_paint_box(WidgetType widget_type, GtkStateType state_type,
1666                    GtkShadowType shadow_type, const gchar *detail,
1667                    gint x, gint y, gint width, gint height,
1668                    gint synth_state, GtkTextDirection dir)
1669{
1670    gtk2_widget = gtk2_get_widget(widget_type);
1671
1672    /*
1673     * The clearlooks engine sometimes looks at the widget's state field
1674     * instead of just the state_type variable that we pass in, so to account
1675     * for those cases we set the widget's state field accordingly.  The
1676     * flags field is similarly important for things like focus/default state.
1677     */
1678    gtk2_widget->state = state_type;
1679
1680    if (widget_type == HSLIDER_TRACK) {
1681        /*
1682         * For horizontal JSliders with right-to-left orientation, we need
1683         * to set the "inverted" flag to match the native GTK behavior where
1684         * the foreground highlight is on the right side of the slider thumb.
1685         * This is needed especially for the ubuntulooks engine, which looks
1686         * exclusively at the "inverted" flag to determine on which side of
1687         * the thumb to paint the highlight...
1688         */
1689        ((GtkRange*)gtk2_widget)->inverted = (dir == GTK_TEXT_DIR_RTL);
1690
1691        /*
1692         * Note however that other engines like clearlooks will look at both
1693         * the "inverted" field and the text direction to determine how
1694         * the foreground highlight is painted:
1695         *     !inverted && ltr --> paint highlight on left side
1696         *     !inverted && rtl --> paint highlight on right side
1697         *      inverted && ltr --> paint highlight on right side
1698         *      inverted && rtl --> paint highlight on left side
1699         * So the only way to reliably get the desired results for horizontal
1700         * JSlider (i.e., highlight on left side for LTR ComponentOrientation
1701         * and highlight on right side for RTL ComponentOrientation) is to
1702         * always override text direction as LTR, and then set the "inverted"
1703         * flag accordingly (as we have done above).
1704         */
1705        dir = GTK_TEXT_DIR_LTR;
1706    }
1707
1708    /*
1709     * Some engines (e.g. clearlooks) will paint the shadow of certain
1710     * widgets (e.g. COMBO_BOX_ARROW_BUTTON) differently depending on the
1711     * the text direction.
1712     */
1713    gtk2_set_direction(gtk2_widget, dir);
1714
1715    switch (widget_type) {
1716    case BUTTON:
1717        if (synth_state & DEFAULT) {
1718            ((GtkObject*)gtk2_widget)->flags |= GTK_HAS_DEFAULT;
1719        } else {
1720            ((GtkObject*)gtk2_widget)->flags &= ~GTK_HAS_DEFAULT;
1721        }
1722        break;
1723    case TOGGLE_BUTTON:
1724        init_toggle_widget(widget_type, synth_state);
1725        break;
1726    case HSCROLL_BAR_BUTTON_LEFT:
1727        /*
1728         * The clearlooks engine will draw a "left" button when:
1729         *   x == w->allocation.x
1730         *
1731         * The ubuntulooks engine will draw a "left" button when:
1732         *   [x,y,width,height]
1733         *     intersects
1734         *   [w->alloc.x,w->alloc.y,width,height]
1735         *
1736         * The values that are set below should ensure that a "left"
1737         * button is rendered for both of these (and other) engines.
1738         */
1739        gtk2_widget->allocation.x = x;
1740        gtk2_widget->allocation.y = y;
1741        gtk2_widget->allocation.width = width;
1742        gtk2_widget->allocation.height = height;
1743        break;
1744    case HSCROLL_BAR_BUTTON_RIGHT:
1745        /*
1746         * The clearlooks engine will draw a "right" button when:
1747         *   x + width == w->allocation.x + w->allocation.width
1748         *
1749         * The ubuntulooks engine will draw a "right" button when:
1750         *   [x,y,width,height]
1751         *     does not intersect
1752         *   [w->alloc.x,w->alloc.y,width,height]
1753         *     but does intersect
1754         *   [w->alloc.x+width,w->alloc.y,width,height]
1755         *
1756         * The values that are set below should ensure that a "right"
1757         * button is rendered for both of these (and other) engines.
1758         */
1759        gtk2_widget->allocation.x = x+width;
1760        gtk2_widget->allocation.y = 0;
1761        gtk2_widget->allocation.width = 0;
1762        gtk2_widget->allocation.height = height;
1763        break;
1764    case VSCROLL_BAR_BUTTON_UP:
1765        /*
1766         * The clearlooks engine will draw an "up" button when:
1767         *   y == w->allocation.y
1768         *
1769         * The ubuntulooks engine will draw an "up" button when:
1770         *   [x,y,width,height]
1771         *     intersects
1772         *   [w->alloc.x,w->alloc.y,width,height]
1773         *
1774         * The values that are set below should ensure that an "up"
1775         * button is rendered for both of these (and other) engines.
1776         */
1777        gtk2_widget->allocation.x = x;
1778        gtk2_widget->allocation.y = y;
1779        gtk2_widget->allocation.width = width;
1780        gtk2_widget->allocation.height = height;
1781        break;
1782    case VSCROLL_BAR_BUTTON_DOWN:
1783        /*
1784         * The clearlooks engine will draw a "down" button when:
1785         *   y + height == w->allocation.y + w->allocation.height
1786         *
1787         * The ubuntulooks engine will draw a "down" button when:
1788         *   [x,y,width,height]
1789         *     does not intersect
1790         *   [w->alloc.x,w->alloc.y,width,height]
1791         *     but does intersect
1792         *   [w->alloc.x,w->alloc.y+height,width,height]
1793         *
1794         * The values that are set below should ensure that a "down"
1795         * button is rendered for both of these (and other) engines.
1796         */
1797        gtk2_widget->allocation.x = x;
1798        gtk2_widget->allocation.y = y+height;
1799        gtk2_widget->allocation.width = width;
1800        gtk2_widget->allocation.height = 0;
1801        break;
1802    default:
1803        break;
1804    }
1805
1806    (*fp_gtk_paint_box)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1807            shadow_type, NULL, gtk2_widget, detail, x, y, width, height);
1808    (*fp_gtk_paint_box)(gtk2_widget->style, gtk2_black_pixmap, state_type,
1809            shadow_type, NULL, gtk2_widget, detail, x, y, width, height);
1810
1811    /*
1812     * Reset the text direction to the default value so that we don't
1813     * accidentally affect other operations and widgets.
1814     */
1815    gtk2_set_direction(gtk2_widget, GTK_TEXT_DIR_LTR);
1816}
1817
1818void gtk2_paint_box_gap(WidgetType widget_type, GtkStateType state_type,
1819        GtkShadowType shadow_type, const gchar *detail,
1820        gint x, gint y, gint width, gint height,
1821        GtkPositionType gap_side, gint gap_x, gint gap_width)
1822{
1823    /* Clearlooks needs a real clip area to paint the gap properly */
1824    GdkRectangle area = { x, y, width, height };
1825
1826    gtk2_widget = gtk2_get_widget(widget_type);
1827    (*fp_gtk_paint_box_gap)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1828            shadow_type, &area, gtk2_widget, detail,
1829            x, y, width, height, gap_side, gap_x, gap_width);
1830    (*fp_gtk_paint_box_gap)(gtk2_widget->style, gtk2_black_pixmap, state_type,
1831            shadow_type, &area, gtk2_widget, detail,
1832            x, y, width, height, gap_side, gap_x, gap_width);
1833}
1834
1835static void gtk2_paint_check(WidgetType widget_type, gint synth_state,
1836        const gchar *detail, gint x, gint y, gint width, gint height)
1837{
1838    GtkStateType state_type = get_gtk_state_type(widget_type, synth_state);
1839    GtkShadowType shadow_type = get_gtk_shadow_type(widget_type, synth_state);
1840
1841    gtk2_widget = gtk2_get_widget(widget_type);
1842    init_toggle_widget(widget_type, synth_state);
1843
1844    (*fp_gtk_paint_check)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1845            shadow_type, NULL, gtk2_widget, detail,
1846            x, y, width, height);
1847    (*fp_gtk_paint_check)(gtk2_widget->style, gtk2_black_pixmap, state_type,
1848            shadow_type, NULL, gtk2_widget, detail,
1849            x, y, width, height);
1850}
1851
1852static void gtk2_paint_diamond(WidgetType widget_type, GtkStateType state_type,
1853        GtkShadowType shadow_type, const gchar *detail,
1854        gint x, gint y, gint width, gint height)
1855{
1856    gtk2_widget = gtk2_get_widget(widget_type);
1857    (*fp_gtk_paint_diamond)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1858            shadow_type, NULL, gtk2_widget, detail,
1859            x, y, width, height);
1860    (*fp_gtk_paint_diamond)(gtk2_widget->style, gtk2_black_pixmap, state_type,
1861            shadow_type, NULL, gtk2_widget, detail,
1862            x, y, width, height);
1863}
1864
1865static void gtk2_paint_expander(WidgetType widget_type, GtkStateType state_type,
1866        const gchar *detail, gint x, gint y, gint width, gint height,
1867        GtkExpanderStyle expander_style)
1868{
1869    gtk2_widget = gtk2_get_widget(widget_type);
1870    (*fp_gtk_paint_expander)(gtk2_widget->style, gtk2_white_pixmap,
1871            state_type, NULL, gtk2_widget, detail,
1872            x + width / 2, y + height / 2, expander_style);
1873    (*fp_gtk_paint_expander)(gtk2_widget->style, gtk2_black_pixmap,
1874            state_type, NULL, gtk2_widget, detail,
1875            x + width / 2, y + height / 2, expander_style);
1876}
1877
1878static void gtk2_paint_extension(WidgetType widget_type, GtkStateType state_type,
1879        GtkShadowType shadow_type, const gchar *detail,
1880        gint x, gint y, gint width, gint height, GtkPositionType gap_side)
1881{
1882    gtk2_widget = gtk2_get_widget(widget_type);
1883    (*fp_gtk_paint_extension)(gtk2_widget->style, gtk2_white_pixmap,
1884            state_type, shadow_type, NULL, gtk2_widget, detail,
1885            x, y, width, height, gap_side);
1886    (*fp_gtk_paint_extension)(gtk2_widget->style, gtk2_black_pixmap,
1887            state_type, shadow_type, NULL, gtk2_widget, detail,
1888            x, y, width, height, gap_side);
1889}
1890
1891static void gtk2_paint_flat_box(WidgetType widget_type, GtkStateType state_type,
1892        GtkShadowType shadow_type, const gchar *detail,
1893        gint x, gint y, gint width, gint height, gboolean has_focus)
1894{
1895    gtk2_widget = gtk2_get_widget(widget_type);
1896
1897    if (has_focus)
1898        ((GtkObject*)gtk2_widget)->flags |= GTK_HAS_FOCUS;
1899    else
1900        ((GtkObject*)gtk2_widget)->flags &= ~GTK_HAS_FOCUS;
1901
1902    (*fp_gtk_paint_flat_box)(gtk2_widget->style, gtk2_white_pixmap,
1903            state_type, shadow_type, NULL, gtk2_widget, detail,
1904            x, y, width, height);
1905    (*fp_gtk_paint_flat_box)(gtk2_widget->style, gtk2_black_pixmap,
1906            state_type, shadow_type, NULL, gtk2_widget, detail,
1907            x, y, width, height);
1908}
1909
1910static void gtk2_paint_focus(WidgetType widget_type, GtkStateType state_type,
1911        const char *detail, gint x, gint y, gint width, gint height)
1912{
1913    gtk2_widget = gtk2_get_widget(widget_type);
1914    (*fp_gtk_paint_focus)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1915            NULL, gtk2_widget, detail, x, y, width, height);
1916    (*fp_gtk_paint_focus)(gtk2_widget->style, gtk2_black_pixmap, state_type,
1917            NULL, gtk2_widget, detail, x, y, width, height);
1918}
1919
1920static void gtk2_paint_handle(WidgetType widget_type, GtkStateType state_type,
1921        GtkShadowType shadow_type, const gchar *detail,
1922        gint x, gint y, gint width, gint height, GtkOrientation orientation)
1923{
1924    gtk2_widget = gtk2_get_widget(widget_type);
1925    (*fp_gtk_paint_handle)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1926            shadow_type, NULL, gtk2_widget, detail,
1927            x, y, width, height, orientation);
1928    (*fp_gtk_paint_handle)(gtk2_widget->style, gtk2_black_pixmap, state_type,
1929            shadow_type, NULL, gtk2_widget, detail,
1930            x, y, width, height, orientation);
1931}
1932
1933static void gtk2_paint_hline(WidgetType widget_type, GtkStateType state_type,
1934        const gchar *detail, gint x, gint y, gint width, gint height)
1935{
1936    gtk2_widget = gtk2_get_widget(widget_type);
1937    (*fp_gtk_paint_hline)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1938            NULL, gtk2_widget, detail, x, x + width, y);
1939    (*fp_gtk_paint_hline)(gtk2_widget->style, gtk2_black_pixmap, state_type,
1940            NULL, gtk2_widget, detail, x, x + width, y);
1941}
1942
1943static void gtk2_paint_option(WidgetType widget_type, gint synth_state,
1944        const gchar *detail, gint x, gint y, gint width, gint height)
1945{
1946    GtkStateType state_type = get_gtk_state_type(widget_type, synth_state);
1947    GtkShadowType shadow_type = get_gtk_shadow_type(widget_type, synth_state);
1948
1949    gtk2_widget = gtk2_get_widget(widget_type);
1950    init_toggle_widget(widget_type, synth_state);
1951
1952    (*fp_gtk_paint_option)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1953            shadow_type, NULL, gtk2_widget, detail,
1954            x, y, width, height);
1955    (*fp_gtk_paint_option)(gtk2_widget->style, gtk2_black_pixmap, state_type,
1956            shadow_type, NULL, gtk2_widget, detail,
1957            x, y, width, height);
1958}
1959
1960static void gtk2_paint_shadow(WidgetType widget_type, GtkStateType state_type,
1961                       GtkShadowType shadow_type, const gchar *detail,
1962                       gint x, gint y, gint width, gint height,
1963                       gint synth_state, GtkTextDirection dir)
1964{
1965    gtk2_widget = gtk2_get_widget(widget_type);
1966
1967    /*
1968     * The clearlooks engine sometimes looks at the widget's state field
1969     * instead of just the state_type variable that we pass in, so to account
1970     * for those cases we set the widget's state field accordingly.  The
1971     * flags field is similarly important for things like focus state.
1972     */
1973    gtk2_widget->state = state_type;
1974
1975    /*
1976     * Some engines (e.g. clearlooks) will paint the shadow of certain
1977     * widgets (e.g. COMBO_BOX_TEXT_FIELD) differently depending on the
1978     * the text direction.
1979     */
1980    gtk2_set_direction(gtk2_widget, dir);
1981
1982    switch (widget_type) {
1983    case COMBO_BOX_TEXT_FIELD:
1984    case FORMATTED_TEXT_FIELD:
1985    case PASSWORD_FIELD:
1986    case SPINNER_TEXT_FIELD:
1987    case TEXT_FIELD:
1988        if (synth_state & FOCUSED) {
1989            ((GtkObject*)gtk2_widget)->flags |= GTK_HAS_FOCUS;
1990        } else {
1991            ((GtkObject*)gtk2_widget)->flags &= ~GTK_HAS_FOCUS;
1992        }
1993        break;
1994    default:
1995        break;
1996    }
1997
1998    (*fp_gtk_paint_shadow)(gtk2_widget->style, gtk2_white_pixmap, state_type,
1999            shadow_type, NULL, gtk2_widget, detail, x, y, width, height);
2000    (*fp_gtk_paint_shadow)(gtk2_widget->style, gtk2_black_pixmap, state_type,
2001            shadow_type, NULL, gtk2_widget, detail, x, y, width, height);
2002
2003    /*
2004     * Reset the text direction to the default value so that we don't
2005     * accidentally affect other operations and widgets.
2006     */
2007    gtk2_set_direction(gtk2_widget, GTK_TEXT_DIR_LTR);
2008}
2009
2010static void gtk2_paint_slider(WidgetType widget_type, GtkStateType state_type,
2011        GtkShadowType shadow_type, const gchar *detail,
2012        gint x, gint y, gint width, gint height, GtkOrientation orientation,
2013        gboolean has_focus)
2014{
2015    gtk2_widget = gtk2_get_widget(widget_type);
2016    (*fp_gtk_paint_slider)(gtk2_widget->style, gtk2_white_pixmap, state_type,
2017            shadow_type, NULL, gtk2_widget, detail,
2018            x, y, width, height, orientation);
2019    (*fp_gtk_paint_slider)(gtk2_widget->style, gtk2_black_pixmap, state_type,
2020            shadow_type, NULL, gtk2_widget, detail,
2021            x, y, width, height, orientation);
2022}
2023
2024static void gtk2_paint_vline(WidgetType widget_type, GtkStateType state_type,
2025        const gchar *detail, gint x, gint y, gint width, gint height)
2026{
2027    gtk2_widget = gtk2_get_widget(widget_type);
2028    (*fp_gtk_paint_vline)(gtk2_widget->style, gtk2_white_pixmap, state_type,
2029            NULL, gtk2_widget, detail, y, y + height, x);
2030    (*fp_gtk_paint_vline)(gtk2_widget->style, gtk2_black_pixmap, state_type,
2031            NULL, gtk2_widget, detail, y, y + height, x);
2032}
2033
2034static void gtk_paint_background(WidgetType widget_type, GtkStateType state_type,
2035        gint x, gint y, gint width, gint height)
2036{
2037    gtk2_widget = gtk2_get_widget(widget_type);
2038    (*fp_gtk_style_apply_default_background)(gtk2_widget->style,
2039            gtk2_white_pixmap, TRUE, state_type, NULL, x, y, width, height);
2040    (*fp_gtk_style_apply_default_background)(gtk2_widget->style,
2041            gtk2_black_pixmap, TRUE, state_type, NULL, x, y, width, height);
2042}
2043
2044static GdkPixbuf *gtk2_get_stock_icon(gint widget_type, const gchar *stock_id,
2045        GtkIconSize size, GtkTextDirection direction, const char *detail)
2046{
2047    init_containers();
2048    gtk2_widget = gtk2_get_widget((widget_type < 0) ? IMAGE : widget_type);
2049    gtk2_widget->state = GTK_STATE_NORMAL;
2050    (*fp_gtk_widget_set_direction)(gtk2_widget, direction);
2051    return (*fp_gtk_widget_render_icon)(gtk2_widget, stock_id, size, detail);
2052}
2053
2054static jboolean gtk2_get_pixbuf_data(JNIEnv *env, GdkPixbuf* pixbuf,
2055                              jmethodID icon_upcall_method, jobject this) {
2056    if (!pixbuf) {
2057        return JNI_FALSE;
2058    }
2059    guchar *pixbuf_data = (*fp_gdk_pixbuf_get_pixels)(pixbuf);
2060    if (pixbuf_data) {
2061        int row_stride = (*fp_gdk_pixbuf_get_rowstride)(pixbuf);
2062        int width = (*fp_gdk_pixbuf_get_width)(pixbuf);
2063        int height = (*fp_gdk_pixbuf_get_height)(pixbuf);
2064        int bps = (*fp_gdk_pixbuf_get_bits_per_sample)(pixbuf);
2065        int channels = (*fp_gdk_pixbuf_get_n_channels)(pixbuf);
2066        gboolean alpha = (*fp_gdk_pixbuf_get_has_alpha)(pixbuf);
2067
2068        jbyteArray data = (*env)->NewByteArray(env, (row_stride * height));
2069        JNU_CHECK_EXCEPTION_RETURN(env, JNI_FALSE);
2070
2071        (*env)->SetByteArrayRegion(env, data, 0, (row_stride * height),
2072                                   (jbyte *)pixbuf_data);
2073        (*fp_g_object_unref)(pixbuf);
2074
2075        /* Call the callback method to create the image on the Java side. */
2076        (*env)->CallVoidMethod(env, this, icon_upcall_method, data,
2077                width, height, row_stride, bps, channels, alpha);
2078        return JNI_TRUE;
2079    }
2080    return JNI_FALSE;
2081}
2082
2083static jboolean gtk2_get_file_icon_data(JNIEnv *env, const char *filename,
2084                 GError **error, jmethodID icon_upcall_method, jobject this) {
2085    GdkPixbuf* pixbuf = fp_gdk_pixbuf_new_from_file(filename, error);
2086    return gtk2_get_pixbuf_data(env, pixbuf, icon_upcall_method, this);
2087}
2088
2089static jboolean gtk2_get_icon_data(JNIEnv *env, gint widget_type,
2090                              const gchar *stock_id, GtkIconSize size,
2091                              GtkTextDirection direction, const char *detail,
2092                              jmethodID icon_upcall_method, jobject this) {
2093    GdkPixbuf* pixbuf = gtk2_get_stock_icon(widget_type, stock_id, size,
2094                                       direction, detail);
2095    return gtk2_get_pixbuf_data(env, pixbuf, icon_upcall_method, this);
2096}
2097
2098/*************************************************/
2099static gint gtk2_get_xthickness(JNIEnv *env, WidgetType widget_type)
2100{
2101    init_containers();
2102
2103    gtk2_widget = gtk2_get_widget(widget_type);
2104    GtkStyle* style = gtk2_widget->style;
2105    return style->xthickness;
2106}
2107
2108static gint gtk2_get_ythickness(JNIEnv *env, WidgetType widget_type)
2109{
2110    init_containers();
2111
2112    gtk2_widget = gtk2_get_widget(widget_type);
2113    GtkStyle* style = gtk2_widget->style;
2114    return style->ythickness;
2115}
2116
2117/*************************************************/
2118static guint8 recode_color(guint16 channel)
2119{
2120    return (guint8)(channel>>8);
2121}
2122
2123static gint gtk2_get_color_for_state(JNIEnv *env, WidgetType widget_type,
2124                              GtkStateType state_type, ColorType color_type)
2125{
2126    gint result = 0;
2127    GdkColor *color = NULL;
2128
2129    init_containers();
2130
2131    gtk2_widget = gtk2_get_widget(widget_type);
2132    GtkStyle* style = gtk2_widget->style;
2133
2134    switch (color_type)
2135    {
2136        case FOREGROUND:
2137            color = &(style->fg[state_type]);
2138            break;
2139        case BACKGROUND:
2140            color = &(style->bg[state_type]);
2141            break;
2142        case TEXT_FOREGROUND:
2143            color = &(style->text[state_type]);
2144            break;
2145        case TEXT_BACKGROUND:
2146            color = &(style->base[state_type]);
2147            break;
2148        case LIGHT:
2149            color = &(style->light[state_type]);
2150            break;
2151        case DARK:
2152            color = &(style->dark[state_type]);
2153            break;
2154        case MID:
2155            color = &(style->mid[state_type]);
2156            break;
2157        case FOCUS:
2158        case BLACK:
2159            color = &(style->black);
2160            break;
2161        case WHITE:
2162            color = &(style->white);
2163            break;
2164    }
2165
2166    if (color)
2167        result = recode_color(color->red)   << 16 |
2168                 recode_color(color->green) << 8  |
2169                 recode_color(color->blue);
2170
2171    return result;
2172}
2173
2174/*************************************************/
2175static jobject create_Boolean(JNIEnv *env, jboolean boolean_value);
2176static jobject create_Integer(JNIEnv *env, jint int_value);
2177static jobject create_Long(JNIEnv *env, jlong long_value);
2178static jobject create_Float(JNIEnv *env, jfloat float_value);
2179static jobject create_Double(JNIEnv *env, jdouble double_value);
2180static jobject create_Character(JNIEnv *env, jchar char_value);
2181static jobject create_Insets(JNIEnv *env, GtkBorder *border);
2182
2183static jobject gtk2_get_class_value(JNIEnv *env, WidgetType widget_type,
2184                              const char* key)
2185{
2186    init_containers();
2187
2188    gtk2_widget = gtk2_get_widget(widget_type);
2189
2190    GValue value;
2191    value.g_type = 0;
2192
2193    GParamSpec* param = (*fp_gtk_widget_class_find_style_property)(
2194                                    ((GTypeInstance*)gtk2_widget)->g_class, key);
2195    if( param )
2196    {
2197        (*fp_g_value_init)( &value, param->value_type );
2198        (*fp_gtk_widget_style_get_property)(gtk2_widget, key, &value);
2199
2200        if( (*fp_g_type_is_a)( param->value_type, G_TYPE_BOOLEAN ))
2201        {
2202            gboolean val = (*fp_g_value_get_boolean)(&value);
2203            return create_Boolean(env, (jboolean)val);
2204        }
2205        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_CHAR ))
2206        {
2207            gchar val = (*fp_g_value_get_char)(&value);
2208            return create_Character(env, (jchar)val);
2209        }
2210        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_UCHAR ))
2211        {
2212            guchar val = (*fp_g_value_get_uchar)(&value);
2213            return create_Character(env, (jchar)val);
2214        }
2215        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_INT ))
2216        {
2217            gint val = (*fp_g_value_get_int)(&value);
2218            return create_Integer(env, (jint)val);
2219        }
2220        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_UINT ))
2221        {
2222            guint val = (*fp_g_value_get_uint)(&value);
2223            return create_Integer(env, (jint)val);
2224        }
2225        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_LONG ))
2226        {
2227            glong val = (*fp_g_value_get_long)(&value);
2228            return create_Long(env, (jlong)val);
2229        }
2230        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_ULONG ))
2231        {
2232            gulong val = (*fp_g_value_get_ulong)(&value);
2233            return create_Long(env, (jlong)val);
2234        }
2235        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_INT64 ))
2236        {
2237            gint64 val = (*fp_g_value_get_int64)(&value);
2238            return create_Long(env, (jlong)val);
2239        }
2240        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_UINT64 ))
2241        {
2242            guint64 val = (*fp_g_value_get_uint64)(&value);
2243            return create_Long(env, (jlong)val);
2244        }
2245        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_FLOAT ))
2246        {
2247            gfloat val = (*fp_g_value_get_float)(&value);
2248            return create_Float(env, (jfloat)val);
2249        }
2250        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_DOUBLE ))
2251        {
2252            gdouble val = (*fp_g_value_get_double)(&value);
2253            return create_Double(env, (jdouble)val);
2254        }
2255        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_ENUM ))
2256        {
2257            gint val = (*fp_g_value_get_enum)(&value);
2258            return create_Integer(env, (jint)val);
2259        }
2260        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_FLAGS ))
2261        {
2262            guint val = (*fp_g_value_get_flags)(&value);
2263            return create_Integer(env, (jint)val);
2264        }
2265        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_STRING ))
2266        {
2267            const gchar* val = (*fp_g_value_get_string)(&value);
2268
2269            /* We suppose that all values come in C locale and
2270             * utf-8 representation of a string is the same as
2271             * the string itself. If this isn't so we should
2272             * use g_convert.
2273             */
2274            return (*env)->NewStringUTF(env, val);
2275        }
2276        else if( (*fp_g_type_is_a)( param->value_type, GTK_TYPE_BORDER ))
2277        {
2278            GtkBorder *border = (GtkBorder*)(*fp_g_value_get_boxed)(&value);
2279            return border ? create_Insets(env, border) : NULL;
2280        }
2281
2282        /*      TODO: Other types are not supported yet.*/
2283/*        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_PARAM ))
2284        {
2285            GParamSpec* val = (*fp_g_value_get_param)(&value);
2286            printf( "Param: %p\n", val );
2287        }
2288        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_BOXED ))
2289        {
2290            gpointer* val = (*fp_g_value_get_boxed)(&value);
2291            printf( "Boxed: %p\n", val );
2292        }
2293        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_POINTER ))
2294        {
2295            gpointer* val = (*fp_g_value_get_pointer)(&value);
2296            printf( "Pointer: %p\n", val );
2297        }
2298        else if( (*fp_g_type_is_a)( param->value_type, G_TYPE_OBJECT ))
2299        {
2300            GObject* val = (GObject*)(*fp_g_value_get_object)(&value);
2301            printf( "Object: %p\n", val );
2302        }*/
2303    }
2304
2305    return NULL;
2306}
2307
2308static void gtk2_set_range_value(WidgetType widget_type, jdouble value,
2309                          jdouble min, jdouble max, jdouble visible)
2310{
2311    GtkAdjustment *adj;
2312
2313    gtk2_widget = gtk2_get_widget(widget_type);
2314
2315    adj = (*fp_gtk_range_get_adjustment)((GtkRange *)gtk2_widget);
2316    adj->value = (gdouble)value;
2317    adj->lower = (gdouble)min;
2318    adj->upper = (gdouble)max;
2319    adj->page_size = (gdouble)visible;
2320}
2321
2322/*************************************************/
2323static jobject create_Object(JNIEnv *env, jmethodID *cid,
2324                             const char* class_name,
2325                             const char* signature,
2326                             jvalue* value)
2327{
2328    jclass  class;
2329    jobject result;
2330
2331    class = (*env)->FindClass(env, class_name);
2332    if( class == NULL )
2333        return NULL; /* can't find/load the class, exception thrown */
2334
2335    if( *cid == NULL)
2336    {
2337        *cid = (*env)->GetMethodID(env, class, "<init>", signature);
2338        if( *cid == NULL )
2339        {
2340            (*env)->DeleteLocalRef(env, class);
2341            return NULL; /* can't find/get the method, exception thrown */
2342        }
2343    }
2344
2345    result = (*env)->NewObjectA(env, class, *cid, value);
2346
2347    (*env)->DeleteLocalRef(env, class);
2348    return result;
2349}
2350
2351jobject create_Boolean(JNIEnv *env, jboolean boolean_value)
2352{
2353    static jmethodID cid = NULL;
2354    jvalue value;
2355
2356    value.z = boolean_value;
2357
2358    return create_Object(env, &cid, "java/lang/Boolean", "(Z)V", &value);
2359}
2360
2361jobject create_Integer(JNIEnv *env, jint int_value)
2362{
2363    static jmethodID cid = NULL;
2364    jvalue value;
2365
2366    value.i = int_value;
2367
2368    return create_Object(env, &cid, "java/lang/Integer", "(I)V", &value);
2369}
2370
2371jobject create_Long(JNIEnv *env, jlong long_value)
2372{
2373    static jmethodID cid = NULL;
2374    jvalue value;
2375
2376    value.j = long_value;
2377
2378    return create_Object(env, &cid, "java/lang/Long", "(J)V", &value);
2379}
2380
2381jobject create_Float(JNIEnv *env, jfloat float_value)
2382{
2383    static jmethodID cid = NULL;
2384    jvalue value;
2385
2386    value.f = float_value;
2387
2388    return create_Object(env, &cid, "java/lang/Float", "(F)V", &value);
2389}
2390
2391jobject create_Double(JNIEnv *env, jdouble double_value)
2392{
2393    static jmethodID cid = NULL;
2394    jvalue value;
2395
2396    value.d = double_value;
2397
2398    return create_Object(env, &cid, "java/lang/Double", "(D)V", &value);
2399}
2400
2401jobject create_Character(JNIEnv *env, jchar char_value)
2402{
2403    static jmethodID cid = NULL;
2404    jvalue value;
2405
2406    value.c = char_value;
2407
2408    return create_Object(env, &cid, "java/lang/Character", "(C)V", &value);
2409}
2410
2411
2412jobject create_Insets(JNIEnv *env, GtkBorder *border)
2413{
2414    static jmethodID cid = NULL;
2415    jvalue values[4];
2416
2417    values[0].i = border->top;
2418    values[1].i = border->left;
2419    values[2].i = border->bottom;
2420    values[3].i = border->right;
2421
2422    return create_Object(env, &cid, "java/awt/Insets", "(IIII)V", values);
2423}
2424
2425/*********************************************/
2426static jstring gtk2_get_pango_font_name(JNIEnv *env, WidgetType widget_type)
2427{
2428    init_containers();
2429
2430    gtk2_widget = gtk2_get_widget(widget_type);
2431    jstring  result = NULL;
2432    GtkStyle* style = gtk2_widget->style;
2433
2434    if (style && style->font_desc)
2435    {
2436        gchar* val = (*fp_pango_font_description_to_string)(style->font_desc);
2437        result = (*env)->NewStringUTF(env, val);
2438        (*fp_g_free)( val );
2439    }
2440
2441    return result;
2442}
2443
2444/***********************************************/
2445static jobject get_string_property(JNIEnv *env, GtkSettings* settings, const gchar* key)
2446{
2447    jobject result = NULL;
2448    gchar*  strval = NULL;
2449
2450    (*fp_g_object_get)(settings, key, &strval, NULL);
2451    result = (*env)->NewStringUTF(env, strval);
2452    (*fp_g_free)(strval);
2453
2454    return result;
2455}
2456
2457static jobject get_integer_property(JNIEnv *env, GtkSettings* settings, const gchar* key)
2458{
2459    gint intval = NULL;
2460    (*fp_g_object_get)(settings, key, &intval, NULL);
2461    return create_Integer(env, intval);
2462}
2463
2464static jobject get_boolean_property(JNIEnv *env, GtkSettings* settings, const gchar* key)
2465{
2466    gint intval = NULL;
2467    (*fp_g_object_get)(settings, key, &intval, NULL);
2468    return create_Boolean(env, intval);
2469}
2470
2471static jobject gtk2_get_setting(JNIEnv *env, Setting property)
2472{
2473    GtkSettings* settings = (*fp_gtk_settings_get_default)();
2474
2475    switch (property)
2476    {
2477        case GTK_FONT_NAME:
2478            return get_string_property(env, settings, "gtk-font-name");
2479        case GTK_ICON_SIZES:
2480            return get_string_property(env, settings, "gtk-icon-sizes");
2481        case GTK_CURSOR_BLINK:
2482            return get_boolean_property(env, settings, "gtk-cursor-blink");
2483        case GTK_CURSOR_BLINK_TIME:
2484            return get_integer_property(env, settings, "gtk-cursor-blink-time");
2485    }
2486
2487    return NULL;
2488}
2489
2490static gboolean gtk2_get_drawable_data(JNIEnv *env, jintArray pixelArray, jint x,
2491     jint y, jint width, jint height, jint jwidth, int dx, int dy, jint scale) {
2492    GdkPixbuf *pixbuf;
2493    jint *ary;
2494
2495    GdkWindow *root = (*fp_gdk_get_default_root_window)();
2496
2497    pixbuf = (*fp_gdk_pixbuf_get_from_drawable)(NULL, root, NULL, x, y,
2498                                                    0, 0, width, height);
2499    if (pixbuf && scale != 1) {
2500        GdkPixbuf *scaledPixbuf;
2501        x /= scale;
2502        y /= scale;
2503        width /= scale;
2504        height /= scale;
2505        dx /= scale;
2506        dy /= scale;
2507        scaledPixbuf = (*fp_gdk_pixbuf_scale_simple)(pixbuf, width, height,
2508                                                     GDK_INTERP_BILINEAR);
2509        (*fp_g_object_unref)(pixbuf);
2510        pixbuf = scaledPixbuf;
2511    }
2512
2513    if (pixbuf) {
2514        int nchan = (*fp_gdk_pixbuf_get_n_channels)(pixbuf);
2515        int stride = (*fp_gdk_pixbuf_get_rowstride)(pixbuf);
2516
2517        if ((*fp_gdk_pixbuf_get_width)(pixbuf) == width
2518                && (*fp_gdk_pixbuf_get_height)(pixbuf) == height
2519                && (*fp_gdk_pixbuf_get_bits_per_sample)(pixbuf) == 8
2520                && (*fp_gdk_pixbuf_get_colorspace)(pixbuf) == GDK_COLORSPACE_RGB
2521                && nchan >= 3
2522                ) {
2523            guchar *p, *pix = (*fp_gdk_pixbuf_get_pixels)(pixbuf);
2524
2525            ary = (*env)->GetPrimitiveArrayCritical(env, pixelArray, NULL);
2526            if (ary) {
2527                jint _x, _y;
2528                int index;
2529                for (_y = 0; _y < height; _y++) {
2530                    for (_x = 0; _x < width; _x++) {
2531                        p = pix + _y * stride + _x * nchan;
2532
2533                        index = (_y + dy) * jwidth + (_x + dx);
2534                        ary[index] = 0xff000000
2535                                        | (p[0] << 16)
2536                                        | (p[1] << 8)
2537                                        | (p[2]);
2538
2539                    }
2540                }
2541                (*env)->ReleasePrimitiveArrayCritical(env, pixelArray, ary, 0);
2542            }
2543        }
2544        (*fp_g_object_unref)(pixbuf);
2545    }
2546    return JNI_FALSE;
2547}
2548
2549static GdkWindow* gtk2_get_window(void *widget) {
2550    return ((GtkWidget*)widget)->window;
2551}
2552
2553void gtk2_init(GtkApi* gtk) {
2554    gtk->version = GTK_2;
2555
2556    gtk->show_uri_load = &gtk2_show_uri_load;
2557    gtk->unload = &gtk2_unload;
2558    gtk->flush_event_loop = &flush_gtk_event_loop;
2559    gtk->gtk_check_version = fp_gtk_check_version;
2560    gtk->get_setting = &gtk2_get_setting;
2561
2562    gtk->paint_arrow = &gtk2_paint_arrow;
2563    gtk->paint_box = &gtk2_paint_box;
2564    gtk->paint_box_gap = &gtk2_paint_box_gap;
2565    gtk->paint_expander = &gtk2_paint_expander;
2566    gtk->paint_extension = &gtk2_paint_extension;
2567    gtk->paint_flat_box = &gtk2_paint_flat_box;
2568    gtk->paint_focus = &gtk2_paint_focus;
2569    gtk->paint_handle = &gtk2_paint_handle;
2570    gtk->paint_hline = &gtk2_paint_hline;
2571    gtk->paint_vline = &gtk2_paint_vline;
2572    gtk->paint_option = &gtk2_paint_option;
2573    gtk->paint_shadow = &gtk2_paint_shadow;
2574    gtk->paint_slider = &gtk2_paint_slider;
2575    gtk->paint_background = &gtk_paint_background;
2576    gtk->paint_check = &gtk2_paint_check;
2577    gtk->set_range_value = &gtk2_set_range_value;
2578
2579    gtk->init_painting = &gtk2_init_painting;
2580    gtk->copy_image = &gtk2_copy_image;
2581
2582    gtk->get_xthickness = &gtk2_get_xthickness;
2583    gtk->get_ythickness = &gtk2_get_ythickness;
2584    gtk->get_color_for_state = &gtk2_get_color_for_state;
2585    gtk->get_class_value = &gtk2_get_class_value;
2586
2587    gtk->get_pango_font_name = &gtk2_get_pango_font_name;
2588    gtk->get_icon_data = &gtk2_get_icon_data;
2589    gtk->get_file_icon_data = &gtk2_get_file_icon_data;
2590    gtk->gdk_threads_enter = fp_gdk_threads_enter;
2591    gtk->gdk_threads_leave = fp_gdk_threads_leave;
2592    gtk->gtk_show_uri = fp_gtk_show_uri;
2593    gtk->get_drawable_data = &gtk2_get_drawable_data;
2594    gtk->g_free = fp_g_free;
2595
2596    gtk->gtk_file_chooser_get_filename = fp_gtk_file_chooser_get_filename;
2597    gtk->gtk_widget_hide = fp_gtk_widget_hide;
2598    gtk->gtk_main_quit = fp_gtk_main_quit;
2599    gtk->gtk_file_chooser_dialog_new = fp_gtk_file_chooser_dialog_new;
2600    gtk->gtk_file_chooser_set_current_folder =
2601                          fp_gtk_file_chooser_set_current_folder;
2602    gtk->gtk_file_chooser_set_filename = fp_gtk_file_chooser_set_filename;
2603    gtk->gtk_file_chooser_set_current_name =
2604                          fp_gtk_file_chooser_set_current_name;
2605    gtk->gtk_file_filter_add_custom = fp_gtk_file_filter_add_custom;
2606    gtk->gtk_file_chooser_set_filter = fp_gtk_file_chooser_set_filter;
2607    gtk->gtk_file_chooser_get_type = fp_gtk_file_chooser_get_type;
2608    gtk->gtk_file_filter_new = fp_gtk_file_filter_new;
2609    gtk->gtk_file_chooser_set_do_overwrite_confirmation =
2610                          fp_gtk_file_chooser_set_do_overwrite_confirmation;
2611    gtk->gtk_file_chooser_set_select_multiple =
2612                          fp_gtk_file_chooser_set_select_multiple;
2613    gtk->gtk_file_chooser_get_current_folder =
2614                          fp_gtk_file_chooser_get_current_folder;
2615    gtk->gtk_file_chooser_get_filenames = fp_gtk_file_chooser_get_filenames;
2616    gtk->gtk_g_slist_length = fp_gtk_g_slist_length;
2617    gtk->g_signal_connect_data = fp_g_signal_connect_data;
2618    gtk->gtk_widget_show = fp_gtk_widget_show;
2619    gtk->gtk_main = fp_gtk_main;
2620    gtk->gtk_main_level = fp_gtk_main_level;
2621    gtk->g_path_get_dirname = fp_g_path_get_dirname;
2622    gtk->gdk_x11_drawable_get_xid = fp_gdk_x11_drawable_get_xid;
2623    gtk->gtk_widget_destroy = fp_gtk_widget_destroy;
2624    gtk->gtk_window_present = fp_gtk_window_present;
2625    gtk->gtk_window_move = fp_gtk_window_move;
2626    gtk->gtk_window_resize = fp_gtk_window_resize;
2627    gtk->get_window = &gtk2_get_window;
2628
2629    gtk->g_object_unref = fp_g_object_unref;
2630    gtk->g_list_append = fp_g_list_append;
2631    gtk->g_list_free = fp_g_list_free;
2632    gtk->g_list_free_full = fp_g_list_free_full;
2633}
2634