1 /*
   2  * Copyright (c) 2005, 2020, 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 
  26 #ifdef HEADLESS
  27     #error This file should not be included in headless library
  28 #endif
  29 
  30 #include <dlfcn.h>
  31 #include <setjmp.h>
  32 #include <X11/Xlib.h>
  33 #include <limits.h>
  34 #include <string.h>
  35 #include "gtk3_interface.h"
  36 #include "java_awt_Transparency.h"
  37 #include "sizecalc.h"
  38 #include <jni_util.h>
  39 #include <stdio.h>
  40 #include "awt.h"
  41 
  42 static void *gtk3_libhandle = NULL;
  43 static void *gthread_libhandle = NULL;
  44 
  45 static jmp_buf j;
  46 
  47 /* Widgets */
  48 static GtkWidget *gtk3_widget = NULL;
  49 static GtkWidget *gtk3_window = NULL;
  50 static GtkFixed  *gtk3_fixed  = NULL;
  51 static GtkStyleProvider *gtk3_css = NULL;
  52 
  53 /* Paint system */
  54 static cairo_surface_t *surface = NULL;
  55 static cairo_t *cr = NULL;
  56 
  57 static const char ENV_PREFIX[] = "GTK_MODULES=";
  58 
  59 static GtkWidget *gtk3_widgets[_GTK_WIDGET_TYPE_SIZE];
  60 
  61 static void throw_exception(JNIEnv *env, const char* name, const char* message)
  62 {
  63     jclass class = (*env)->FindClass(env, name);
  64 
  65     if (class != NULL)
  66         (*env)->ThrowNew(env, class, message);
  67 
  68     (*env)->DeleteLocalRef(env, class);
  69 }
  70 
  71 static void gtk3_add_state(GtkWidget *widget, GtkStateType state) {
  72     GtkStateType old_state = fp_gtk_widget_get_state(widget);
  73     fp_gtk_widget_set_state(widget, old_state | state);
  74 }
  75 
  76 static void gtk3_remove_state(GtkWidget *widget, GtkStateType state) {
  77     GtkStateType old_state = fp_gtk_widget_get_state(widget);
  78     fp_gtk_widget_set_state(widget, old_state & ~state);
  79 }
  80 
  81 /* This is a workaround for the bug:
  82  * http://sourceware.org/bugzilla/show_bug.cgi?id=1814
  83  * (dlsym/dlopen clears dlerror state)
  84  * This bug is specific to Linux, but there is no harm in
  85  * applying this workaround on Solaris as well.
  86  */
  87 static void* dl_symbol(const char* name)
  88 {
  89     void* result = dlsym(gtk3_libhandle, name);
  90     if (!result)
  91         longjmp(j, NO_SYMBOL_EXCEPTION);
  92 
  93     return result;
  94 }
  95 
  96 static void* dl_symbol_gthread(const char* name)
  97 {
  98     void* result = dlsym(gthread_libhandle, name);
  99     if (!result)
 100         longjmp(j, NO_SYMBOL_EXCEPTION);
 101 
 102     return result;
 103 }
 104 
 105 gboolean gtk3_check(const char* lib_name, gboolean load)
 106 {
 107     if (gtk3_libhandle != NULL) {
 108         /* We've already successfully opened the GTK libs, so return true. */
 109         return TRUE;
 110     } else {
 111 #ifdef RTLD_NOLOAD
 112         void *lib = dlopen(lib_name, RTLD_LAZY | RTLD_NOLOAD);
 113         if (!load || lib != NULL) {
 114             return lib != NULL;
 115         }
 116 #else
 117 #ifdef _AIX
 118         /* On AIX we could implement this with the help of loadquery(L_GETINFO, ..)  */
 119         /* (see reload_table() in hotspot/src/os/aix/vm/loadlib_aix.cpp) but it is   */
 120         /* probably not worth it because most AIX servers don't have GTK libs anyway */
 121 #endif
 122 #endif
 123         return dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL) != NULL;
 124     }
 125 }
 126 
 127 #define ADD_SUPPORTED_ACTION(actionStr)                                        \
 128 do {                                                                           \
 129     jfieldID fld_action = (*env)->GetStaticFieldID(env, cls_action, actionStr, \
 130                                                  "Ljava/awt/Desktop$Action;"); \
 131     if (!(*env)->ExceptionCheck(env)) {                                        \
 132         jobject action = (*env)->GetStaticObjectField(env, cls_action,         \
 133                                                                   fld_action); \
 134         (*env)->CallBooleanMethod(env, supportedActions, mid_arrayListAdd,     \
 135                                                                       action); \
 136     } else {                                                                   \
 137         (*env)->ExceptionClear(env);                                           \
 138     }                                                                          \
 139 } while(0);
 140 
 141 
 142 static void update_supported_actions(JNIEnv *env) {
 143     GVfs * (*fp_g_vfs_get_default) (void);
 144     const gchar * const * (*fp_g_vfs_get_supported_uri_schemes) (GVfs * vfs);
 145     const gchar * const * schemes = NULL;
 146 
 147     jclass cls_action = (*env)->FindClass(env, "java/awt/Desktop$Action");
 148     CHECK_NULL(cls_action);
 149     jclass cls_xDesktopPeer = (*env)->
 150                                      FindClass(env, "sun/awt/X11/XDesktopPeer");
 151     CHECK_NULL(cls_xDesktopPeer);
 152     jfieldID fld_supportedActions = (*env)->GetStaticFieldID(env,
 153                       cls_xDesktopPeer, "supportedActions", "Ljava/util/List;");
 154     CHECK_NULL(fld_supportedActions);
 155     jobject supportedActions = (*env)->GetStaticObjectField(env,
 156                                         cls_xDesktopPeer, fld_supportedActions);
 157 
 158     jclass cls_arrayList = (*env)->FindClass(env, "java/util/ArrayList");
 159     CHECK_NULL(cls_arrayList);
 160     jmethodID mid_arrayListAdd = (*env)->GetMethodID(env, cls_arrayList, "add",
 161                                                        "(Ljava/lang/Object;)Z");
 162     CHECK_NULL(mid_arrayListAdd);
 163     jmethodID mid_arrayListClear = (*env)->GetMethodID(env, cls_arrayList,
 164                                                                 "clear", "()V");
 165     CHECK_NULL(mid_arrayListClear);
 166 
 167     (*env)->CallVoidMethod(env, supportedActions, mid_arrayListClear);
 168 
 169     ADD_SUPPORTED_ACTION("OPEN");
 170 
 171     /**
 172      * gtk_show_uri() documentation says:
 173      *
 174      * > you need to install gvfs to get support for uri schemes such as http://
 175      * > or ftp://, as only local files are handled by GIO itself.
 176      *
 177      * So OPEN action was safely added here.
 178      * However, it looks like Solaris 11 have gvfs support only for 32-bit
 179      * applications only by default.
 180      */
 181 
 182     fp_g_vfs_get_default = dl_symbol("g_vfs_get_default");
 183     fp_g_vfs_get_supported_uri_schemes =
 184                            dl_symbol("g_vfs_get_supported_uri_schemes");
 185     dlerror();
 186 
 187     if (fp_g_vfs_get_default && fp_g_vfs_get_supported_uri_schemes) {
 188         GVfs * vfs = fp_g_vfs_get_default();
 189         schemes = vfs ? fp_g_vfs_get_supported_uri_schemes(vfs) : NULL;
 190         if (schemes) {
 191             int i = 0;
 192             while (schemes[i]) {
 193                 if (strcmp(schemes[i], "http") == 0) {
 194                     ADD_SUPPORTED_ACTION("BROWSE");
 195                     ADD_SUPPORTED_ACTION("MAIL");
 196                     break;
 197                 }
 198                 i++;
 199             }
 200         }
 201     } else {
 202 #ifdef DEBUG
 203         fprintf(stderr, "Cannot load g_vfs_get_supported_uri_schemes\n");
 204 #endif /* DEBUG */
 205     }
 206 
 207 }
 208 /**
 209  * Functions for awt_Desktop.c
 210  */
 211 static gboolean gtk3_show_uri_load(JNIEnv *env) {
 212     gboolean success = FALSE;
 213     dlerror();
 214     fp_gtk_show_uri = dl_symbol("gtk_show_uri");
 215     const char *dlsym_error = dlerror();
 216     if (dlsym_error) {
 217 #ifdef DEBUG
 218         fprintf (stderr, "Cannot load symbol: %s \n", dlsym_error);
 219 #endif /* DEBUG */
 220     } else if (fp_gtk_show_uri == NULL) {
 221 #ifdef DEBUG
 222         fprintf(stderr, "dlsym(gtk_show_uri) returned NULL\n");
 223 #endif /* DEBUG */
 224     } else {
 225         gtk->gtk_show_uri = fp_gtk_show_uri;
 226         update_supported_actions(env);
 227         success = TRUE;
 228     }
 229     return success;
 230 }
 231 
 232 /**
 233  * Functions for sun_awt_X11_GtkFileDialogPeer.c
 234  */
 235 static void gtk3_file_chooser_load()
 236 {
 237     fp_gtk_file_chooser_get_filename = dl_symbol(
 238             "gtk_file_chooser_get_filename");
 239     fp_gtk_file_chooser_dialog_new = dl_symbol("gtk_file_chooser_dialog_new");
 240     fp_gtk_file_chooser_set_current_folder = dl_symbol(
 241             "gtk_file_chooser_set_current_folder");
 242     fp_gtk_file_chooser_set_filename = dl_symbol(
 243             "gtk_file_chooser_set_filename");
 244     fp_gtk_file_chooser_set_current_name = dl_symbol(
 245             "gtk_file_chooser_set_current_name");
 246     fp_gtk_file_filter_add_custom = dl_symbol("gtk_file_filter_add_custom");
 247     fp_gtk_file_chooser_set_filter = dl_symbol("gtk_file_chooser_set_filter");
 248     fp_gtk_file_chooser_get_type = dl_symbol("gtk_file_chooser_get_type");
 249     fp_gtk_file_filter_new = dl_symbol("gtk_file_filter_new");
 250     fp_gtk_file_chooser_set_do_overwrite_confirmation = dl_symbol(
 251                 "gtk_file_chooser_set_do_overwrite_confirmation");
 252     fp_gtk_file_chooser_set_select_multiple = dl_symbol(
 253             "gtk_file_chooser_set_select_multiple");
 254     fp_gtk_file_chooser_get_current_folder = dl_symbol(
 255             "gtk_file_chooser_get_current_folder");
 256     fp_gtk_file_chooser_get_filenames = dl_symbol(
 257             "gtk_file_chooser_get_filenames");
 258     fp_gtk_g_slist_length = dl_symbol("g_slist_length");
 259     fp_gdk_x11_drawable_get_xid = dl_symbol("gdk_x11_window_get_xid");
 260 }
 261 
 262 static void empty() {}
 263 
 264 static gboolean gtk3_version_3_10 = TRUE;
 265 static gboolean gtk3_version_3_14 = FALSE;
 266 static gboolean gtk3_version_3_20 = FALSE;
 267 
 268 GtkApi* gtk3_load(JNIEnv *env, const char* lib_name)
 269 {
 270     gboolean result;
 271     int i;
 272     int (*handler)();
 273     int (*io_handler)();
 274     char *gtk_modules_env;
 275     gtk3_libhandle = dlopen(lib_name, RTLD_LAZY | RTLD_LOCAL);
 276     if (gtk3_libhandle == NULL) {
 277         return FALSE;
 278     }
 279 
 280     gthread_libhandle = dlopen(GTHREAD_LIB_VERSIONED, RTLD_LAZY | RTLD_LOCAL);
 281     if (gthread_libhandle == NULL) {
 282         gthread_libhandle = dlopen(GTHREAD_LIB, RTLD_LAZY | RTLD_LOCAL);
 283         if (gthread_libhandle == NULL)
 284             return FALSE;
 285     }
 286 
 287     if (setjmp(j) == 0)
 288     {
 289         fp_gtk_check_version = dl_symbol("gtk_check_version");
 290 
 291         /* GLib */
 292         fp_glib_check_version = dlsym(gtk3_libhandle, "glib_check_version");
 293         if (!fp_glib_check_version) {
 294             dlerror();
 295         }
 296         fp_g_free = dl_symbol("g_free");
 297         fp_g_object_unref = dl_symbol("g_object_unref");
 298 
 299         fp_g_main_context_iteration =
 300             dl_symbol("g_main_context_iteration");
 301 
 302         fp_g_value_init = dl_symbol("g_value_init");
 303         fp_g_type_is_a = dl_symbol("g_type_is_a");
 304         fp_g_value_get_boolean = dl_symbol("g_value_get_boolean");
 305         fp_g_value_get_char = dl_symbol("g_value_get_char");
 306         fp_g_value_get_uchar = dl_symbol("g_value_get_uchar");
 307         fp_g_value_get_int = dl_symbol("g_value_get_int");
 308         fp_g_value_get_uint = dl_symbol("g_value_get_uint");
 309         fp_g_value_get_long = dl_symbol("g_value_get_long");
 310         fp_g_value_get_ulong = dl_symbol("g_value_get_ulong");
 311         fp_g_value_get_int64 = dl_symbol("g_value_get_int64");
 312         fp_g_value_get_uint64 = dl_symbol("g_value_get_uint64");
 313         fp_g_value_get_float = dl_symbol("g_value_get_float");
 314         fp_g_value_get_double = dl_symbol("g_value_get_double");
 315         fp_g_value_get_string = dl_symbol("g_value_get_string");
 316         fp_g_value_get_enum = dl_symbol("g_value_get_enum");
 317         fp_g_value_get_flags = dl_symbol("g_value_get_flags");
 318         fp_g_value_get_param = dl_symbol("g_value_get_param");
 319         fp_g_value_get_boxed = dl_symbol("g_value_get_boxed");
 320         fp_g_value_get_pointer = dl_symbol("g_value_get_pointer");
 321 
 322         fp_g_object_get = dl_symbol("g_object_get");
 323         fp_g_object_set = dl_symbol("g_object_set");
 324 
 325         fp_g_str_has_prefix = dl_symbol("g_str_has_prefix");
 326         fp_g_strsplit = dl_symbol("g_strsplit");
 327         fp_g_strfreev = dl_symbol("g_strfreev");
 328 
 329         /* GDK */
 330         fp_gdk_get_default_root_window =
 331             dl_symbol("gdk_get_default_root_window");
 332 
 333         /* Pixbuf */
 334         fp_gdk_pixbuf_new = dl_symbol("gdk_pixbuf_new");
 335         fp_gdk_pixbuf_new_from_file =
 336                 dl_symbol("gdk_pixbuf_new_from_file");
 337         fp_gdk_pixbuf_get_from_drawable =
 338                     dl_symbol("gdk_pixbuf_get_from_window");
 339         fp_gdk_pixbuf_get_width = dl_symbol("gdk_pixbuf_get_width");
 340         fp_gdk_pixbuf_get_height = dl_symbol("gdk_pixbuf_get_height");
 341         fp_gdk_pixbuf_get_pixels = dl_symbol("gdk_pixbuf_get_pixels");
 342         fp_gdk_pixbuf_get_rowstride =
 343                 dl_symbol("gdk_pixbuf_get_rowstride");
 344         fp_gdk_pixbuf_get_has_alpha =
 345                 dl_symbol("gdk_pixbuf_get_has_alpha");
 346         fp_gdk_pixbuf_get_bits_per_sample =
 347                 dl_symbol("gdk_pixbuf_get_bits_per_sample");
 348         fp_gdk_pixbuf_get_n_channels =
 349                 dl_symbol("gdk_pixbuf_get_n_channels");
 350         fp_gdk_pixbuf_get_colorspace =
 351                 dl_symbol("gdk_pixbuf_get_colorspace");
 352 
 353         fp_cairo_image_surface_create = dl_symbol("cairo_image_surface_create");
 354         fp_cairo_surface_destroy = dl_symbol("cairo_surface_destroy");
 355         fp_cairo_create = dl_symbol("cairo_create");
 356         fp_cairo_destroy = dl_symbol("cairo_destroy");
 357         fp_cairo_fill = dl_symbol("cairo_fill");
 358         fp_cairo_rectangle = dl_symbol("cairo_rectangle");
 359         fp_cairo_set_source_rgb = dl_symbol("cairo_set_source_rgb");
 360         fp_cairo_set_source_rgba = dl_symbol("cairo_set_source_rgba");
 361         fp_cairo_surface_flush = dl_symbol("cairo_surface_flush");
 362         fp_cairo_paint = dl_symbol("cairo_paint");
 363         fp_cairo_clip = dl_symbol("cairo_clip");
 364         fp_cairo_image_surface_get_data =
 365                        dl_symbol("cairo_image_surface_get_data");
 366         fp_cairo_image_surface_get_stride =
 367                        dl_symbol("cairo_image_surface_get_stride");
 368 
 369         fp_gdk_pixbuf_get_from_surface =
 370                        dl_symbol("gdk_pixbuf_get_from_surface");
 371 
 372         fp_gtk_widget_get_state = dl_symbol("gtk_widget_get_state");
 373         fp_gtk_widget_set_state = dl_symbol("gtk_widget_set_state");
 374 
 375         fp_gtk_widget_is_focus = dl_symbol("gtk_widget_is_focus");
 376         fp_gtk_widget_set_allocation = dl_symbol("gtk_widget_set_allocation");
 377         fp_gtk_widget_get_parent = dl_symbol("gtk_widget_get_parent");
 378         fp_gtk_widget_get_window = dl_symbol("gtk_widget_get_window");
 379 
 380         fp_gtk_widget_get_style_context =
 381                        dl_symbol("gtk_widget_get_style_context");
 382         fp_gtk_style_context_get_color =
 383                        dl_symbol("gtk_style_context_get_color");
 384         fp_gtk_style_context_get_background_color =
 385                        dl_symbol("gtk_style_context_get_background_color");
 386         fp_gtk_widget_get_state_flags = dl_symbol("gtk_widget_get_state_flags");
 387         fp_gtk_style_context_set_state =
 388                        dl_symbol("gtk_style_context_set_state");
 389         fp_gtk_style_context_add_class =
 390                        dl_symbol("gtk_style_context_add_class");
 391         fp_gtk_style_context_save = dl_symbol("gtk_style_context_save");
 392         fp_gtk_style_context_restore = dl_symbol("gtk_style_context_restore");
 393         fp_gtk_render_check = dl_symbol("gtk_render_check");
 394         fp_gtk_render_option = dl_symbol("gtk_render_option");
 395         fp_gtk_render_extension = dl_symbol("gtk_render_extension");
 396         fp_gtk_render_expander = dl_symbol("gtk_render_expander");
 397         fp_gtk_render_frame_gap = dl_symbol("gtk_render_frame_gap");
 398         fp_gtk_render_line = dl_symbol("gtk_render_line");
 399         fp_gtk_widget_render_icon_pixbuf =
 400                       dl_symbol("gtk_widget_render_icon_pixbuf");
 401         if (fp_gtk_check_version(3, 10, 0)) {
 402             gtk3_version_3_10 = FALSE;
 403         } else {
 404             fp_gdk_window_create_similar_image_surface =
 405                        dl_symbol("gdk_window_create_similar_image_surface");
 406             fp_gdk_window_get_scale_factor =
 407                     dl_symbol("gdk_window_get_scale_factor");
 408         }
 409         gtk3_version_3_14 = !fp_gtk_check_version(3, 14, 0);
 410 
 411         if (!fp_gtk_check_version(3, 20, 0)) {
 412             gtk3_version_3_20 = TRUE;
 413             fp_gtk_widget_path_copy = dl_symbol("gtk_widget_path_copy");
 414             fp_gtk_widget_path_new = dl_symbol("gtk_widget_path_new");
 415             fp_gtk_widget_path_append_type = dl_symbol("gtk_widget_path_append_type");
 416             fp_gtk_widget_path_iter_set_object_name = dl_symbol("gtk_widget_path_iter_set_object_name");
 417             fp_gtk_style_context_set_path = dl_symbol("gtk_style_context_set_path");
 418             fp_gtk_widget_path_unref = dl_symbol("gtk_widget_path_unref");
 419             fp_gtk_style_context_get_path = dl_symbol("gtk_style_context_get_path");
 420             fp_gtk_style_context_new = dl_symbol("gtk_style_context_new");
 421         }
 422 
 423         fp_gdk_window_create_similar_surface =
 424                       dl_symbol("gdk_window_create_similar_surface");
 425         fp_gtk_settings_get_for_screen =
 426                       dl_symbol("gtk_settings_get_for_screen");
 427         fp_gtk_widget_get_screen = dl_symbol("gtk_widget_get_screen");
 428         fp_gtk_css_provider_get_named = dl_symbol("gtk_css_provider_get_named");
 429         fp_gtk_style_context_add_provider =
 430                       dl_symbol("gtk_style_context_add_provider");
 431         fp_gtk_render_frame = dl_symbol("gtk_render_frame");
 432         fp_gtk_render_focus = dl_symbol("gtk_render_focus");
 433         fp_gtk_render_handle = dl_symbol("gtk_render_handle");
 434         fp_gtk_render_arrow = dl_symbol("gtk_render_arrow");
 435 
 436         fp_gtk_style_context_get_property =
 437                       dl_symbol("gtk_style_context_get_property");
 438         fp_gtk_scrolled_window_set_shadow_type =
 439                       dl_symbol("gtk_scrolled_window_set_shadow_type");
 440         fp_gtk_render_slider = dl_symbol("gtk_render_slider");
 441         fp_gtk_style_context_get_padding =
 442                       dl_symbol("gtk_style_context_get_padding");
 443         fp_gtk_range_set_inverted = dl_symbol("gtk_range_set_inverted");
 444         fp_gtk_style_context_get_font = dl_symbol("gtk_style_context_get_font");
 445         fp_gtk_widget_get_allocated_width =
 446                       dl_symbol("gtk_widget_get_allocated_width");
 447         fp_gtk_widget_get_allocated_height =
 448                       dl_symbol("gtk_widget_get_allocated_height");
 449         fp_gtk_icon_theme_get_default = dl_symbol("gtk_icon_theme_get_default");
 450         fp_gtk_icon_theme_load_icon = dl_symbol("gtk_icon_theme_load_icon");
 451 
 452         fp_gtk_adjustment_set_lower = dl_symbol("gtk_adjustment_set_lower");
 453         fp_gtk_adjustment_set_page_increment =
 454                       dl_symbol("gtk_adjustment_set_page_increment");
 455         fp_gtk_adjustment_set_page_size =
 456                       dl_symbol("gtk_adjustment_set_page_size");
 457         fp_gtk_adjustment_set_step_increment =
 458                       dl_symbol("gtk_adjustment_set_step_increment");
 459         fp_gtk_adjustment_set_upper = dl_symbol("gtk_adjustment_set_upper");
 460         fp_gtk_adjustment_set_value = dl_symbol("gtk_adjustment_set_value");
 461 
 462         fp_gtk_render_activity = dl_symbol("gtk_render_activity");
 463         fp_gtk_render_background = dl_symbol("gtk_render_background");
 464         fp_gtk_style_context_has_class =
 465                       dl_symbol("gtk_style_context_has_class");
 466 
 467         fp_gtk_style_context_set_junction_sides =
 468                       dl_symbol("gtk_style_context_set_junction_sides");
 469         fp_gtk_style_context_add_region =
 470                       dl_symbol("gtk_style_context_add_region");
 471 
 472         fp_gtk_init_check = dl_symbol("gtk_init_check");
 473 
 474         /* GTK widgets */
 475         fp_gtk_arrow_new = dl_symbol("gtk_arrow_new");
 476         fp_gtk_button_new = dl_symbol("gtk_button_new");
 477         fp_gtk_spin_button_new = dl_symbol("gtk_spin_button_new");
 478         fp_gtk_check_button_new = dl_symbol("gtk_check_button_new");
 479         fp_gtk_check_menu_item_new =
 480                 dl_symbol("gtk_check_menu_item_new");
 481         fp_gtk_color_selection_dialog_new =
 482                 dl_symbol("gtk_color_selection_dialog_new");
 483         fp_gtk_entry_new = dl_symbol("gtk_entry_new");
 484         fp_gtk_fixed_new = dl_symbol("gtk_fixed_new");
 485         fp_gtk_handle_box_new = dl_symbol("gtk_handle_box_new");
 486         fp_gtk_image_new = dl_symbol("gtk_image_new");
 487         fp_gtk_paned_new = dl_symbol("gtk_paned_new");
 488         fp_gtk_scale_new = dl_symbol("gtk_scale_new");
 489         fp_gtk_hscrollbar_new = dl_symbol("gtk_hscrollbar_new");
 490         fp_gtk_vscrollbar_new = dl_symbol("gtk_vscrollbar_new");
 491         fp_gtk_hseparator_new = dl_symbol("gtk_hseparator_new");
 492         fp_gtk_vseparator_new = dl_symbol("gtk_vseparator_new");
 493         fp_gtk_label_new = dl_symbol("gtk_label_new");
 494         fp_gtk_menu_new = dl_symbol("gtk_menu_new");
 495         fp_gtk_menu_bar_new = dl_symbol("gtk_menu_bar_new");
 496         fp_gtk_menu_item_new = dl_symbol("gtk_menu_item_new");
 497         fp_gtk_menu_item_set_submenu =
 498                 dl_symbol("gtk_menu_item_set_submenu");
 499         fp_gtk_notebook_new = dl_symbol("gtk_notebook_new");
 500         fp_gtk_progress_bar_new =
 501             dl_symbol("gtk_progress_bar_new");
 502         fp_gtk_progress_bar_set_orientation =
 503             dl_symbol("gtk_orientable_set_orientation");
 504         fp_gtk_radio_button_new =
 505             dl_symbol("gtk_radio_button_new");
 506         fp_gtk_radio_menu_item_new =
 507             dl_symbol("gtk_radio_menu_item_new");
 508         fp_gtk_scrolled_window_new =
 509             dl_symbol("gtk_scrolled_window_new");
 510         fp_gtk_separator_menu_item_new =
 511             dl_symbol("gtk_separator_menu_item_new");
 512         fp_gtk_text_view_new = dl_symbol("gtk_text_view_new");
 513         fp_gtk_toggle_button_new =
 514             dl_symbol("gtk_toggle_button_new");
 515         fp_gtk_toolbar_new = dl_symbol("gtk_toolbar_new");
 516         fp_gtk_tree_view_new = dl_symbol("gtk_tree_view_new");
 517         fp_gtk_viewport_new = dl_symbol("gtk_viewport_new");
 518         fp_gtk_window_new = dl_symbol("gtk_window_new");
 519         fp_gtk_window_present = dl_symbol("gtk_window_present");
 520         fp_gtk_window_move = dl_symbol("gtk_window_move");
 521         fp_gtk_window_resize = dl_symbol("gtk_window_resize");
 522 
 523           fp_gtk_dialog_new = dl_symbol("gtk_dialog_new");
 524         fp_gtk_frame_new = dl_symbol("gtk_frame_new");
 525 
 526         fp_gtk_adjustment_new = dl_symbol("gtk_adjustment_new");
 527         fp_gtk_container_add = dl_symbol("gtk_container_add");
 528         fp_gtk_menu_shell_append =
 529             dl_symbol("gtk_menu_shell_append");
 530         fp_gtk_widget_realize = dl_symbol("gtk_widget_realize");
 531         fp_gtk_widget_destroy = dl_symbol("gtk_widget_destroy");
 532         fp_gtk_widget_render_icon =
 533             dl_symbol("gtk_widget_render_icon");
 534         fp_gtk_widget_set_name =
 535             dl_symbol("gtk_widget_set_name");
 536         fp_gtk_widget_set_parent =
 537             dl_symbol("gtk_widget_set_parent");
 538         fp_gtk_widget_set_direction =
 539             dl_symbol("gtk_widget_set_direction");
 540         fp_gtk_widget_style_get =
 541             dl_symbol("gtk_widget_style_get");
 542         fp_gtk_widget_class_install_style_property =
 543             dl_symbol("gtk_widget_class_install_style_property");
 544         fp_gtk_widget_class_find_style_property =
 545             dl_symbol("gtk_widget_class_find_style_property");
 546         fp_gtk_widget_style_get_property =
 547             dl_symbol("gtk_widget_style_get_property");
 548         fp_pango_font_description_to_string =
 549             dl_symbol("pango_font_description_to_string");
 550         fp_gtk_settings_get_default =
 551             dl_symbol("gtk_settings_get_default");
 552         fp_gtk_widget_get_settings =
 553             dl_symbol("gtk_widget_get_settings");
 554         fp_gtk_border_get_type =  dl_symbol("gtk_border_get_type");
 555         fp_gtk_arrow_set = dl_symbol("gtk_arrow_set");
 556         fp_gtk_widget_size_request =
 557             dl_symbol("gtk_widget_size_request");
 558         fp_gtk_range_get_adjustment =
 559             dl_symbol("gtk_range_get_adjustment");
 560 
 561         fp_gtk_widget_hide = dl_symbol("gtk_widget_hide");
 562         fp_gtk_main_quit = dl_symbol("gtk_main_quit");
 563         fp_g_signal_connect_data = dl_symbol("g_signal_connect_data");
 564         fp_gtk_widget_show = dl_symbol("gtk_widget_show");
 565         fp_gtk_main = dl_symbol("gtk_main");
 566 
 567         fp_g_path_get_dirname = dl_symbol("g_path_get_dirname");
 568 
 569         fp_gdk_threads_init = dl_symbol("gdk_threads_init");
 570         fp_gdk_threads_enter = dl_symbol("gdk_threads_enter");
 571         fp_gdk_threads_leave = dl_symbol("gdk_threads_leave");
 572 
 573         /**
 574          * Functions for sun_awt_X11_GtkFileDialogPeer.c
 575          */
 576         gtk3_file_chooser_load();
 577 
 578         fp_gtk_combo_box_new = dlsym(gtk3_libhandle, "gtk_combo_box_new");
 579         fp_gtk_combo_box_entry_new = dlsym(gtk3_libhandle,
 580                                                 "gtk_combo_box_new_with_entry");
 581         fp_gtk_separator_tool_item_new = dlsym(gtk3_libhandle,
 582                                                  "gtk_separator_tool_item_new");
 583         fp_g_list_append = dl_symbol("g_list_append");
 584         fp_g_list_free = dl_symbol("g_list_free");
 585         fp_g_list_free_full = dl_symbol("g_list_free_full");
 586     }
 587     /* Now we have only one kind of exceptions: NO_SYMBOL_EXCEPTION
 588      * Otherwise we can check the return value of setjmp method.
 589      */
 590     else
 591     {
 592         dlclose(gtk3_libhandle);
 593         gtk3_libhandle = NULL;
 594 
 595         dlclose(gthread_libhandle);
 596         gthread_libhandle = NULL;
 597 
 598         return NULL;
 599     }
 600 
 601     /*
 602      * Strip the AT-SPI GTK_MODULES if present
 603      */
 604     gtk_modules_env = getenv ("GTK_MODULES");
 605     if ((gtk_modules_env && strstr(gtk_modules_env, "atk-bridge")) ||
 606         (gtk_modules_env && strstr(gtk_modules_env, "gail"))) {
 607         /* careful, strtok modifies its args */
 608         gchar *tmp_env = strdup(gtk_modules_env);
 609         if (tmp_env) {
 610             /* the new env will be smaller than the old one */
 611             gchar *s, *new_env = SAFE_SIZE_STRUCT_ALLOC(malloc,
 612                     sizeof(ENV_PREFIX), 1, strlen (gtk_modules_env));
 613 
 614             if (new_env) {
 615                 strcpy(new_env, ENV_PREFIX);
 616 
 617                 /* strip out 'atk-bridge' and 'gail' */
 618                 size_t PREFIX_LENGTH = strlen(ENV_PREFIX);
 619                 gchar *tmp_ptr = NULL;
 620                 for (s = strtok_r(tmp_env, ":", &tmp_ptr); s;
 621                      s = strtok_r(NULL, ":", &tmp_ptr)) {
 622                     if ((!strstr(s, "atk-bridge")) && (!strstr(s, "gail"))) {
 623                         if (strlen(new_env) > PREFIX_LENGTH) {
 624                             new_env = strcat(new_env, ":");
 625                         }
 626                         new_env = strcat(new_env, s);
 627                     }
 628                 }
 629                 if (putenv(new_env) != 0) {
 630                     /* no free() on success, putenv() doesn't copy string */
 631                     free(new_env);
 632                 }
 633             }
 634             free(tmp_env);
 635         }
 636     }
 637     /*
 638      * GTK should be initialized with gtk_init_check() before use.
 639      *
 640      * gtk_init_check installs its own error handlers. It is critical that
 641      * we preserve error handler set from AWT. Otherwise we'll crash on
 642      * BadMatch errors which we would normally ignore. The IO error handler
 643      * is preserved here, too, just for consistency.
 644     */
 645     AWT_LOCK();
 646     handler = XSetErrorHandler(NULL);
 647     io_handler = XSetIOErrorHandler(NULL);
 648 
 649     //According the GTK documentation, gdk_threads_init() should be
 650     //called before gtk_init() or gtk_init_check()
 651     fp_gdk_threads_init();
 652     result = (*fp_gtk_init_check)(NULL, NULL);
 653 
 654     XSetErrorHandler(handler);
 655     XSetIOErrorHandler(io_handler);
 656     AWT_UNLOCK();
 657 
 658     /* Initialize widget array. */
 659     for (i = 0; i < _GTK_WIDGET_TYPE_SIZE; i++)
 660     {
 661         gtk3_widgets[i] = NULL;
 662     }
 663     if (result) {
 664         GtkApi* gtk = (GtkApi*)malloc(sizeof(GtkApi));
 665         gtk3_init(gtk);
 666         return gtk;
 667     }
 668     return NULL;
 669 }
 670 
 671 static int gtk3_unload()
 672 {
 673     int i;
 674     char *gtk3_error;
 675 
 676     if (!gtk3_libhandle)
 677         return TRUE;
 678 
 679     /* Release painting objects */
 680     if (surface != NULL) {
 681         fp_cairo_destroy(cr);
 682         fp_cairo_surface_destroy(surface);
 683         surface = NULL;
 684     }
 685 
 686     if (gtk3_window != NULL) {
 687         /* Destroying toplevel widget will destroy all contained widgets */
 688         (*fp_gtk_widget_destroy)(gtk3_window);
 689 
 690         /* Unset some static data so they get reinitialized on next load */
 691         gtk3_window = NULL;
 692     }
 693 
 694     dlerror();
 695     dlclose(gtk3_libhandle);
 696     dlclose(gthread_libhandle);
 697     if ((gtk3_error = dlerror()) != NULL)
 698     {
 699         return FALSE;
 700     }
 701     return TRUE;
 702 }
 703 
 704 /* Dispatch all pending events from the GTK event loop.
 705  * This is needed to catch theme change and update widgets' style.
 706  */
 707 static void flush_gtk_event_loop()
 708 {
 709     while((*fp_g_main_context_iteration)(NULL));
 710 }
 711 
 712 /*
 713  * Initialize components of containment hierarchy. This creates a GtkFixed
 714  * inside a GtkWindow. All widgets get realized.
 715  */
 716 static void init_containers()
 717 {
 718     if (gtk3_window == NULL)
 719     {
 720         gtk3_window = (*fp_gtk_window_new)(GTK_WINDOW_TOPLEVEL);
 721         gtk3_fixed = (GtkFixed *)(*fp_gtk_fixed_new)();
 722         (*fp_gtk_container_add)((GtkContainer*)gtk3_window,
 723                                 (GtkWidget *)gtk3_fixed);
 724         (*fp_gtk_widget_realize)(gtk3_window);
 725         (*fp_gtk_widget_realize)((GtkWidget *)gtk3_fixed);
 726 
 727         GtkSettings* settings = fp_gtk_settings_get_for_screen(
 728                                          fp_gtk_widget_get_screen(gtk3_window));
 729         gchar*  strval = NULL;
 730         fp_g_object_get(settings, "gtk-theme-name", &strval, NULL);
 731         gtk3_css = fp_gtk_css_provider_get_named(strval, NULL);
 732     }
 733 }
 734 
 735 /*
 736  * Ensure everything is ready for drawing an element of the specified width
 737  * and height.
 738  *
 739  * We should somehow handle translucent images. GTK can draw to X Drawables
 740  * only, which don't support alpha. When we retrieve the image back from
 741  * the server, translucency information is lost. There're several ways to
 742  * work around this:
 743  * 1) Subclass GdkPixmap and cache translucent objects on client side. This
 744  * requires us to implement parts of X server drawing logic on client side.
 745  * Many X requests can potentially be "translucent"; e.g. XDrawLine with
 746  * fill=tile and a translucent tile is a "translucent" operation, whereas
 747  * XDrawLine with fill=solid is an "opaque" one. Moreover themes can (and some
 748  * do) intermix transparent and opaque operations which makes caching even
 749  * more problematic.
 750  * 2) Use Xorg 32bit ARGB visual when available. GDK has no native support
 751  * for it (as of version 2.6). Also even in JDS 3 Xorg does not support
 752  * these visuals by default, which makes optimizing for them pointless.
 753  * We can consider doing this at a later point when ARGB visuals become more
 754  * popular.
 755  * 3') GTK has plans to use Cairo as its graphical backend (presumably in
 756  * 2.8), and Cairo supports alpha. With it we could also get rid of the
 757  * unnecessary round trip to server and do all the drawing on client side.
 758  * 4) For now we draw to two different pixmaps and restore alpha channel by
 759  * comparing results. This can be optimized by using subclassed pixmap and
 760 */
 761 static void gtk3_init_painting(JNIEnv *env, gint width, gint height)
 762 {
 763     init_containers();
 764 
 765     if (cr) {
 766         fp_cairo_destroy(cr);
 767     }
 768 
 769     if (surface != NULL) {
 770         /* free old stuff */
 771         fp_cairo_surface_destroy(surface);
 772 
 773     }
 774 
 775     if (gtk3_version_3_10) {
 776         surface = fp_gdk_window_create_similar_image_surface(
 777                            fp_gtk_widget_get_window(gtk3_window),
 778                                          CAIRO_FORMAT_ARGB32, width, height, 1);
 779     } else {
 780         surface = fp_cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
 781                                                                  width, height);
 782     }
 783 
 784     cr = fp_cairo_create(surface);
 785 }
 786 
 787 /*
 788  * Restore image from white and black pixmaps and copy it into destination
 789  * buffer. This method compares two pixbufs taken from white and black
 790  * pixmaps and decodes color and alpha components. Pixbufs are RGB without
 791  * alpha, destination buffer is ABGR.
 792  *
 793  * The return value is the transparency type of the resulting image, either
 794  * one of java_awt_Transparency_OPAQUE, java_awt_Transparency_BITMASK, and
 795  * java_awt_Transparency_TRANSLUCENT.
 796  */
 797 static gint gtk3_copy_image(gint *dst, gint width, gint height)
 798 {
 799     gint i, j, r, g, b;
 800     guchar *data;
 801     gint stride, padding;
 802 
 803     fp_cairo_surface_flush(surface);
 804     data = (*fp_cairo_image_surface_get_data)(surface);
 805     stride = (*fp_cairo_image_surface_get_stride)(surface);
 806     padding = stride - width * 4;
 807 
 808     for (i = 0; i < height; i++) {
 809         for (j = 0; j < width; j++) {
 810             int r = *data++;
 811             int g = *data++;
 812             int b = *data++;
 813             int a = *data++;
 814             *dst++ = (a << 24 | b << 16 | g << 8 | r);
 815         }
 816         data += padding;
 817     }
 818     return java_awt_Transparency_TRANSLUCENT;
 819 }
 820 
 821 static void gtk3_set_direction(GtkWidget *widget, GtkTextDirection dir)
 822 {
 823     /*
 824      * Some engines (inexplicably) look at the direction of the widget's
 825      * parent, so we need to set the direction of both the widget and its
 826      * parent.
 827      */
 828     (*fp_gtk_widget_set_direction)(widget, dir);
 829     GtkWidget* parent = fp_gtk_widget_get_parent(widget);
 830     if (parent != NULL) {
 831         fp_gtk_widget_set_direction(parent, dir);
 832     }
 833 }
 834 
 835 /* GTK state_type filter */
 836 static GtkStateType get_gtk_state_type(WidgetType widget_type, gint synth_state)
 837 {
 838     GtkStateType result = GTK_STATE_NORMAL;
 839 
 840     if ((synth_state & DISABLED) != 0) {
 841         result = GTK_STATE_INSENSITIVE;
 842     } else if ((synth_state & PRESSED) != 0) {
 843         result = GTK_STATE_ACTIVE;
 844     } else if ((synth_state & MOUSE_OVER) != 0) {
 845         result = GTK_STATE_PRELIGHT;
 846     }
 847     return result;
 848 }
 849 
 850 static GtkStateFlags get_gtk_state_flags(gint synth_state)
 851 {
 852     GtkStateFlags flags = 0;
 853 
 854     if ((synth_state & DISABLED) != 0) {
 855         flags |= GTK_STATE_FLAG_INSENSITIVE;
 856     }
 857     if (((synth_state & PRESSED) != 0 || (synth_state & SELECTED) != 0)) {
 858         flags |= GTK_STATE_FLAG_ACTIVE;
 859     }
 860     if ((synth_state & MOUSE_OVER) != 0) {
 861         flags |= GTK_STATE_FLAG_PRELIGHT;
 862     }
 863     if ((synth_state & FOCUSED) != 0) {
 864         flags |= GTK_STATE_FLAG_FOCUSED;
 865     }
 866     return flags;
 867 }
 868 
 869 static GtkStateFlags get_gtk_flags(GtkStateType state_type) {
 870     GtkStateFlags flags = 0;
 871     switch (state_type)
 872     {
 873         case GTK_STATE_PRELIGHT:
 874           flags |= GTK_STATE_FLAG_PRELIGHT;
 875           break;
 876         case GTK_STATE_SELECTED:
 877           flags |= GTK_STATE_FLAG_SELECTED;
 878           break;
 879         case GTK_STATE_INSENSITIVE:
 880           flags |= GTK_STATE_FLAG_INSENSITIVE;
 881           break;
 882         case GTK_STATE_ACTIVE:
 883           flags |= GTK_STATE_FLAG_ACTIVE;
 884           break;
 885         case GTK_STATE_FOCUSED:
 886           flags |= GTK_STATE_FLAG_FOCUSED;
 887           break;
 888         default:
 889           break;
 890     }
 891     return flags;
 892 }
 893 
 894 /* GTK shadow_type filter */
 895 static GtkShadowType get_gtk_shadow_type(WidgetType widget_type,
 896                                                                gint synth_state)
 897 {
 898     GtkShadowType result = GTK_SHADOW_OUT;
 899 
 900     if ((synth_state & SELECTED) != 0) {
 901         result = GTK_SHADOW_IN;
 902     }
 903     return result;
 904 }
 905 
 906 
 907 static GtkWidget* gtk3_get_arrow(GtkArrowType arrow_type,
 908                                                       GtkShadowType shadow_type)
 909 {
 910     GtkWidget *arrow = NULL;
 911     if (NULL == gtk3_widgets[_GTK_ARROW_TYPE])
 912     {
 913         gtk3_widgets[_GTK_ARROW_TYPE] = (*fp_gtk_arrow_new)(arrow_type,
 914                                                                    shadow_type);
 915         (*fp_gtk_container_add)((GtkContainer *)gtk3_fixed,
 916                                                  gtk3_widgets[_GTK_ARROW_TYPE]);
 917         (*fp_gtk_widget_realize)(gtk3_widgets[_GTK_ARROW_TYPE]);
 918     }
 919     arrow = gtk3_widgets[_GTK_ARROW_TYPE];
 920 
 921     (*fp_gtk_arrow_set)(arrow, arrow_type, shadow_type);
 922     return arrow;
 923 }
 924 
 925 static GtkAdjustment* create_adjustment()
 926 {
 927     return (GtkAdjustment *)
 928             (*fp_gtk_adjustment_new)(50.0, 0.0, 100.0, 10.0, 20.0, 20.0);
 929 }
 930 
 931 /**
 932  * Returns a pointer to the cached native widget for the specified widget
 933  * type.
 934  */
 935 static GtkWidget *gtk3_get_widget(WidgetType widget_type)
 936 {
 937     gboolean init_result = FALSE;
 938     GtkWidget *result = NULL;
 939     switch (widget_type)
 940     {
 941         case BUTTON:
 942         case TABLE_HEADER:
 943             if (init_result = (NULL == gtk3_widgets[_GTK_BUTTON_TYPE]))
 944             {
 945                 gtk3_widgets[_GTK_BUTTON_TYPE] = (*fp_gtk_button_new)();
 946             }
 947             result = gtk3_widgets[_GTK_BUTTON_TYPE];
 948             break;
 949         case CHECK_BOX:
 950             if (init_result = (NULL == gtk3_widgets[_GTK_CHECK_BUTTON_TYPE]))
 951             {
 952                 gtk3_widgets[_GTK_CHECK_BUTTON_TYPE] =
 953                     (*fp_gtk_check_button_new)();
 954             }
 955             result = gtk3_widgets[_GTK_CHECK_BUTTON_TYPE];
 956             break;
 957         case CHECK_BOX_MENU_ITEM:
 958             if (init_result = (NULL == gtk3_widgets[_GTK_CHECK_MENU_ITEM_TYPE]))
 959             {
 960                 gtk3_widgets[_GTK_CHECK_MENU_ITEM_TYPE] =
 961                     (*fp_gtk_check_menu_item_new)();
 962             }
 963             result = gtk3_widgets[_GTK_CHECK_MENU_ITEM_TYPE];
 964             break;
 965         /************************************************************
 966          *    Creation a dedicated color chooser is dangerous because
 967          * it deadlocks the EDT
 968          ************************************************************/
 969 /*        case COLOR_CHOOSER:
 970             if (init_result =
 971                     (NULL == gtk3_widgets[_GTK_COLOR_SELECTION_DIALOG_TYPE]))
 972             {
 973                 gtk3_widgets[_GTK_COLOR_SELECTION_DIALOG_TYPE] =
 974                     (*fp_gtk_color_selection_dialog_new)(NULL);
 975             }
 976             result = gtk3_widgets[_GTK_COLOR_SELECTION_DIALOG_TYPE];
 977             break;*/
 978         case COMBO_BOX:
 979             if (init_result = (NULL == gtk3_widgets[_GTK_COMBO_BOX_TYPE]))
 980             {
 981                 gtk3_widgets[_GTK_COMBO_BOX_TYPE] =
 982                     (*fp_gtk_combo_box_new)();
 983             }
 984             result = gtk3_widgets[_GTK_COMBO_BOX_TYPE];
 985             break;
 986         case COMBO_BOX_ARROW_BUTTON:
 987             if (init_result =
 988                     (NULL == gtk3_widgets[_GTK_COMBO_BOX_ARROW_BUTTON_TYPE]))
 989             {
 990                 gtk3_widgets[_GTK_COMBO_BOX_ARROW_BUTTON_TYPE] =
 991                      (*fp_gtk_toggle_button_new)();
 992             }
 993             result = gtk3_widgets[_GTK_COMBO_BOX_ARROW_BUTTON_TYPE];
 994             break;
 995         case COMBO_BOX_TEXT_FIELD:
 996             if (init_result =
 997                     (NULL == gtk3_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE]))
 998             {
 999                 result = gtk3_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE] =
1000                      (*fp_gtk_entry_new)();
1001             }
1002             result = gtk3_widgets[_GTK_COMBO_BOX_TEXT_FIELD_TYPE];
1003             break;
1004         case DESKTOP_ICON:
1005         case INTERNAL_FRAME_TITLE_PANE:
1006         case LABEL:
1007             if (init_result = (NULL == gtk3_widgets[_GTK_LABEL_TYPE]))
1008             {
1009                 gtk3_widgets[_GTK_LABEL_TYPE] =
1010                     (*fp_gtk_label_new)(NULL);
1011             }
1012             result = gtk3_widgets[_GTK_LABEL_TYPE];
1013             break;
1014         case DESKTOP_PANE:
1015         case PANEL:
1016         case ROOT_PANE:
1017             if (init_result = (NULL == gtk3_widgets[_GTK_CONTAINER_TYPE]))
1018             {
1019                 /* There is no constructor for a container type.  I've
1020                  * chosen GtkFixed container since it has a default
1021                  * constructor.
1022                  */
1023                 gtk3_widgets[_GTK_CONTAINER_TYPE] =
1024                     (*fp_gtk_fixed_new)();
1025             }
1026             result = gtk3_widgets[_GTK_CONTAINER_TYPE];
1027             break;
1028         case EDITOR_PANE:
1029         case TEXT_AREA:
1030         case TEXT_PANE:
1031             if (init_result = (NULL == gtk3_widgets[_GTK_TEXT_VIEW_TYPE]))
1032             {
1033                 gtk3_widgets[_GTK_TEXT_VIEW_TYPE] =
1034                     (*fp_gtk_text_view_new)();
1035             }
1036             result = gtk3_widgets[_GTK_TEXT_VIEW_TYPE];
1037             break;
1038         case FORMATTED_TEXT_FIELD:
1039         case PASSWORD_FIELD:
1040         case TEXT_FIELD:
1041             if (init_result = (NULL == gtk3_widgets[_GTK_ENTRY_TYPE]))
1042             {
1043                 gtk3_widgets[_GTK_ENTRY_TYPE] =
1044                     (*fp_gtk_entry_new)();
1045             }
1046             result = gtk3_widgets[_GTK_ENTRY_TYPE];
1047             break;
1048         case HANDLE_BOX:
1049             if (init_result = (NULL == gtk3_widgets[_GTK_HANDLE_BOX_TYPE]))
1050             {
1051                 gtk3_widgets[_GTK_HANDLE_BOX_TYPE] =
1052                     (*fp_gtk_handle_box_new)();
1053             }
1054             result = gtk3_widgets[_GTK_HANDLE_BOX_TYPE];
1055             break;
1056         case HSCROLL_BAR:
1057         case HSCROLL_BAR_BUTTON_LEFT:
1058         case HSCROLL_BAR_BUTTON_RIGHT:
1059         case HSCROLL_BAR_TRACK:
1060         case HSCROLL_BAR_THUMB:
1061             if (init_result = (NULL == gtk3_widgets[_GTK_HSCROLLBAR_TYPE]))
1062             {
1063                 gtk3_widgets[_GTK_HSCROLLBAR_TYPE] =
1064                     (*fp_gtk_hscrollbar_new)(create_adjustment());
1065             }
1066             result = gtk3_widgets[_GTK_HSCROLLBAR_TYPE];
1067             break;
1068         case HSEPARATOR:
1069             if (init_result = (NULL == gtk3_widgets[_GTK_HSEPARATOR_TYPE]))
1070             {
1071                 gtk3_widgets[_GTK_HSEPARATOR_TYPE] =
1072                     (*fp_gtk_hseparator_new)();
1073             }
1074             result = gtk3_widgets[_GTK_HSEPARATOR_TYPE];
1075             break;
1076         case HSLIDER:
1077         case HSLIDER_THUMB:
1078         case HSLIDER_TRACK:
1079             if (init_result = (NULL == gtk3_widgets[_GTK_HSCALE_TYPE]))
1080             {
1081                 gtk3_widgets[_GTK_HSCALE_TYPE] =
1082                     (*fp_gtk_scale_new)(GTK_ORIENTATION_HORIZONTAL, NULL);
1083             }
1084             result = gtk3_widgets[_GTK_HSCALE_TYPE];
1085             break;
1086         case HSPLIT_PANE_DIVIDER:
1087         case SPLIT_PANE:
1088             if (init_result = (NULL == gtk3_widgets[_GTK_HPANED_TYPE]))
1089             {
1090                 gtk3_widgets[_GTK_HPANED_TYPE] = (*fp_gtk_paned_new)(GTK_ORIENTATION_HORIZONTAL);
1091             }
1092             result = gtk3_widgets[_GTK_HPANED_TYPE];
1093             break;
1094         case IMAGE:
1095             if (init_result = (NULL == gtk3_widgets[_GTK_IMAGE_TYPE]))
1096             {
1097                 gtk3_widgets[_GTK_IMAGE_TYPE] = (*fp_gtk_image_new)();
1098             }
1099             result = gtk3_widgets[_GTK_IMAGE_TYPE];
1100             break;
1101         case INTERNAL_FRAME:
1102             if (init_result = (NULL == gtk3_widgets[_GTK_WINDOW_TYPE]))
1103             {
1104                 gtk3_widgets[_GTK_WINDOW_TYPE] =
1105                     (*fp_gtk_window_new)(GTK_WINDOW_TOPLEVEL);
1106             }
1107             result = gtk3_widgets[_GTK_WINDOW_TYPE];
1108             break;
1109         case TOOL_TIP:
1110             if (init_result = (NULL == gtk3_widgets[_GTK_TOOLTIP_TYPE]))
1111             {
1112                 result = (*fp_gtk_window_new)(GTK_WINDOW_TOPLEVEL);
1113                 gtk3_widgets[_GTK_TOOLTIP_TYPE] = result;
1114             }
1115             result = gtk3_widgets[_GTK_TOOLTIP_TYPE];
1116             break;
1117         case LIST:
1118         case TABLE:
1119         case TREE:
1120         case TREE_CELL:
1121             if (init_result = (NULL == gtk3_widgets[_GTK_TREE_VIEW_TYPE]))
1122             {
1123                 gtk3_widgets[_GTK_TREE_VIEW_TYPE] =
1124                     (*fp_gtk_tree_view_new)();
1125             }
1126             result = gtk3_widgets[_GTK_TREE_VIEW_TYPE];
1127             break;
1128         case TITLED_BORDER:
1129             if (init_result = (NULL == gtk3_widgets[_GTK_FRAME_TYPE]))
1130             {
1131                 gtk3_widgets[_GTK_FRAME_TYPE] = fp_gtk_frame_new(NULL);
1132             }
1133             result = gtk3_widgets[_GTK_FRAME_TYPE];
1134             break;
1135         case POPUP_MENU:
1136             if (init_result = (NULL == gtk3_widgets[_GTK_MENU_TYPE]))
1137             {
1138                 gtk3_widgets[_GTK_MENU_TYPE] =
1139                     (*fp_gtk_menu_new)();
1140             }
1141             result = gtk3_widgets[_GTK_MENU_TYPE];
1142             break;
1143         case MENU:
1144         case MENU_ITEM:
1145         case MENU_ITEM_ACCELERATOR:
1146             if (init_result = (NULL == gtk3_widgets[_GTK_MENU_ITEM_TYPE]))
1147             {
1148                 gtk3_widgets[_GTK_MENU_ITEM_TYPE] =
1149                     (*fp_gtk_menu_item_new)();
1150             }
1151             result = gtk3_widgets[_GTK_MENU_ITEM_TYPE];
1152             break;
1153         case MENU_BAR:
1154             if (init_result = (NULL == gtk3_widgets[_GTK_MENU_BAR_TYPE]))
1155             {
1156                 gtk3_widgets[_GTK_MENU_BAR_TYPE] =
1157                     (*fp_gtk_menu_bar_new)();
1158             }
1159             result = gtk3_widgets[_GTK_MENU_BAR_TYPE];
1160             break;
1161         case COLOR_CHOOSER:
1162         case OPTION_PANE:
1163             if (init_result = (NULL == gtk3_widgets[_GTK_DIALOG_TYPE]))
1164             {
1165                 gtk3_widgets[_GTK_DIALOG_TYPE] =
1166                     (*fp_gtk_dialog_new)();
1167             }
1168             result = gtk3_widgets[_GTK_DIALOG_TYPE];
1169             break;
1170         case POPUP_MENU_SEPARATOR:
1171             if (init_result =
1172                     (NULL == gtk3_widgets[_GTK_SEPARATOR_MENU_ITEM_TYPE]))
1173             {
1174                 gtk3_widgets[_GTK_SEPARATOR_MENU_ITEM_TYPE] =
1175                     (*fp_gtk_separator_menu_item_new)();
1176             }
1177             result = gtk3_widgets[_GTK_SEPARATOR_MENU_ITEM_TYPE];
1178             break;
1179         case HPROGRESS_BAR:
1180             if (init_result = (NULL == gtk3_widgets[_GTK_HPROGRESS_BAR_TYPE]))
1181             {
1182                 gtk3_widgets[_GTK_HPROGRESS_BAR_TYPE] =
1183                     (*fp_gtk_progress_bar_new)();
1184             }
1185             result = gtk3_widgets[_GTK_HPROGRESS_BAR_TYPE];
1186             break;
1187         case VPROGRESS_BAR:
1188             if (init_result = (NULL == gtk3_widgets[_GTK_VPROGRESS_BAR_TYPE]))
1189             {
1190                 gtk3_widgets[_GTK_VPROGRESS_BAR_TYPE] =
1191                     (*fp_gtk_progress_bar_new)();
1192                 /*
1193                  * Vertical JProgressBars always go bottom-to-top,
1194                  * regardless of the ComponentOrientation.
1195                  */
1196                 (*fp_gtk_progress_bar_set_orientation)(
1197                     (GtkProgressBar *)gtk3_widgets[_GTK_VPROGRESS_BAR_TYPE],
1198                     GTK_PROGRESS_BOTTOM_TO_TOP);
1199             }
1200             result = gtk3_widgets[_GTK_VPROGRESS_BAR_TYPE];
1201             break;
1202         case RADIO_BUTTON:
1203             if (init_result = (NULL == gtk3_widgets[_GTK_RADIO_BUTTON_TYPE]))
1204             {
1205                 gtk3_widgets[_GTK_RADIO_BUTTON_TYPE] =
1206                     (*fp_gtk_radio_button_new)(NULL);
1207             }
1208             result = gtk3_widgets[_GTK_RADIO_BUTTON_TYPE];
1209             break;
1210         case RADIO_BUTTON_MENU_ITEM:
1211             if (init_result =
1212                     (NULL == gtk3_widgets[_GTK_RADIO_MENU_ITEM_TYPE]))
1213             {
1214                 gtk3_widgets[_GTK_RADIO_MENU_ITEM_TYPE] =
1215                     (*fp_gtk_radio_menu_item_new)(NULL);
1216             }
1217             result = gtk3_widgets[_GTK_RADIO_MENU_ITEM_TYPE];
1218             break;
1219         case SCROLL_PANE:
1220             if (init_result =
1221                     (NULL == gtk3_widgets[_GTK_SCROLLED_WINDOW_TYPE]))
1222             {
1223                 gtk3_widgets[_GTK_SCROLLED_WINDOW_TYPE] =
1224                     (*fp_gtk_scrolled_window_new)(NULL, NULL);
1225             }
1226             result = gtk3_widgets[_GTK_SCROLLED_WINDOW_TYPE];
1227             break;
1228         case SPINNER:
1229         case SPINNER_ARROW_BUTTON:
1230         case SPINNER_TEXT_FIELD:
1231             if (init_result = (NULL == gtk3_widgets[_GTK_SPIN_BUTTON_TYPE]))
1232             {
1233                 result = gtk3_widgets[_GTK_SPIN_BUTTON_TYPE] =
1234                     (*fp_gtk_spin_button_new)(NULL, 0, 0);
1235             }
1236             result = gtk3_widgets[_GTK_SPIN_BUTTON_TYPE];
1237             break;
1238         case TABBED_PANE:
1239         case TABBED_PANE_TAB_AREA:
1240         case TABBED_PANE_CONTENT:
1241         case TABBED_PANE_TAB:
1242             if (init_result = (NULL == gtk3_widgets[_GTK_NOTEBOOK_TYPE]))
1243             {
1244                 gtk3_widgets[_GTK_NOTEBOOK_TYPE] =
1245                     (*fp_gtk_notebook_new)(NULL);
1246             }
1247             result = gtk3_widgets[_GTK_NOTEBOOK_TYPE];
1248             break;
1249         case TOGGLE_BUTTON:
1250             if (init_result = (NULL == gtk3_widgets[_GTK_TOGGLE_BUTTON_TYPE]))
1251             {
1252                 gtk3_widgets[_GTK_TOGGLE_BUTTON_TYPE] =
1253                     (*fp_gtk_toggle_button_new)(NULL);
1254             }
1255             result = gtk3_widgets[_GTK_TOGGLE_BUTTON_TYPE];
1256             break;
1257         case TOOL_BAR:
1258         case TOOL_BAR_DRAG_WINDOW:
1259             if (init_result = (NULL == gtk3_widgets[_GTK_TOOLBAR_TYPE]))
1260             {
1261                 gtk3_widgets[_GTK_TOOLBAR_TYPE] =
1262                     (*fp_gtk_toolbar_new)(NULL);
1263             }
1264             result = gtk3_widgets[_GTK_TOOLBAR_TYPE];
1265             break;
1266         case TOOL_BAR_SEPARATOR:
1267             if (init_result =
1268                     (NULL == gtk3_widgets[_GTK_SEPARATOR_TOOL_ITEM_TYPE]))
1269             {
1270                 gtk3_widgets[_GTK_SEPARATOR_TOOL_ITEM_TYPE] =
1271                     (*fp_gtk_separator_tool_item_new)();
1272             }
1273             result = gtk3_widgets[_GTK_SEPARATOR_TOOL_ITEM_TYPE];
1274             break;
1275         case VIEWPORT:
1276             if (init_result = (NULL == gtk3_widgets[_GTK_VIEWPORT_TYPE]))
1277             {
1278                 GtkAdjustment *adjustment = create_adjustment();
1279                 gtk3_widgets[_GTK_VIEWPORT_TYPE] =
1280                     (*fp_gtk_viewport_new)(adjustment, adjustment);
1281             }
1282             result = gtk3_widgets[_GTK_VIEWPORT_TYPE];
1283             break;
1284         case VSCROLL_BAR:
1285         case VSCROLL_BAR_BUTTON_UP:
1286         case VSCROLL_BAR_BUTTON_DOWN:
1287         case VSCROLL_BAR_TRACK:
1288         case VSCROLL_BAR_THUMB:
1289             if (init_result = (NULL == gtk3_widgets[_GTK_VSCROLLBAR_TYPE]))
1290             {
1291                 gtk3_widgets[_GTK_VSCROLLBAR_TYPE] =
1292                     (*fp_gtk_vscrollbar_new)(create_adjustment());
1293             }
1294             result = gtk3_widgets[_GTK_VSCROLLBAR_TYPE];
1295             break;
1296         case VSEPARATOR:
1297             if (init_result = (NULL == gtk3_widgets[_GTK_VSEPARATOR_TYPE]))
1298             {
1299                 gtk3_widgets[_GTK_VSEPARATOR_TYPE] =
1300                     (*fp_gtk_vseparator_new)();
1301             }
1302             result = gtk3_widgets[_GTK_VSEPARATOR_TYPE];
1303             break;
1304         case VSLIDER:
1305         case VSLIDER_THUMB:
1306         case VSLIDER_TRACK:
1307             if (init_result = (NULL == gtk3_widgets[_GTK_VSCALE_TYPE]))
1308             {
1309                 gtk3_widgets[_GTK_VSCALE_TYPE] =
1310                     (*fp_gtk_scale_new)(GTK_ORIENTATION_VERTICAL, NULL);
1311             }
1312             result = gtk3_widgets[_GTK_VSCALE_TYPE];
1313             /*
1314              * Vertical JSliders start at the bottom, while vertical
1315              * GtkVScale widgets start at the top (by default), so to fix
1316              * this we set the "inverted" flag to get the Swing behavior.
1317              */
1318              fp_gtk_range_set_inverted((GtkRange*)result, TRUE);
1319             break;
1320         case VSPLIT_PANE_DIVIDER:
1321             if (init_result = (NULL == gtk3_widgets[_GTK_VPANED_TYPE]))
1322             {
1323                 gtk3_widgets[_GTK_VPANED_TYPE] = (*fp_gtk_paned_new)(GTK_ORIENTATION_VERTICAL);
1324             }
1325             result = gtk3_widgets[_GTK_VPANED_TYPE];
1326             break;
1327         default:
1328             result = NULL;
1329             break;
1330     }
1331 
1332     if (result != NULL && init_result)
1333     {
1334         if (widget_type == RADIO_BUTTON_MENU_ITEM ||
1335                 widget_type == CHECK_BOX_MENU_ITEM ||
1336                 widget_type == MENU_ITEM ||
1337                 widget_type == MENU ||
1338                 widget_type == POPUP_MENU_SEPARATOR)
1339         {
1340             GtkWidget *menu = gtk3_get_widget(POPUP_MENU);
1341             (*fp_gtk_menu_shell_append)((GtkMenuShell *)menu, result);
1342         }
1343         else if (widget_type == POPUP_MENU)
1344         {
1345             GtkWidget *menu_bar = gtk3_get_widget(MENU_BAR);
1346             GtkWidget *root_menu = (*fp_gtk_menu_item_new)();
1347             (*fp_gtk_menu_item_set_submenu)((GtkMenuItem*)root_menu, result);
1348             (*fp_gtk_menu_shell_append)((GtkMenuShell *)menu_bar, root_menu);
1349         }
1350         else if (widget_type == COMBO_BOX_TEXT_FIELD )
1351         {
1352             GtkWidget* combo = gtk3_get_widget(COMBO_BOX);
1353 
1354             /*
1355             * We add a regular GtkButton/GtkEntry to a GtkComboBoxEntry
1356             * in order to trick engines into thinking it's a real combobox
1357             * arrow button/text field.
1358             */
1359 
1360             fp_gtk_container_add ((GtkContainer*)(combo), result);
1361             GtkStyleContext* context = fp_gtk_widget_get_style_context (combo);
1362             fp_gtk_style_context_add_class (context, "combobox-entry");
1363             context = fp_gtk_widget_get_style_context (result);
1364             fp_gtk_style_context_add_class (context, "combobox");
1365             fp_gtk_style_context_add_class (context, "entry");
1366         }
1367         else if (widget_type == COMBO_BOX_ARROW_BUTTON )
1368         {
1369             GtkWidget* combo = gtk3_get_widget(COMBO_BOX);
1370             fp_gtk_widget_set_parent(result, combo);
1371         }
1372         else if (widget_type != TOOL_TIP &&
1373                  widget_type != INTERNAL_FRAME &&
1374                  widget_type != OPTION_PANE)
1375         {
1376             (*fp_gtk_container_add)((GtkContainer *)gtk3_fixed, result);
1377         }
1378         (*fp_gtk_widget_realize)(result);
1379     }
1380     return result;
1381 }
1382 
1383 static void append_element (GtkWidgetPath *path, const gchar *selector)
1384 {
1385     fp_gtk_widget_path_append_type (path, G_TYPE_NONE);
1386     fp_gtk_widget_path_iter_set_object_name (path, -1, selector);
1387 }
1388 
1389 static GtkWidgetPath* createWidgetPath(const GtkWidgetPath* path) {
1390     if (path == NULL) {
1391         return fp_gtk_widget_path_new();
1392     } else {
1393         return fp_gtk_widget_path_copy(path);
1394     }
1395 }
1396 
1397 static GtkStyleContext* get_style(WidgetType widget_type, const gchar *detail)
1398 {
1399     if (!gtk3_version_3_20) {
1400         gtk3_widget = gtk3_get_widget(widget_type);
1401         GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
1402         fp_gtk_style_context_save (context);
1403         if (detail != 0) {
1404              transform_detail_string(detail, context);
1405         }
1406         return context;
1407     } else {
1408         gtk3_widget = gtk3_get_widget(widget_type);
1409         GtkStyleContext* widget_context = fp_gtk_widget_get_style_context (gtk3_widget);
1410         GtkWidgetPath *path = NULL;
1411         if (detail != 0) {
1412             if (strcmp(detail, "checkbutton") == 0) {
1413                 path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1414                 append_element(path, "check");
1415             } else if (strcmp(detail, "radiobutton") == 0) {
1416                 path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1417                 append_element(path, "radio");
1418             } else if (strcmp(detail, "vscale") == 0 || strcmp(detail, "hscale") == 0) {
1419                 path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1420                 append_element(path, "slider");
1421             } else if (strcmp(detail, "trough") == 0) {
1422                 //This is a fast solution to the scrollbar trough not being rendered properly
1423                 if (widget_type == HSCROLL_BAR || widget_type == HSCROLL_BAR_TRACK ||
1424                     widget_type == VSCROLL_BAR || widget_type == VSCROLL_BAR_TRACK) {
1425                     path = createWidgetPath (NULL);
1426                 } else {
1427                     path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1428                 }
1429                 append_element(path, detail);
1430             } else if (strcmp(detail, "bar") == 0) {
1431                 path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1432                 append_element(path, "trough");
1433                 append_element(path, "progress");
1434             } else if (strcmp(detail, "vscrollbar") == 0 || strcmp(detail, "hscrollbar") == 0) {
1435                 path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1436                 append_element(path, "button");
1437             } else if (strcmp(detail, "check") == 0) {
1438                 path = createWidgetPath (NULL);
1439                 append_element(path, detail);
1440             } else if (strcmp(detail, "option") == 0) {
1441                 path = createWidgetPath (NULL);
1442                 append_element(path, "radio");
1443             } else if (strcmp(detail, "paned") == 0) {
1444                 path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1445                 append_element(path, "paned");
1446                 append_element(path, "separator");
1447             } else if (strcmp(detail, "spinbutton_down") == 0 || strcmp(detail, "spinbutton_up") == 0) {
1448                 path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1449                 append_element(path, "spinbutton");
1450                 append_element(path, "button");
1451             } else {
1452                 path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1453                 append_element(path, detail);
1454             }
1455         } else {
1456             path = createWidgetPath (fp_gtk_style_context_get_path (widget_context));
1457         }
1458 
1459         GtkStyleContext *context = fp_gtk_style_context_new ();
1460         fp_gtk_style_context_set_path (context, path);
1461         fp_gtk_widget_path_unref (path);
1462         return context;
1463     }
1464 }
1465 
1466 static void disposeOrRestoreContext(GtkStyleContext *context)
1467 {
1468     if (!gtk3_version_3_20) {
1469         fp_gtk_style_context_restore (context);
1470     } else {
1471         fp_g_object_unref (context);
1472     }
1473 }
1474 
1475 static void gtk3_paint_arrow(WidgetType widget_type, GtkStateType state_type,
1476         GtkShadowType shadow_type, const gchar *detail,
1477         gint x, gint y, gint width, gint height,
1478         GtkArrowType arrow_type, gboolean fill)
1479 {
1480     gdouble xx, yy, a = G_PI;
1481     int s = width;
1482     gtk3_widget = gtk3_get_arrow(arrow_type, shadow_type);
1483 
1484     switch (widget_type)
1485     {
1486         case SPINNER_ARROW_BUTTON:
1487             s = (int)(0.4 * width + 0.5) + 1;
1488             if (arrow_type == GTK_ARROW_UP) {
1489                 a = 0;
1490             } else if (arrow_type == GTK_ARROW_DOWN) {
1491                 a = G_PI;
1492             }
1493             break;
1494 
1495         case HSCROLL_BAR_BUTTON_LEFT:
1496             s = (int)(0.5 * MIN(height, width * 2) + 0.5) + 1;
1497             a = 3 * G_PI / 2;
1498             break;
1499 
1500         case HSCROLL_BAR_BUTTON_RIGHT:
1501             s = (int)(0.5 * MIN(height, width * 2) + 0.5) + 1;
1502             a = G_PI / 2;
1503             break;
1504 
1505         case VSCROLL_BAR_BUTTON_UP:
1506             s = (int)(0.5 * MIN(height * 2, width) + 0.5) + 1;
1507             a = 0;
1508             break;
1509 
1510         case VSCROLL_BAR_BUTTON_DOWN:
1511             s = (int)(0.5 * MIN(height * 2, width) + 0.5) + 1;
1512             a = G_PI;
1513             break;
1514 
1515         case COMBO_BOX_ARROW_BUTTON:
1516             s = (int)(0.3 * height + 0.5) + 1;
1517             a = G_PI;
1518             break;
1519 
1520         case TABLE:
1521             s = (int)(0.8 * height + 0.5) + 1;
1522             if (arrow_type == GTK_ARROW_UP) {
1523                 a = G_PI;
1524             } else if (arrow_type == GTK_ARROW_DOWN) {
1525                 a = 0;
1526             }
1527             break;
1528 
1529         case MENU_ITEM:
1530             if (arrow_type == GTK_ARROW_UP) {
1531                 a = G_PI;
1532             } else if (arrow_type == GTK_ARROW_DOWN) {
1533                 a = 0;
1534             } else if (arrow_type == GTK_ARROW_RIGHT) {
1535                 a = G_PI / 2;
1536             } else if (arrow_type == GTK_ARROW_LEFT) {
1537                 a = 3 * G_PI / 2;
1538             }
1539             break;
1540 
1541         default:
1542             if (arrow_type == GTK_ARROW_UP) {
1543                 a = G_PI;
1544             } else if (arrow_type == GTK_ARROW_DOWN) {
1545                 a = 0;
1546             } else if (arrow_type == GTK_ARROW_RIGHT) {
1547                 a = G_PI / 2;
1548             } else if (arrow_type == GTK_ARROW_LEFT) {
1549                 a = 3 * G_PI / 2;
1550             }
1551             break;
1552     }
1553 
1554     if (s < width && s < height) {
1555         xx = x + (0.5 * (width - s) + 0.5);
1556         yy = y + (0.5 * (height - s) + 0.5);
1557     } else {
1558         xx = x;
1559         yy = y;
1560     }
1561 
1562     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
1563     fp_gtk_style_context_save (context);
1564 
1565 
1566     if (detail != NULL) {
1567         transform_detail_string(detail, context);
1568     }
1569 
1570     GtkStateFlags flags = get_gtk_flags(state_type);
1571 
1572     fp_gtk_style_context_set_state (context, flags);
1573 
1574     (*fp_gtk_render_arrow)(context, cr, a, xx, yy, s);
1575 
1576     fp_gtk_style_context_restore (context);
1577 }
1578 
1579 static void gtk3_paint_box(WidgetType widget_type, GtkStateType state_type,
1580                     GtkShadowType shadow_type, const gchar *detail,
1581                     gint x, gint y, gint width, gint height,
1582                     gint synth_state, GtkTextDirection dir)
1583 {
1584     gtk3_widget = gtk3_get_widget(widget_type);
1585 
1586     if (widget_type == HSLIDER_TRACK) {
1587         /*
1588          * For horizontal JSliders with right-to-left orientation, we need
1589          * to set the "inverted" flag to match the native GTK behavior where
1590          * the foreground highlight is on the right side of the slider thumb.
1591          * This is needed especially for the ubuntulooks engine, which looks
1592          * exclusively at the "inverted" flag to determine on which side of
1593          * the thumb to paint the highlight...
1594          */
1595         fp_gtk_range_set_inverted((GtkRange*)gtk3_widget, dir ==
1596                                                               GTK_TEXT_DIR_RTL);
1597 
1598         /*
1599          * Note however that other engines like clearlooks will look at both
1600          * the "inverted" field and the text direction to determine how
1601          * the foreground highlight is painted:
1602          *     !inverted && ltr --> paint highlight on left side
1603          *     !inverted && rtl --> paint highlight on right side
1604          *      inverted && ltr --> paint highlight on right side
1605          *      inverted && rtl --> paint highlight on left side
1606          * So the only way to reliably get the desired results for horizontal
1607          * JSlider (i.e., highlight on left side for LTR ComponentOrientation
1608          * and highlight on right side for RTL ComponentOrientation) is to
1609          * always override text direction as LTR, and then set the "inverted"
1610          * flag accordingly (as we have done above).
1611          */
1612         dir = GTK_TEXT_DIR_LTR;
1613     }
1614 
1615     /*
1616      * Some engines (e.g. clearlooks) will paint the shadow of certain
1617      * widgets (e.g. COMBO_BOX_ARROW_BUTTON) differently depending on the
1618      * the text direction.
1619      */
1620     gtk3_set_direction(gtk3_widget, dir);
1621 
1622     GtkStyleContext* context = get_style(widget_type, detail);
1623 
1624     GtkStateFlags flags = get_gtk_flags(state_type);
1625     if (shadow_type == GTK_SHADOW_IN && widget_type != COMBO_BOX_ARROW_BUTTON) {
1626         flags |= GTK_STATE_FLAG_ACTIVE;
1627     }
1628 
1629     if (synth_state & MOUSE_OVER) {
1630         flags |= GTK_STATE_FLAG_PRELIGHT;
1631     }
1632 
1633     if (synth_state & FOCUSED) {
1634         flags |= GTK_STATE_FLAG_FOCUSED;
1635     }
1636 
1637     if (synth_state & DEFAULT) {
1638         fp_gtk_style_context_add_class (context, "default");
1639     }
1640 
1641     if (fp_gtk_style_context_has_class(context, "trough")) {
1642         flags |= GTK_STATE_FLAG_BACKDROP;
1643     }
1644 
1645     fp_gtk_style_context_set_state (context, flags);
1646 
1647     fp_gtk_render_background (context, cr, x, y, width, height);
1648     if (shadow_type != GTK_SHADOW_NONE) {
1649         fp_gtk_render_frame(context, cr, x, y, width, height);
1650     }
1651 
1652     disposeOrRestoreContext(context);
1653 
1654     /*
1655      * Reset the text direction to the default value so that we don't
1656      * accidentally affect other operations and widgets.
1657      */
1658     gtk3_set_direction(gtk3_widget, GTK_TEXT_DIR_LTR);
1659 
1660     //This is a fast solution to the scrollbar trough not being rendered properly
1661     if ((widget_type == HSCROLL_BAR || widget_type == HSCROLL_BAR_TRACK ||
1662         widget_type == VSCROLL_BAR || widget_type == VSCROLL_BAR_TRACK) && detail != 0) {
1663         gtk3_paint_box(widget_type, state_type, shadow_type, NULL,
1664                     x, y, width, height, synth_state, dir);
1665     }
1666 }
1667 
1668 static void gtk3_paint_box_gap(WidgetType widget_type, GtkStateType state_type,
1669         GtkShadowType shadow_type, const gchar *detail,
1670         gint x, gint y, gint width, gint height,
1671         GtkPositionType gap_side, gint gap_x, gint gap_width)
1672 {
1673     gtk3_widget = gtk3_get_widget(widget_type);
1674 
1675     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
1676 
1677     fp_gtk_style_context_save (context);
1678 
1679     GtkStateFlags flags = get_gtk_flags(state_type);
1680     fp_gtk_style_context_set_state(context, flags);
1681 
1682     if (detail != 0) {
1683         transform_detail_string(detail, context);
1684     }
1685     fp_gtk_render_background(context, cr, x, y, width, height);
1686 
1687     if (shadow_type != GTK_SHADOW_NONE) {
1688         fp_gtk_render_frame_gap(context, cr, x, y, width, height, gap_side,
1689                                     (gdouble)gap_x, (gdouble)gap_x + gap_width);
1690     }
1691     fp_gtk_style_context_restore (context);
1692 }
1693 
1694 static void gtk3_paint_check(WidgetType widget_type, gint synth_state,
1695         const gchar *detail, gint x, gint y, gint width, gint height)
1696 {
1697     GtkStyleContext* context = get_style(widget_type, detail);
1698 
1699     GtkStateFlags flags = get_gtk_state_flags(synth_state);
1700     if (gtk3_version_3_14 && (synth_state & SELECTED)) {
1701         flags &= ~GTK_STATE_FLAG_SELECTED;
1702         flags |= GTK_STATE_FLAG_CHECKED;
1703     }
1704     fp_gtk_style_context_set_state(context, flags);
1705 
1706     fp_gtk_render_background(context, cr, x, y, width, height);
1707     fp_gtk_render_frame(context, cr, x, y, width, height);
1708     fp_gtk_render_check(context, cr, x, y, width, height);
1709     disposeOrRestoreContext(context);
1710 }
1711 
1712 
1713 static void gtk3_paint_expander(WidgetType widget_type, GtkStateType state_type,
1714         const gchar *detail, gint x, gint y, gint width, gint height,
1715         GtkExpanderStyle expander_style)
1716 {
1717     gtk3_widget = gtk3_get_widget(widget_type);
1718 
1719     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
1720 
1721     fp_gtk_style_context_save (context);
1722 
1723     GtkStateFlags flags = get_gtk_flags(state_type);
1724     if (expander_style == GTK_EXPANDER_EXPANDED) {
1725         if (gtk3_version_3_14) {
1726             flags |= GTK_STATE_FLAG_CHECKED;
1727         } else {
1728             flags |= GTK_STATE_FLAG_ACTIVE;
1729         }
1730     }
1731 
1732     fp_gtk_style_context_set_state(context, flags);
1733 
1734     if (detail != 0) {
1735         transform_detail_string(detail, context);
1736     }
1737 
1738     fp_gtk_render_expander (context, cr, x + 2, y + 2, width - 4, height - 4);
1739 
1740     fp_gtk_style_context_restore (context);
1741 }
1742 
1743 static void gtk3_paint_extension(WidgetType widget_type, GtkStateType state_type,
1744         GtkShadowType shadow_type, const gchar *detail,
1745         gint x, gint y, gint width, gint height, GtkPositionType gap_side)
1746 {
1747     gtk3_widget = gtk3_get_widget(widget_type);
1748 
1749     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
1750 
1751     fp_gtk_style_context_save (context);
1752 
1753     GtkStateFlags flags = GTK_STATE_FLAG_NORMAL;
1754 
1755     if (state_type == 0) {
1756         flags = GTK_STATE_FLAG_ACTIVE;
1757     }
1758 
1759     fp_gtk_style_context_set_state(context, flags);
1760 
1761     if (detail != 0) {
1762         transform_detail_string(detail, context);
1763     }
1764     switch(gap_side) {
1765       case GTK_POS_LEFT:
1766         fp_gtk_style_context_add_class(context, "right");
1767         break;
1768       case GTK_POS_RIGHT:
1769         fp_gtk_style_context_add_class(context, "left");
1770         break;
1771       case GTK_POS_TOP:
1772         fp_gtk_style_context_add_class(context, "bottom");
1773         break;
1774       case GTK_POS_BOTTOM:
1775         fp_gtk_style_context_add_class(context, "top");
1776         break;
1777       default:
1778         break;
1779     }
1780 
1781     fp_gtk_render_extension(context, cr, x, y, width, height, gap_side);
1782 
1783     fp_gtk_style_context_restore (context);
1784 }
1785 
1786 static void gtk3_paint_flat_box(WidgetType widget_type, GtkStateType state_type,
1787         GtkShadowType shadow_type, const gchar *detail,
1788         gint x, gint y, gint width, gint height, gboolean has_focus)
1789 {
1790     if (state_type == GTK_STATE_PRELIGHT &&
1791         (widget_type == CHECK_BOX || widget_type == RADIO_BUTTON)) {
1792         return;
1793     }
1794 
1795     GtkStyleContext* context = NULL;
1796     if (widget_type == TOOL_TIP) {
1797         context = get_style(widget_type, detail);
1798         fp_gtk_style_context_add_class(context, "background");
1799     } else {
1800         gtk3_widget = gtk3_get_widget(widget_type);
1801         context = fp_gtk_widget_get_style_context (gtk3_widget);
1802         fp_gtk_style_context_save (context);
1803         if (detail != 0) {
1804             transform_detail_string(detail, context);
1805         }
1806     }
1807 
1808     GtkStateFlags flags = get_gtk_flags(state_type);
1809 
1810     if (has_focus) {
1811         flags |= GTK_STATE_FLAG_FOCUSED;
1812     }
1813 
1814     fp_gtk_style_context_set_state (context, flags);
1815 
1816     if (widget_type == COMBO_BOX_TEXT_FIELD) {
1817         width += height /2;
1818     }
1819 
1820     fp_gtk_render_background (context, cr, x, y, width, height);
1821     if (widget_type == TOOL_TIP) {
1822         disposeOrRestoreContext(context);
1823     } else {
1824         fp_gtk_style_context_restore (context);
1825     }
1826 }
1827 
1828 static void gtk3_paint_focus(WidgetType widget_type, GtkStateType state_type,
1829         const char *detail, gint x, gint y, gint width, gint height)
1830 {
1831     gtk3_widget = gtk3_get_widget(widget_type);
1832 
1833     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
1834     fp_gtk_style_context_save (context);
1835 
1836     transform_detail_string(detail, context);
1837     fp_gtk_render_focus (context, cr, x, y, width, height);
1838 
1839     fp_gtk_style_context_restore (context);
1840 
1841 }
1842 
1843 static void gtk3_paint_handle(WidgetType widget_type, GtkStateType state_type,
1844         GtkShadowType shadow_type, const gchar *detail,
1845         gint x, gint y, gint width, gint height, GtkOrientation orientation)
1846 {
1847     gtk3_widget = gtk3_get_widget(widget_type);
1848 
1849     GtkStyleContext* context = get_style(widget_type, detail);
1850 
1851     GtkStateFlags flags = get_gtk_flags(state_type);
1852     fp_gtk_style_context_set_state(context, GTK_STATE_FLAG_PRELIGHT);
1853 
1854     if (detail != 0 && !(strcmp(detail, "paned") == 0)) {
1855         transform_detail_string(detail, context);
1856         fp_gtk_style_context_add_class (context, "handlebox_bin");
1857     }
1858 
1859     if (!(strcmp(detail, "paned") == 0)) {
1860         fp_gtk_render_handle(context, cr, x, y, width, height);
1861         fp_gtk_render_background(context, cr, x, y, width, height);
1862     } else {
1863         if (orientation == GTK_ORIENTATION_VERTICAL) {
1864             fp_gtk_render_handle(context, cr, x+width/2, y, 2, height);
1865             fp_gtk_render_background(context, cr, x+width/2, y, 2, height);
1866         } else {
1867             fp_gtk_render_handle(context, cr, x, y+height/2, width, 2);
1868             fp_gtk_render_background(context, cr, x, y+height/2, width, 2);
1869         }
1870     }
1871 
1872     disposeOrRestoreContext(context);
1873 }
1874 
1875 static void gtk3_paint_hline(WidgetType widget_type, GtkStateType state_type,
1876         const gchar *detail, gint x, gint y, gint width, gint height)
1877 {
1878     gtk3_widget = gtk3_get_widget(widget_type);
1879 
1880     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
1881 
1882     fp_gtk_style_context_save (context);
1883 
1884     if (detail != 0) {
1885         transform_detail_string(detail, context);
1886     }
1887 
1888     fp_gtk_render_line(context, cr, x, y, x + width, y);
1889 
1890     fp_gtk_style_context_restore (context);
1891 }
1892 
1893 static void gtk3_paint_vline(WidgetType widget_type, GtkStateType state_type,
1894         const gchar *detail, gint x, gint y, gint width, gint height)
1895 {
1896     gtk3_widget = gtk3_get_widget(widget_type);
1897 
1898 
1899     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
1900 
1901     fp_gtk_style_context_save (context);
1902 
1903     if (detail != 0) {
1904         transform_detail_string(detail, context);
1905     }
1906 
1907     fp_gtk_render_line(context, cr, x, y, x, y + height);
1908 
1909     fp_gtk_style_context_restore (context);
1910 }
1911 
1912 static void gtk3_paint_option(WidgetType widget_type, gint synth_state,
1913         const gchar *detail, gint x, gint y, gint width, gint height)
1914 {
1915      GtkStyleContext* context = get_style(widget_type, detail);
1916 
1917      GtkStateFlags flags = get_gtk_state_flags(synth_state);
1918      if (gtk3_version_3_14 && (synth_state & SELECTED)) {
1919          flags &= ~GTK_STATE_FLAG_SELECTED;
1920          flags |= GTK_STATE_FLAG_CHECKED;
1921      }
1922      fp_gtk_style_context_set_state(context, flags);
1923 
1924      fp_gtk_render_background(context, cr, x, y, width, height);
1925      fp_gtk_render_frame(context, cr, x, y, width, height);
1926      fp_gtk_render_option(context, cr, x, y, width, height);
1927      disposeOrRestoreContext(context);
1928 }
1929 
1930 static void gtk3_paint_shadow(WidgetType widget_type, GtkStateType state_type,
1931                        GtkShadowType shadow_type, const gchar *detail,
1932                        gint x, gint y, gint width, gint height,
1933                        gint synth_state, GtkTextDirection dir)
1934 {
1935     if (shadow_type == GTK_SHADOW_NONE) {
1936         return;
1937     }
1938     gtk3_widget = gtk3_get_widget(widget_type);
1939 
1940     /*
1941      * Some engines (e.g. clearlooks) will paint the shadow of certain
1942      * widgets (e.g. COMBO_BOX_TEXT_FIELD) differently depending on the
1943      * the text direction.
1944      */
1945     gtk3_set_direction(gtk3_widget, dir);
1946 
1947 
1948     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
1949     fp_gtk_style_context_save (context);
1950 
1951     if (detail) {
1952         transform_detail_string(detail, context);
1953     }
1954 
1955     GtkStateFlags flags = get_gtk_flags(state_type);
1956 
1957     if (synth_state & MOUSE_OVER) {
1958         flags |= GTK_STATE_FLAG_PRELIGHT;
1959     }
1960 
1961     if (synth_state & FOCUSED) {
1962         flags |= GTK_STATE_FLAG_FOCUSED;
1963     }
1964 
1965     fp_gtk_style_context_set_state (context, flags);
1966 
1967     if (widget_type == COMBO_BOX_TEXT_FIELD) {
1968         width += height / 2;
1969     }
1970     fp_gtk_render_frame(context, cr, x, y, width, height);
1971 
1972     fp_gtk_style_context_restore (context);
1973 
1974     /*
1975      * Reset the text direction to the default value so that we don't
1976      * accidentally affect other operations and widgets.
1977      */
1978     gtk3_set_direction(gtk3_widget, GTK_TEXT_DIR_LTR);
1979 }
1980 
1981 static void gtk3_paint_slider(WidgetType widget_type, GtkStateType state_type,
1982         GtkShadowType shadow_type, const gchar *detail,
1983         gint x, gint y, gint width, gint height, GtkOrientation orientation,
1984         gboolean has_focus)
1985 {
1986     GtkStyleContext *context = get_style(widget_type, detail);
1987 
1988     GtkStateFlags flags = get_gtk_flags(state_type);
1989 
1990     if (state_type == GTK_STATE_ACTIVE) {
1991         flags |= GTK_STATE_FLAG_PRELIGHT;
1992     }
1993 
1994     if (has_focus) {
1995         flags |= GTK_STATE_FLAG_FOCUSED;
1996     }
1997 
1998     fp_gtk_style_context_set_state (context, flags);
1999 
2000     fp_gtk_render_background (context, cr, x, y, width, height);
2001     fp_gtk_render_frame(context, cr, x, y, width, height);
2002     (*fp_gtk_render_slider)(context, cr, x, y, width, height, orientation);
2003     disposeOrRestoreContext(context);
2004 }
2005 
2006 static void gtk3_paint_background(WidgetType widget_type,
2007              GtkStateType state_type, gint x, gint y, gint width, gint height) {
2008     gtk3_widget = gtk3_get_widget(widget_type);
2009 
2010     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
2011     fp_gtk_style_context_save (context);
2012 
2013     GtkStateFlags flags = get_gtk_flags(state_type);
2014 
2015     fp_gtk_style_context_set_state (context, flags);
2016 
2017     fp_gtk_render_background (context, cr, x, y, width, height);
2018 
2019     fp_gtk_style_context_restore (context);
2020 }
2021 
2022 static GdkPixbuf *gtk3_get_stock_icon(gint widget_type, const gchar *stock_id,
2023         GtkIconSize size, GtkTextDirection direction, const char *detail)
2024 {
2025     int sz;
2026 
2027     switch(size) {
2028       case GTK_ICON_SIZE_MENU:
2029         sz = 16;
2030         break;
2031       case GTK_ICON_SIZE_SMALL_TOOLBAR:
2032         sz = 18;
2033         break;
2034       case GTK_ICON_SIZE_LARGE_TOOLBAR:
2035         sz = 24;
2036         break;
2037       case GTK_ICON_SIZE_BUTTON:
2038         sz = 20;
2039         break;
2040       case GTK_ICON_SIZE_DND:
2041         sz = 32;
2042         break;
2043       case GTK_ICON_SIZE_DIALOG:
2044         sz = 48;
2045         break;
2046       default:
2047         sz = 0;
2048         break;
2049     }
2050 
2051     init_containers();
2052     gtk3_widget = gtk3_get_widget((widget_type < 0) ? IMAGE : widget_type);
2053     (*fp_gtk_widget_set_direction)(gtk3_widget, direction);
2054     GtkIconTheme *icon_theme = fp_gtk_icon_theme_get_default();
2055     GdkPixbuf *result = fp_gtk_icon_theme_load_icon(icon_theme, stock_id, sz,
2056                                              GTK_ICON_LOOKUP_USE_BUILTIN, NULL);
2057     return result;
2058 }
2059 
2060 static jboolean gtk3_get_pixbuf_data(JNIEnv *env, GdkPixbuf* pixbuf,
2061                               jmethodID icon_upcall_method, jobject this) {
2062     if (!pixbuf) {
2063         return JNI_FALSE;
2064     }
2065     guchar *pixbuf_data = (*fp_gdk_pixbuf_get_pixels)(pixbuf);
2066     if (pixbuf_data) {
2067         int row_stride = (*fp_gdk_pixbuf_get_rowstride)(pixbuf);
2068         int width = (*fp_gdk_pixbuf_get_width)(pixbuf);
2069         int height = (*fp_gdk_pixbuf_get_height)(pixbuf);
2070         int bps = (*fp_gdk_pixbuf_get_bits_per_sample)(pixbuf);
2071         int channels = (*fp_gdk_pixbuf_get_n_channels)(pixbuf);
2072         gboolean alpha = (*fp_gdk_pixbuf_get_has_alpha)(pixbuf);
2073 
2074         jbyteArray data = (*env)->NewByteArray(env, (row_stride * height));
2075         JNU_CHECK_EXCEPTION_RETURN(env, JNI_FALSE);
2076 
2077         (*env)->SetByteArrayRegion(env, data, 0, (row_stride * height),
2078                                    (jbyte *)pixbuf_data);
2079         (*fp_g_object_unref)(pixbuf);
2080 
2081         /* Call the callback method to create the image on the Java side. */
2082         (*env)->CallVoidMethod(env, this, icon_upcall_method, data,
2083                 width, height, row_stride, bps, channels, alpha);
2084         return JNI_TRUE;
2085     }
2086     return JNI_FALSE;
2087 }
2088 
2089 static jboolean gtk3_get_file_icon_data(JNIEnv *env, const char *filename,
2090                  GError **error, jmethodID icon_upcall_method, jobject this) {
2091     GdkPixbuf* pixbuf = fp_gdk_pixbuf_new_from_file(filename, error);
2092     return gtk3_get_pixbuf_data(env, pixbuf, icon_upcall_method, this);
2093 }
2094 
2095 static jboolean gtk3_get_icon_data(JNIEnv *env, gint widget_type,
2096                               const gchar *stock_id, GtkIconSize size,
2097                               GtkTextDirection direction, const char *detail,
2098                               jmethodID icon_upcall_method, jobject this) {
2099     GdkPixbuf* pixbuf = gtk3_get_stock_icon(widget_type, stock_id, size,
2100                                        direction, detail);
2101     return gtk3_get_pixbuf_data(env, pixbuf, icon_upcall_method, this);
2102 }
2103 
2104 /*************************************************/
2105 static gint gtk3_get_xthickness(JNIEnv *env, WidgetType widget_type)
2106 {
2107     init_containers();
2108 
2109     gtk3_widget = gtk3_get_widget(widget_type);
2110     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
2111     if (context) {
2112         GtkBorder padding;
2113         fp_gtk_style_context_get_padding(context, 0, &padding);
2114         return padding.left + 1;
2115     }
2116     return 0;
2117 }
2118 
2119 static gint gtk3_get_ythickness(JNIEnv *env, WidgetType widget_type)
2120 {
2121     init_containers();
2122 
2123     gtk3_widget = gtk3_get_widget(widget_type);
2124     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
2125     if (context) {
2126         GtkBorder padding;
2127         fp_gtk_style_context_get_padding(context, 0, &padding);
2128         return padding.top + 1;
2129     }
2130     return 0;
2131 }
2132 
2133 /*************************************************/
2134 static guint8 recode_color(gdouble channel)
2135 {
2136     guint16 result = (guint16)(channel * 65535);
2137     if (result > 65535) {
2138         result = 65535;
2139     }
2140     return (guint8)( result >> 8);
2141 }
2142 
2143 static GtkStateFlags gtk3_get_state_flags(GtkStateType state_type) {
2144     switch (state_type)
2145     {
2146         case GTK_STATE_NORMAL:
2147             return GTK_STATE_FLAG_NORMAL;
2148         case GTK_STATE_ACTIVE:
2149             return GTK_STATE_FLAG_ACTIVE;
2150         case GTK_STATE_PRELIGHT:
2151             return GTK_STATE_FLAG_PRELIGHT;
2152         case GTK_STATE_SELECTED:
2153             return GTK_STATE_FLAG_SELECTED;
2154         case GTK_STATE_INSENSITIVE:
2155             return GTK_STATE_FLAG_INSENSITIVE;
2156         case GTK_STATE_INCONSISTENT:
2157             return GTK_STATE_FLAG_INCONSISTENT;
2158         case GTK_STATE_FOCUSED:
2159             return GTK_STATE_FLAG_FOCUSED;
2160     }
2161     return 0;
2162 }
2163 
2164 
2165 static void rgb_to_hls (gdouble *r, gdouble *g, gdouble *b) {
2166   gdouble min;
2167   gdouble max;
2168   gdouble red;
2169   gdouble green;
2170   gdouble blue;
2171   gdouble h, l, s;
2172   gdouble delta;
2173 
2174   red = *r;
2175   green = *g;
2176   blue = *b;
2177 
2178   if (red > green)
2179     {
2180       if (red > blue)
2181         max = red;
2182       else
2183         max = blue;
2184 
2185       if (green < blue)
2186         min = green;
2187       else
2188         min = blue;
2189     }
2190   else
2191     {
2192       if (green > blue)
2193         max = green;
2194       else
2195         max = blue;
2196 
2197       if (red < blue)
2198         min = red;
2199       else
2200         min = blue;
2201     }
2202 
2203   l = (max + min) / 2;
2204   s = 0;
2205   h = 0;
2206 
2207   if (max != min)
2208     {
2209       if (l <= 0.5)
2210         s = (max - min) / (max + min);
2211       else
2212         s = (max - min) / (2 - max - min);
2213 
2214       delta = max -min;
2215       if (red == max)
2216         h = (green - blue) / delta;
2217       else if (green == max)
2218         h = 2 + (blue - red) / delta;
2219       else if (blue == max)
2220         h = 4 + (red - green) / delta;
2221 
2222       h *= 60;
2223       if (h < 0.0)
2224         h += 360;
2225     }
2226 
2227   *r = h;
2228   *g = l;
2229   *b = s;
2230 }
2231 
2232 static void hls_to_rgb (gdouble *h, gdouble *l, gdouble *s)
2233 {
2234   gdouble hue;
2235   gdouble lightness;
2236   gdouble saturation;
2237   gdouble m1, m2;
2238   gdouble r, g, b;
2239 
2240   lightness = *l;
2241   saturation = *s;
2242 
2243   if (lightness <= 0.5)
2244     m2 = lightness * (1 + saturation);
2245   else
2246     m2 = lightness + saturation - lightness * saturation;
2247   m1 = 2 * lightness - m2;
2248 
2249   if (saturation == 0)
2250     {
2251       *h = lightness;
2252       *l = lightness;
2253       *s = lightness;
2254     }
2255   else
2256     {
2257       hue = *h + 120;
2258       while (hue > 360)
2259         hue -= 360;
2260       while (hue < 0)
2261         hue += 360;
2262 
2263       if (hue < 60)
2264         r = m1 + (m2 - m1) * hue / 60;
2265       else if (hue < 180)
2266         r = m2;
2267       else if (hue < 240)
2268         r = m1 + (m2 - m1) * (240 - hue) / 60;
2269       else
2270         r = m1;
2271 
2272       hue = *h;
2273       while (hue > 360)
2274         hue -= 360;
2275       while (hue < 0)
2276         hue += 360;
2277 
2278       if (hue < 60)
2279         g = m1 + (m2 - m1) * hue / 60;
2280       else if (hue < 180)
2281         g = m2;
2282       else if (hue < 240)
2283         g = m1 + (m2 - m1) * (240 - hue) / 60;
2284       else
2285         g = m1;
2286 
2287       hue = *h - 120;
2288       while (hue > 360)
2289         hue -= 360;
2290       while (hue < 0)
2291         hue += 360;
2292 
2293       if (hue < 60)
2294         b = m1 + (m2 - m1) * hue / 60;
2295       else if (hue < 180)
2296         b = m2;
2297       else if (hue < 240)
2298         b = m1 + (m2 - m1) * (240 - hue) / 60;
2299       else
2300         b = m1;
2301 
2302       *h = r;
2303       *l = g;
2304       *s = b;
2305     }
2306 }
2307 
2308 
2309 
2310 static void gtk3_style_shade (const GdkRGBA *a, GdkRGBA *b, gdouble k) {
2311   gdouble red = a->red;
2312   gdouble green = a->green;
2313   gdouble blue = a->blue;
2314 
2315   rgb_to_hls (&red, &green, &blue);
2316 
2317   green *= k;
2318   if (green > 1.0)
2319     green = 1.0;
2320   else if (green < 0.0)
2321     green = 0.0;
2322 
2323   blue *= k;
2324   if (blue > 1.0)
2325     blue = 1.0;
2326   else if (blue < 0.0)
2327     blue = 0.0;
2328 
2329   hls_to_rgb (&red, &green, &blue);
2330 
2331   b->red = red;
2332   b->green = green;
2333   b->blue = blue;
2334 }
2335 
2336 static GdkRGBA gtk3_get_color_for_flags(GtkStyleContext* context,
2337                                   GtkStateFlags flags, ColorType color_type) {
2338     GdkRGBA c, color;
2339     color.alpha = 1;
2340 
2341     switch (color_type)
2342     {
2343         case FOREGROUND:
2344         case TEXT_FOREGROUND:
2345             fp_gtk_style_context_get_color(context, flags, &color);
2346             break;
2347         case BACKGROUND:
2348         case TEXT_BACKGROUND:
2349             fp_gtk_style_context_get_background_color(context, flags, &color);
2350             break;
2351         case LIGHT:
2352             c = gtk3_get_color_for_flags(context, flags, BACKGROUND);
2353             gtk3_style_shade(&c, &color, LIGHTNESS_MULT);
2354             break;
2355         case DARK:
2356             c = gtk3_get_color_for_flags(context, flags, BACKGROUND);
2357             gtk3_style_shade (&c, &color, DARKNESS_MULT);
2358             break;
2359         case MID:
2360             {
2361                 GdkRGBA c1 = gtk3_get_color_for_flags(context, flags, LIGHT);
2362                 GdkRGBA c2 = gtk3_get_color_for_flags(context, flags, DARK);
2363                 color.red = (c1.red + c2.red) / 2;
2364                 color.green = (c1.green + c2.green) / 2;
2365                 color.blue = (c1.blue + c2.blue) / 2;
2366             }
2367             break;
2368         case FOCUS:
2369         case BLACK:
2370             color.red = 0;
2371             color.green = 0;
2372             color.blue = 0;
2373             break;
2374         case WHITE:
2375             color.red = 1;
2376             color.green = 1;
2377             color.blue = 1;
2378             break;
2379     }
2380     return color;
2381 }
2382 
2383 static gint gtk3_get_color_for_state(JNIEnv *env, WidgetType widget_type,
2384                               GtkStateType state_type, ColorType color_type)
2385 {
2386 
2387     gint result = 0;
2388 
2389     GtkStateFlags flags = gtk3_get_state_flags(state_type);
2390 
2391     init_containers();
2392 
2393     if (gtk3_version_3_20) {
2394         if ((widget_type == TEXT_FIELD || widget_type == PASSWORD_FIELD || widget_type == SPINNER_TEXT_FIELD ||
2395             widget_type == FORMATTED_TEXT_FIELD) && state_type == GTK_STATE_SELECTED && color_type == TEXT_BACKGROUND) {
2396             widget_type = TEXT_AREA;
2397         }
2398     }
2399 
2400     GtkStyleContext* context = NULL;
2401     if (widget_type == TOOL_TIP) {
2402         context = get_style(widget_type, "tooltip");
2403     } else {
2404         gtk3_widget = gtk3_get_widget(widget_type);
2405         context = fp_gtk_widget_get_style_context(gtk3_widget);
2406     }
2407     if (widget_type == CHECK_BOX_MENU_ITEM
2408      || widget_type == RADIO_BUTTON_MENU_ITEM) {
2409         flags &= GTK_STATE_FLAG_NORMAL | GTK_STATE_FLAG_SELECTED
2410                   | GTK_STATE_FLAG_INSENSITIVE | GTK_STATE_FLAG_FOCUSED;
2411     }
2412 
2413     GdkRGBA color = gtk3_get_color_for_flags(context, flags, color_type);
2414 
2415     if (recode_color(color.alpha) == 0) {
2416         color = gtk3_get_color_for_flags(
2417         fp_gtk_widget_get_style_context(gtk3_get_widget(INTERNAL_FRAME)),
2418         0, BACKGROUND);
2419     }
2420 
2421     result = recode_color(color.alpha) << 24 | recode_color(color.red) << 16 |
2422              recode_color(color.green) << 8 | recode_color(color.blue);
2423     if (widget_type == TOOL_TIP) {
2424         disposeOrRestoreContext(context);
2425     }
2426     return result;
2427 }
2428 
2429 /*************************************************/
2430 static jobject create_Boolean(JNIEnv *env, jboolean boolean_value);
2431 static jobject create_Integer(JNIEnv *env, jint int_value);
2432 static jobject create_Long(JNIEnv *env, jlong long_value);
2433 static jobject create_Float(JNIEnv *env, jfloat float_value);
2434 static jobject create_Double(JNIEnv *env, jdouble double_value);
2435 static jobject create_Character(JNIEnv *env, jchar char_value);
2436 static jobject create_Insets(JNIEnv *env, GtkBorder *border);
2437 
2438 static jobject gtk3_get_class_value(JNIEnv *env, WidgetType widget_type,
2439                                                      const char* key)
2440 {
2441     init_containers();
2442 
2443     gtk3_widget = gtk3_get_widget(widget_type);
2444 
2445     GValue value = { 0, { { 0 } } };
2446 
2447     GParamSpec* param = (*fp_gtk_widget_class_find_style_property)(
2448                                     ((GTypeInstance*)gtk3_widget)->g_class, key);
2449     if ( param )
2450     {
2451         (*fp_g_value_init)( &value, param->value_type );
2452         (*fp_gtk_widget_style_get_property)(gtk3_widget, key, &value);
2453 
2454         if ((*fp_g_type_is_a)( param->value_type, G_TYPE_BOOLEAN ))
2455         {
2456             gboolean val = (*fp_g_value_get_boolean)(&value);
2457             return create_Boolean(env, (jboolean)val);
2458         }
2459         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_CHAR ))
2460         {
2461             gchar val = (*fp_g_value_get_char)(&value);
2462             return create_Character(env, (jchar)val);
2463         }
2464         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_UCHAR ))
2465         {
2466             guchar val = (*fp_g_value_get_uchar)(&value);
2467             return create_Character(env, (jchar)val);
2468         }
2469         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_INT ))
2470         {
2471             gint val = (*fp_g_value_get_int)(&value);
2472             return create_Integer(env, (jint)val);
2473         }
2474         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_UINT ))
2475         {
2476             guint val = (*fp_g_value_get_uint)(&value);
2477                     return create_Integer(env, (jint)val);
2478         }
2479         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_LONG ))
2480         {
2481             glong val = (*fp_g_value_get_long)(&value);
2482             return create_Long(env, (jlong)val);
2483         }
2484         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_ULONG ))
2485         {
2486             gulong val = (*fp_g_value_get_ulong)(&value);
2487             return create_Long(env, (jlong)val);
2488         }
2489         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_INT64 ))
2490         {
2491             gint64 val = (*fp_g_value_get_int64)(&value);
2492             return create_Long(env, (jlong)val);
2493         }
2494         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_UINT64 ))
2495         {
2496             guint64 val = (*fp_g_value_get_uint64)(&value);
2497             return create_Long(env, (jlong)val);
2498         }
2499         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_FLOAT ))
2500         {
2501             gfloat val = (*fp_g_value_get_float)(&value);
2502             return create_Float(env, (jfloat)val);
2503         }
2504         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_DOUBLE ))
2505         {
2506             gdouble val = (*fp_g_value_get_double)(&value);
2507             return create_Double(env, (jdouble)val);
2508         }
2509         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_ENUM ))
2510         {
2511             gint val = (*fp_g_value_get_enum)(&value);
2512             return create_Integer(env, (jint)val);
2513         }
2514         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_FLAGS ))
2515         {
2516             guint val = (*fp_g_value_get_flags)(&value);
2517             return create_Integer(env, (jint)val);
2518         }
2519         else if ((*fp_g_type_is_a)( param->value_type, G_TYPE_STRING ))
2520         {
2521             const gchar* val = (*fp_g_value_get_string)(&value);
2522 
2523             /* We suppose that all values come in C locale and
2524              * utf-8 representation of a string is the same as
2525              * the string itself. If this isn't so we should
2526              * use g_convert.
2527              */
2528             return (*env)->NewStringUTF(env, val);
2529         }
2530         else if ((*fp_g_type_is_a)( param->value_type, GTK_TYPE_BORDER ))
2531         {
2532             GtkBorder *border = (GtkBorder*)(*fp_g_value_get_boxed)(&value);
2533             return border ? create_Insets(env, border) : NULL;
2534         }
2535 
2536         /*      TODO: Other types are not supported yet.*/
2537 /*        else if((*fp_g_type_is_a)( param->value_type, G_TYPE_PARAM ))
2538         {
2539             GParamSpec* val = (*fp_g_value_get_param)(&value);
2540             printf( "Param: %p\n", val );
2541         }
2542         else if((*fp_g_type_is_a)( param->value_type, G_TYPE_BOXED ))
2543         {
2544             gpointer* val = (*fp_g_value_get_boxed)(&value);
2545             printf( "Boxed: %p\n", val );
2546         }
2547         else if((*fp_g_type_is_a)( param->value_type, G_TYPE_POINTER ))
2548         {
2549             gpointer* val = (*fp_g_value_get_pointer)(&value);
2550             printf( "Pointer: %p\n", val );
2551         }
2552         else if((*fp_g_type_is_a)( param->value_type, G_TYPE_OBJECT ))
2553         {
2554             GObject* val = (GObject*)(*fp_g_value_get_object)(&value);
2555             printf( "Object: %p\n", val );
2556         }*/
2557     }
2558 
2559     return NULL;
2560 }
2561 
2562 static void gtk3_set_range_value(WidgetType widget_type, jdouble value,
2563                           jdouble min, jdouble max, jdouble visible)
2564 {
2565     GtkAdjustment *adj;
2566 
2567     gtk3_widget = gtk3_get_widget(widget_type);
2568 
2569     adj = (*fp_gtk_range_get_adjustment)((GtkRange *)gtk3_widget);
2570 
2571     fp_gtk_adjustment_set_value(adj, value);
2572     fp_gtk_adjustment_set_lower(adj, min);
2573     fp_gtk_adjustment_set_upper(adj, max);
2574     fp_gtk_adjustment_set_page_size(adj, visible);
2575 }
2576 
2577 /*************************************************/
2578 static jobject create_Object(JNIEnv *env, jmethodID *cid,
2579                              const char* class_name,
2580                              const char* signature,
2581                              jvalue* value)
2582 {
2583     jclass  class;
2584     jobject result;
2585 
2586     class = (*env)->FindClass(env, class_name);
2587     if (class == NULL)
2588         return NULL; /* can't find/load the class, exception thrown */
2589 
2590     if (*cid == NULL)
2591     {
2592         *cid = (*env)->GetMethodID(env, class, "<init>", signature);
2593         if (*cid == NULL)
2594         {
2595             (*env)->DeleteLocalRef(env, class);
2596             return NULL; /* can't find/get the method, exception thrown */
2597         }
2598     }
2599 
2600     result = (*env)->NewObjectA(env, class, *cid, value);
2601 
2602     (*env)->DeleteLocalRef(env, class);
2603     return result;
2604 }
2605 
2606 jobject create_Boolean(JNIEnv *env, jboolean boolean_value)
2607 {
2608     static jmethodID cid = NULL;
2609     jvalue value;
2610 
2611     value.z = boolean_value;
2612 
2613     return create_Object(env, &cid, "java/lang/Boolean", "(Z)V", &value);
2614 }
2615 
2616 jobject create_Integer(JNIEnv *env, jint int_value)
2617 {
2618     static jmethodID cid = NULL;
2619     jvalue value;
2620 
2621     value.i = int_value;
2622 
2623     return create_Object(env, &cid, "java/lang/Integer", "(I)V", &value);
2624 }
2625 
2626 jobject create_Long(JNIEnv *env, jlong long_value)
2627 {
2628     static jmethodID cid = NULL;
2629     jvalue value;
2630 
2631     value.j = long_value;
2632 
2633     return create_Object(env, &cid, "java/lang/Long", "(J)V", &value);
2634 }
2635 
2636 jobject create_Float(JNIEnv *env, jfloat float_value)
2637 {
2638     static jmethodID cid = NULL;
2639     jvalue value;
2640 
2641     value.f = float_value;
2642 
2643     return create_Object(env, &cid, "java/lang/Float", "(F)V", &value);
2644 }
2645 
2646 jobject create_Double(JNIEnv *env, jdouble double_value)
2647 {
2648     static jmethodID cid = NULL;
2649     jvalue value;
2650 
2651     value.d = double_value;
2652 
2653     return create_Object(env, &cid, "java/lang/Double", "(D)V", &value);
2654 }
2655 
2656 jobject create_Character(JNIEnv *env, jchar char_value)
2657 {
2658     static jmethodID cid = NULL;
2659     jvalue value;
2660 
2661     value.c = char_value;
2662 
2663     return create_Object(env, &cid, "java/lang/Character", "(C)V", &value);
2664 }
2665 
2666 
2667 jobject create_Insets(JNIEnv *env, GtkBorder *border)
2668 {
2669     static jmethodID cid = NULL;
2670     jvalue values[4];
2671 
2672     values[0].i = border->top;
2673     values[1].i = border->left;
2674     values[2].i = border->bottom;
2675     values[3].i = border->right;
2676 
2677     return create_Object(env, &cid, "java/awt/Insets", "(IIII)V", values);
2678 }
2679 
2680 /*********************************************/
2681 static jstring gtk3_get_pango_font_name(JNIEnv *env, WidgetType widget_type)
2682 {
2683     init_containers();
2684 
2685     gtk3_widget = gtk3_get_widget(widget_type);
2686     jstring  result = NULL;
2687     GtkStyleContext* context = fp_gtk_widget_get_style_context (gtk3_widget);
2688     if (context)
2689     {
2690         PangoFontDescription* fd = fp_gtk_style_context_get_font(context, 0);
2691         gchar* val = (*fp_pango_font_description_to_string)(fd);
2692         result = (*env)->NewStringUTF(env, val);
2693         (*fp_g_free)( val );
2694     }
2695 
2696     return result;
2697 }
2698 
2699 /***********************************************/
2700 static jobject get_string_property(JNIEnv *env, GtkSettings* settings,
2701                                                              const gchar* key) {
2702     jobject result = NULL;
2703     gchar*  strval = NULL;
2704 
2705     (*fp_g_object_get)(settings, key, &strval, NULL);
2706     result = (*env)->NewStringUTF(env, strval);
2707     (*fp_g_free)(strval);
2708 
2709     return result;
2710 }
2711 
2712 static jobject get_integer_property(JNIEnv *env, GtkSettings* settings,
2713                                                              const gchar* key) {
2714     gint intval = 0;
2715     (*fp_g_object_get)(settings, key, &intval, NULL);
2716     return create_Integer(env, intval);
2717 }
2718 
2719 static jobject get_boolean_property(JNIEnv *env, GtkSettings* settings,
2720                                                              const gchar* key) {
2721     gint intval = 0;
2722     (*fp_g_object_get)(settings, key, &intval, NULL);
2723     return create_Boolean(env, intval);
2724 }
2725 
2726 static jobject gtk3_get_setting(JNIEnv *env, Setting property)
2727 {
2728     GtkSettings* settings = (*fp_gtk_settings_get_default)();
2729 
2730     switch (property)
2731     {
2732         case GTK_FONT_NAME:
2733             return get_string_property(env, settings, "gtk-font-name");
2734         case GTK_ICON_SIZES:
2735             return get_string_property(env, settings, "gtk-icon-sizes");
2736         case GTK_CURSOR_BLINK:
2737             return get_boolean_property(env, settings, "gtk-cursor-blink");
2738         case GTK_CURSOR_BLINK_TIME:
2739             return get_integer_property(env, settings, "gtk-cursor-blink-time");
2740     }
2741 
2742     return NULL;
2743 }
2744 
2745 static void transform_detail_string (const gchar *detail,
2746                                                      GtkStyleContext *context) {
2747   if (!detail)
2748     return;
2749 
2750   if (strcmp (detail, "arrow") == 0)
2751     fp_gtk_style_context_add_class (context, "arrow");
2752   else if (strcmp (detail, "button") == 0)
2753     fp_gtk_style_context_add_class (context, "button");
2754   else if (strcmp (detail, "buttondefault") == 0)
2755     {
2756       fp_gtk_style_context_add_class (context, "button");
2757       fp_gtk_style_context_add_class (context, "default");
2758     }
2759   else if (strcmp (detail, "calendar") == 0)
2760     fp_gtk_style_context_add_class (context, "calendar");
2761   else if (strcmp (detail, "cellcheck") == 0)
2762     {
2763       fp_gtk_style_context_add_class (context, "cell");
2764       fp_gtk_style_context_add_class (context, "check");
2765     }
2766   else if (strcmp (detail, "cellradio") == 0)
2767     {
2768       fp_gtk_style_context_add_class (context, "cell");
2769       fp_gtk_style_context_add_class (context, "radio");
2770     }
2771   else if (strcmp (detail, "checkbutton") == 0)
2772     fp_gtk_style_context_add_class (context, "check");
2773   else if (strcmp (detail, "check") == 0)
2774     {
2775       fp_gtk_style_context_add_class (context, "check");
2776       fp_gtk_style_context_add_class (context, "menu");
2777     }
2778   else if (strcmp (detail, "radiobutton") == 0)
2779     {
2780       fp_gtk_style_context_add_class (context, "radio");
2781     }
2782   else if (strcmp (detail, "option") == 0)
2783     {
2784       fp_gtk_style_context_add_class (context, "radio");
2785       fp_gtk_style_context_add_class (context, "menu");
2786     }
2787   else if (strcmp (detail, "entry") == 0 ||
2788            strcmp (detail, "entry_bg") == 0)
2789     fp_gtk_style_context_add_class (context, "entry");
2790   else if (strcmp (detail, "expander") == 0)
2791     fp_gtk_style_context_add_class (context, "expander");
2792   else if (strcmp (detail, "tooltip") == 0)
2793     fp_gtk_style_context_add_class (context, "tooltip");
2794   else if (strcmp (detail, "frame") == 0)
2795     fp_gtk_style_context_add_class (context, "frame");
2796   else if (strcmp (detail, "scrolled_window") == 0)
2797     fp_gtk_style_context_add_class (context, "scrolled-window");
2798   else if (strcmp (detail, "viewport") == 0 ||
2799            strcmp (detail, "viewportbin") == 0)
2800     fp_gtk_style_context_add_class (context, "viewport");
2801   else if (strncmp (detail, "trough", 6) == 0)
2802     fp_gtk_style_context_add_class (context, "trough");
2803   else if (strcmp (detail, "spinbutton") == 0)
2804     fp_gtk_style_context_add_class (context, "spinbutton");
2805   else if (strcmp (detail, "spinbutton_up") == 0)
2806     {
2807       fp_gtk_style_context_add_class (context, "spinbutton");
2808       fp_gtk_style_context_add_class (context, "button");
2809       fp_gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
2810     }
2811   else if (strcmp (detail, "spinbutton_down") == 0)
2812     {
2813       fp_gtk_style_context_add_class (context, "spinbutton");
2814       fp_gtk_style_context_add_class (context, "button");
2815       fp_gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
2816     }
2817   else if ((detail[0] == 'h' || detail[0] == 'v') &&
2818            strncmp (&detail[1], "scrollbar_", 9) == 0)
2819     {
2820       fp_gtk_style_context_add_class (context, "button");
2821       fp_gtk_style_context_add_class (context, "scrollbar");
2822     }
2823   else if (strcmp (detail, "slider") == 0)
2824     {
2825       fp_gtk_style_context_add_class (context, "slider");
2826       fp_gtk_style_context_add_class (context, "scrollbar");
2827     }
2828   else if (strcmp (detail, "vscale") == 0 ||
2829            strcmp (detail, "hscale") == 0)
2830     {
2831       fp_gtk_style_context_add_class (context, "slider");
2832       fp_gtk_style_context_add_class (context, "scale");
2833     }
2834   else if (strcmp (detail, "menuitem") == 0)
2835     {
2836       fp_gtk_style_context_add_class (context, "menuitem");
2837       fp_gtk_style_context_add_class (context, "menu");
2838     }
2839   else if (strcmp (detail, "menu") == 0)
2840     {
2841       fp_gtk_style_context_add_class (context, "popup");
2842       fp_gtk_style_context_add_class (context, "menu");
2843     }
2844   else if (strcmp (detail, "accellabel") == 0)
2845     fp_gtk_style_context_add_class (context, "accelerator");
2846   else if (strcmp (detail, "menubar") == 0)
2847     fp_gtk_style_context_add_class (context, "menubar");
2848   else if (strcmp (detail, "base") == 0)
2849     fp_gtk_style_context_add_class (context, "background");
2850   else if (strcmp (detail, "bar") == 0 ||
2851            strcmp (detail, "progressbar") == 0)
2852     fp_gtk_style_context_add_class (context, "progressbar");
2853   else if (strcmp (detail, "toolbar") == 0)
2854     fp_gtk_style_context_add_class (context, "toolbar");
2855   else if (strcmp (detail, "handlebox_bin") == 0)
2856     fp_gtk_style_context_add_class (context, "dock");
2857   else if (strcmp (detail, "notebook") == 0)
2858     fp_gtk_style_context_add_class (context, "notebook");
2859   else if (strcmp (detail, "tab") == 0)
2860   {
2861       fp_gtk_style_context_add_class (context, "notebook");
2862       fp_gtk_style_context_add_region (context, "tab", 0);
2863   } else if (strcmp (detail, "paned") == 0) {
2864       fp_gtk_style_context_add_class (context, "pane-separator");
2865   }
2866   else if (fp_g_str_has_prefix (detail, "cell"))
2867     {
2868       GtkRegionFlags row, col;
2869       gboolean ruled = FALSE;
2870       gchar** tokens;
2871       guint i;
2872 
2873       tokens = fp_g_strsplit (detail, "_", -1);
2874       row = col = 0;
2875       i = 0;
2876 
2877       while (tokens[i])
2878         {
2879           if (strcmp (tokens[i], "even") == 0)
2880             row |= GTK_REGION_EVEN;
2881           else if (strcmp (tokens[i], "odd") == 0)
2882             row |= GTK_REGION_ODD;
2883           else if (strcmp (tokens[i], "start") == 0)
2884             col |= GTK_REGION_FIRST;
2885           else if (strcmp (tokens[i], "end") == 0)
2886             col |= GTK_REGION_LAST;
2887           else if (strcmp (tokens[i], "ruled") == 0)
2888             ruled = TRUE;
2889           else if (strcmp (tokens[i], "sorted") == 0)
2890             col |= GTK_REGION_SORTED;
2891 
2892           i++;
2893         }
2894 
2895       if (!ruled)
2896         row &= ~(GTK_REGION_EVEN | GTK_REGION_ODD);
2897 
2898       fp_gtk_style_context_add_class (context, "cell");
2899       fp_gtk_style_context_add_region (context, "row", row);
2900       fp_gtk_style_context_add_region (context, "column", col);
2901 
2902       fp_g_strfreev (tokens);
2903     }
2904 }
2905 
2906 static gboolean gtk3_get_drawable_data(JNIEnv *env, jintArray pixelArray,
2907      int x, jint y, jint width, jint height, jint jwidth, int dx, int dy,
2908                                                                    jint scale) {
2909     GdkPixbuf *pixbuf;
2910     jint *ary;
2911 
2912     GdkWindow *root = (*fp_gdk_get_default_root_window)();
2913     if (gtk3_version_3_10) {
2914         int win_scale = (*fp_gdk_window_get_scale_factor)(root);
2915         pixbuf = (*fp_gdk_pixbuf_get_from_drawable)(
2916             root, x, y, (int) (width / (float) win_scale + 0.5), (int) (height / (float) win_scale + 0.5));
2917     } else {
2918         pixbuf = (*fp_gdk_pixbuf_get_from_drawable)(root, x, y, width, height);
2919     }
2920 
2921     if (pixbuf && scale != 1) {
2922         GdkPixbuf *scaledPixbuf;
2923         x /= scale;
2924         y /= scale;
2925         width /= scale;
2926         height /= scale;
2927         dx /= scale;
2928         dy /= scale;
2929         scaledPixbuf = (*fp_gdk_pixbuf_scale_simple)(pixbuf, width, height,
2930                                                      GDK_INTERP_BILINEAR);
2931         (*fp_g_object_unref)(pixbuf);
2932         pixbuf = scaledPixbuf;
2933     }
2934 
2935     if (pixbuf) {
2936         int nchan = (*fp_gdk_pixbuf_get_n_channels)(pixbuf);
2937         int stride = (*fp_gdk_pixbuf_get_rowstride)(pixbuf);
2938         if ((*fp_gdk_pixbuf_get_width)(pixbuf) >= width
2939                 && (*fp_gdk_pixbuf_get_height)(pixbuf) >= height
2940                 && (*fp_gdk_pixbuf_get_bits_per_sample)(pixbuf) == 8
2941                 && (*fp_gdk_pixbuf_get_colorspace)(pixbuf) == GDK_COLORSPACE_RGB
2942                 && nchan >= 3
2943                 ) {
2944             guchar *p, *pix = (*fp_gdk_pixbuf_get_pixels)(pixbuf);
2945             ary = (*env)->GetPrimitiveArrayCritical(env, pixelArray, NULL);
2946             if (ary) {
2947                 jint _x, _y;
2948                 int index;
2949                 for (_y = 0; _y < height; _y++) {
2950                     for (_x = 0; _x < width; _x++) {
2951                         p = pix + _y * stride + _x * nchan;
2952 
2953                         index = (_y + dy) * jwidth + (_x + dx);
2954                         ary[index] = 0xff000000
2955                                         | (p[0] << 16)
2956                                         | (p[1] << 8)
2957                                         | (p[2]);
2958 
2959                     }
2960                 }
2961                 (*env)->ReleasePrimitiveArrayCritical(env, pixelArray, ary, 0);
2962             }
2963         }
2964         (*fp_g_object_unref)(pixbuf);
2965     }
2966     return JNI_FALSE;
2967 }
2968 
2969 static GdkWindow* gtk3_get_window(void *widget) {
2970     return fp_gtk_widget_get_window((GtkWidget*)widget);
2971 }
2972 
2973 static void gtk3_init(GtkApi* gtk) {
2974     gtk->version = GTK_3;
2975 
2976     gtk->show_uri_load = &gtk3_show_uri_load;
2977     gtk->unload = &gtk3_unload;
2978     gtk->flush_event_loop = &flush_gtk_event_loop;
2979     gtk->gtk_check_version = fp_gtk_check_version;
2980     gtk->get_setting = &gtk3_get_setting;
2981 
2982     gtk->paint_arrow = &gtk3_paint_arrow;
2983     gtk->paint_box = &gtk3_paint_box;
2984     gtk->paint_box_gap = &gtk3_paint_box_gap;
2985     gtk->paint_expander = &gtk3_paint_expander;
2986     gtk->paint_extension = &gtk3_paint_extension;
2987     gtk->paint_flat_box = &gtk3_paint_flat_box;
2988     gtk->paint_focus = &gtk3_paint_focus;
2989     gtk->paint_handle = &gtk3_paint_handle;
2990     gtk->paint_hline = &gtk3_paint_hline;
2991     gtk->paint_vline = &gtk3_paint_vline;
2992     gtk->paint_option = &gtk3_paint_option;
2993     gtk->paint_shadow = &gtk3_paint_shadow;
2994     gtk->paint_slider = &gtk3_paint_slider;
2995     gtk->paint_background = &gtk3_paint_background;
2996     gtk->paint_check = &gtk3_paint_check;
2997     gtk->set_range_value = &gtk3_set_range_value;
2998 
2999     gtk->init_painting = &gtk3_init_painting;
3000     gtk->copy_image = &gtk3_copy_image;
3001 
3002     gtk->get_xthickness = &gtk3_get_xthickness;
3003     gtk->get_ythickness = &gtk3_get_ythickness;
3004     gtk->get_color_for_state = &gtk3_get_color_for_state;
3005     gtk->get_class_value = &gtk3_get_class_value;
3006 
3007     gtk->get_pango_font_name = &gtk3_get_pango_font_name;
3008     gtk->get_icon_data = &gtk3_get_icon_data;
3009     gtk->get_file_icon_data = &gtk3_get_file_icon_data;
3010     gtk->gdk_threads_enter = fp_gdk_threads_enter;
3011     gtk->gdk_threads_leave = fp_gdk_threads_leave;
3012     gtk->gtk_show_uri = fp_gtk_show_uri;
3013     gtk->get_drawable_data = &gtk3_get_drawable_data;
3014     gtk->g_free = fp_g_free;
3015 
3016     gtk->gtk_file_chooser_get_filename = fp_gtk_file_chooser_get_filename;
3017     gtk->gtk_widget_hide = fp_gtk_widget_hide;
3018     gtk->gtk_main_quit = fp_gtk_main_quit;
3019     gtk->gtk_file_chooser_dialog_new = fp_gtk_file_chooser_dialog_new;
3020     gtk->gtk_file_chooser_set_current_folder =
3021                           fp_gtk_file_chooser_set_current_folder;
3022     gtk->gtk_file_chooser_set_filename = fp_gtk_file_chooser_set_filename;
3023     gtk->gtk_file_chooser_set_current_name =
3024                           fp_gtk_file_chooser_set_current_name;
3025     gtk->gtk_file_filter_add_custom = fp_gtk_file_filter_add_custom;
3026     gtk->gtk_file_chooser_set_filter = fp_gtk_file_chooser_set_filter;
3027     gtk->gtk_file_chooser_get_type = fp_gtk_file_chooser_get_type;
3028     gtk->gtk_file_filter_new = fp_gtk_file_filter_new;
3029     gtk->gtk_file_chooser_set_do_overwrite_confirmation =
3030                           fp_gtk_file_chooser_set_do_overwrite_confirmation;
3031     gtk->gtk_file_chooser_set_select_multiple =
3032                           fp_gtk_file_chooser_set_select_multiple;
3033     gtk->gtk_file_chooser_get_current_folder =
3034                           fp_gtk_file_chooser_get_current_folder;
3035     gtk->gtk_file_chooser_get_filenames = fp_gtk_file_chooser_get_filenames;
3036     gtk->gtk_g_slist_length = fp_gtk_g_slist_length;
3037     gtk->g_signal_connect_data = fp_g_signal_connect_data;
3038     gtk->gtk_widget_show = fp_gtk_widget_show;
3039     gtk->gtk_main = fp_gtk_main;
3040     gtk->gtk_main_level = fp_gtk_main_level;
3041     gtk->g_path_get_dirname = fp_g_path_get_dirname;
3042     gtk->gdk_x11_drawable_get_xid = fp_gdk_x11_drawable_get_xid;
3043     gtk->gtk_widget_destroy = fp_gtk_widget_destroy;
3044     gtk->gtk_window_present = fp_gtk_window_present;
3045     gtk->gtk_window_move = fp_gtk_window_move;
3046     gtk->gtk_window_resize = fp_gtk_window_resize;
3047     gtk->get_window = &gtk3_get_window;
3048 
3049     gtk->g_object_unref = fp_g_object_unref;
3050     gtk->g_list_append = fp_g_list_append;
3051     gtk->g_list_free = fp_g_list_free;
3052     gtk->g_list_free_full = fp_g_list_free_full;
3053 }