< prev index next >

modules/graphics/src/main/native-glass/gtk/glass_dnd.cpp

Print this page


   1 /*
   2  * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 #include "glass_dnd.h"
  26 #include "glass_gtkcompat.h"
  27 #include "glass_general.h"
  28 #include "glass_evloop.h"
  29 
  30 #include "com_sun_glass_events_DndEvent.h"
  31 #include "com_sun_glass_ui_gtk_GtkDnDClipboard.h"
  32 
  33 #include <jni.h>
  34 #include <cstring>
  35 
  36 #include <gtk/gtk.h>
  37 #include <gdk/gdkx.h>

  38 
  39 /************************* COMMON *********************************************/
  40 static jint translate_gdk_action_to_glass(GdkDragAction action)
  41 {
  42     jint result = 0;
  43     result |= (action & GDK_ACTION_COPY)? com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_COPY : 0;
  44     result |= (action & GDK_ACTION_MOVE)? com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_MOVE : 0;
  45     result |= (action & GDK_ACTION_LINK)? com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_REFERENCE : 0;
  46     return result;
  47 }
  48 
  49 static GdkDragAction translate_glass_action_to_gdk(jint action)
  50 {
  51     int result = 0;
  52     result |= (action & com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_COPY)? GDK_ACTION_COPY : 0;
  53     result |= (action & com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_MOVE)? GDK_ACTION_MOVE : 0;
  54     result |= (action & com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_REFERENCE)? GDK_ACTION_LINK : 0;
  55     return static_cast<GdkDragAction>(result);
  56 }
  57 


 148 
 149     memset(&enter_ctx, 0, sizeof(enter_ctx));
 150 }
 151 
 152 static void process_dnd_target_drag_enter(WindowContext *ctx, GdkEventDND *event)
 153 {
 154     reset_enter_ctx();
 155     enter_ctx.ctx = event->context;
 156     enter_ctx.just_entered = TRUE;
 157     gdk_window_get_origin(ctx->get_gdk_window(), &enter_ctx.dx, &enter_ctx.dy);
 158     is_dnd_owner = is_in_drag();
 159 }
 160 
 161 static void process_dnd_target_drag_motion(WindowContext *ctx, GdkEventDND *event)
 162 {
 163     if (!enter_ctx.ctx) {
 164         gdk_drag_status(event->context, static_cast<GdkDragAction>(0), GDK_CURRENT_TIME);
 165         return; // Do not process motion events if no enter event was received
 166     }
 167     jmethodID method = enter_ctx.just_entered ? jViewNotifyDragEnter : jViewNotifyDragOver;
 168     GdkDragAction suggested = GLASS_GDK_DRAG_CONTEXT_GET_SUGGESTED_ACTION(event->context);
 169     GdkDragAction result = translate_glass_action_to_gdk(mainEnv->CallIntMethod(ctx->get_jview(), method,
 170             (jint)event->x_root - enter_ctx.dx, (jint)event->y_root - enter_ctx.dy,
 171             (jint)event->x_root, (jint)event->y_root,
 172             translate_gdk_action_to_glass(suggested)));
 173     CHECK_JNI_EXCEPTION(mainEnv)
 174 
 175     if (enter_ctx.just_entered) {
 176         enter_ctx.just_entered = FALSE;
 177     }
 178     gdk_drag_status(event->context, result, GDK_CURRENT_TIME);
 179 }
 180 
 181 static void process_dnd_target_drag_leave(WindowContext *ctx, GdkEventDND *event)
 182 {
 183     (void)event;
 184 
 185     mainEnv->CallVoidMethod(ctx->get_jview(), jViewNotifyDragLeave, NULL);
 186     CHECK_JNI_EXCEPTION(mainEnv)
 187 }
 188 
 189 static void process_dnd_target_drop_start(WindowContext *ctx, GdkEventDND *event)
 190 {
 191     if (!enter_ctx.ctx || enter_ctx.just_entered) {
 192         gdk_drop_finish(event->context, FALSE, GDK_CURRENT_TIME);
 193         gdk_drop_reply(event->context, FALSE, GDK_CURRENT_TIME);
 194         return; // Do not process drop events if no enter event and subsequent motion event were received
 195     }
 196     GdkDragAction selected = GLASS_GDK_DRAG_CONTEXT_GET_SELECTED_ACTION(event->context);
 197 
 198     mainEnv->CallIntMethod(ctx->get_jview(), jViewNotifyDragDrop,
 199             (jint)event->x_root - enter_ctx.dx, (jint)event->y_root - enter_ctx.dy,
 200             (jint)event->x_root, (jint)event->y_root,
 201             translate_gdk_action_to_glass(selected));
 202     LOG_EXCEPTION(mainEnv)
 203 
 204     gdk_drop_finish(event->context, TRUE, GDK_CURRENT_TIME);
 205     gdk_drop_reply(event->context, TRUE, GDK_CURRENT_TIME);
 206 }
 207 
 208 static gboolean check_state_in_drag(JNIEnv *env)
 209 {
 210     if (!enter_ctx.ctx) {
 211         jclass jc = env->FindClass("java/lang/IllegalStateException");
 212         if (!env->ExceptionCheck()) {
 213             env->ThrowNew(jc,
 214                     "Cannot get supported actions. Drag pointer haven't entered the application window");
 215         }
 216         return TRUE;


 228         case GDK_DRAG_MOTION:
 229             process_dnd_target_drag_motion(ctx, event);
 230             break;
 231         case GDK_DRAG_LEAVE:
 232             process_dnd_target_drag_leave(ctx, event);
 233             break;
 234         case GDK_DROP_START:
 235             process_dnd_target_drop_start(ctx, event);
 236             break;
 237         default:
 238             break;
 239     }
 240 }
 241 
 242 jobjectArray dnd_target_get_mimes(JNIEnv *env)
 243 {
 244     if (check_state_in_drag(env)) {
 245         return NULL;
 246     }
 247     if (!enter_ctx.mimes) {
 248         GList* targets = GLASS_GDK_DRAG_CONTEXT_LIST_TARGETS(enter_ctx.ctx);
 249         jobject set = env->NewObject(jHashSetCls, jHashSetInit, NULL);
 250         EXCEPTION_OCCURED(env);
 251 
 252         while (targets) {
 253             GdkAtom target = GDK_POINTER_TO_ATOM(targets->data);
 254             gchar *name = gdk_atom_name(target);
 255 
 256             if (target_is_text(target)) {
 257                 jstring jStr = env->NewStringUTF("text/plain");
 258                 EXCEPTION_OCCURED(env);
 259                 env->CallBooleanMethod(set, jSetAdd, jStr, NULL);
 260                 EXCEPTION_OCCURED(env);
 261             }
 262 
 263             if (target_is_image(target)) {
 264                 jstring jStr = env->NewStringUTF("application/x-java-rawimage");
 265                 EXCEPTION_OCCURED(env);
 266                 env->CallBooleanMethod(set, jSetAdd, jStr, NULL);
 267                 EXCEPTION_OCCURED(env);
 268             }


 295                 EXCEPTION_OCCURED(env);
 296             }
 297 
 298             g_free(name);
 299             targets = targets->next;
 300         }
 301         enter_ctx.mimes = env->NewObjectArray(env->CallIntMethod(set, jSetSize, NULL),
 302                 jStringCls, NULL);
 303         EXCEPTION_OCCURED(env);
 304         enter_ctx.mimes = (jobjectArray)env->CallObjectMethod(set, jSetToArray, enter_ctx.mimes, NULL);
 305         enter_ctx.mimes = (jobjectArray)env->NewGlobalRef(enter_ctx.mimes);
 306     }
 307     return enter_ctx.mimes;
 308 }
 309 
 310 jint dnd_target_get_supported_actions(JNIEnv *env)
 311 {
 312     if (check_state_in_drag(env)) {
 313         return 0;
 314     }
 315     return translate_gdk_action_to_glass(GLASS_GDK_DRAG_CONTEXT_GET_ACTIONS(enter_ctx.ctx));
 316 }
 317 
 318 static void wait_for_selection_data_hook(GdkEvent * event, void * data)
 319 {
 320     selection_data_ctx *ctx = (selection_data_ctx*)data;
 321     GdkWindow *dest = GLASS_GDK_DRAG_CONTEXT_GET_DEST_WINDOW(enter_ctx.ctx);
 322     if (event->type == GDK_SELECTION_NOTIFY &&
 323             event->selection.window == dest) {
 324         if (event->selection.property) { // if 0, that we received negative response
 325             ctx->length = gdk_selection_property_get(dest, &(ctx->data), &(ctx->type), &(ctx->format));
 326         }
 327         ctx->received = TRUE;
 328     }
 329 }
 330 
 331 static gboolean dnd_target_receive_data(JNIEnv *env, GdkAtom target, selection_data_ctx *selection_ctx)
 332 {
 333     GevlHookRegistration hookReg;
 334 
 335     memset(selection_ctx, 0, sizeof(selection_data_ctx));
 336 
 337     gdk_selection_convert(GLASS_GDK_DRAG_CONTEXT_GET_DEST_WINDOW(enter_ctx.ctx), gdk_drag_get_selection(enter_ctx.ctx), target,
 338                           GDK_CURRENT_TIME);
 339 
 340     hookReg =
 341             glass_evloop_hook_add(
 342                     (GevlHookFunction) wait_for_selection_data_hook,
 343                     selection_ctx);
 344     if (HANDLE_MEM_ALLOC_ERROR(env, hookReg,
 345                                "Failed to allocate event hook")) {
 346         return TRUE;
 347     }
 348 
 349     do {
 350         gtk_main_iteration();
 351     } while (!(selection_ctx->received));
 352 
 353 
 354     glass_evloop_hook_remove(hookReg);
 355     return selection_ctx->data != NULL;
 356 }
 357 


 543 
 544 static jint dnd_get_performed_action() {
 545     return dnd_performed_action;
 546 }
 547 
 548 static void dnd_pointer_grab(GdkCursor *cursor)
 549 {
 550     glass_gdk_master_pointer_grab(dnd_window, cursor);
 551 }
 552 
 553 static GdkDragContext *get_drag_context() {
 554     GdkDragContext *ctx;
 555     ctx = (GdkDragContext*)g_object_get_data(G_OBJECT(dnd_window), SOURCE_DND_CONTEXT);
 556     return ctx;
 557 }
 558 
 559 static gboolean dnd_finish_callback() {
 560     if (dnd_window) {
 561         dnd_set_performed_action(
 562                 translate_gdk_action_to_glass(
 563                     GLASS_GDK_DRAG_CONTEXT_GET_SELECTED_ACTION(
 564                         get_drag_context())));
 565 
 566         gdk_window_destroy(dnd_window);
 567         dnd_window = NULL;
 568         DragView::reset_drag_view();
 569     }
 570 
 571     return FALSE;
 572 }
 573 
 574 gboolean is_in_drag()
 575 {
 576     return dnd_window != NULL;
 577 }
 578 
 579 static void determine_actions(guint state, GdkDragAction *action, GdkDragAction *possible_actions)
 580 {
 581     GdkDragAction suggested = static_cast<GdkDragAction>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dnd_window), SOURCE_DND_ACTIONS)));
 582 
 583     if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {


 768                     jsize nraw = mainEnv->GetArrayLength(byteArray);
 769 
 770                     gdk_property_change(requestor, property, target,
 771                             8, GDK_PROP_MODE_REPLACE, (guchar *) raw, nraw);
 772 
 773                     mainEnv->ReleaseByteArrayElements(byteArray, raw, JNI_ABORT);
 774                     is_data_set = TRUE;
 775                 }
 776             }
 777         }
 778     }
 779 
 780     g_free(target_name);
 781     return is_data_set;
 782 }
 783 
 784 static void process_dnd_source_selection_req(GdkWindow *window, GdkEventSelection* event)
 785 {
 786     (void)window;
 787 
 788     GdkWindow *requestor = GLASS_GDK_SELECTION_EVENT_GET_REQUESTOR(event);





 789 
 790     gboolean is_data_set = FALSE;
 791     if (event->target == TARGET_UTF8_STRING_ATOM
 792             || event->target == TARGET_MIME_TEXT_PLAIN_ATOM) {
 793         is_data_set = dnd_source_set_utf8_string(requestor, event->property);
 794     } else if (event->target == TARGET_STRING_ATOM) {
 795         is_data_set = dnd_source_set_string(requestor, event->property);
 796 //    } else if (event->target == TARGET_COMPOUND_TEXT_ATOM) { // XXX compound text
 797     } else if (target_is_image(event->target)) {
 798         is_data_set = dnd_source_set_image(requestor, event->property, event->target);
 799     } else if (event->target == TARGET_MIME_URI_LIST_ATOM) {
 800         is_data_set = dnd_source_set_uri_list(requestor, event->property);
 801     } else {
 802         is_data_set = dnd_source_set_raw(requestor, event->property, event->target);
 803     }
 804 
 805     gdk_selection_send_notify(event->requestor, event->selection, event->target,
 806                                (is_data_set) ? event->property : GDK_NONE, event->time);
 807 }
 808 
 809 static void process_dnd_source_mouse_release(GdkWindow *window, GdkEventButton *event) {
 810     (void)window;
 811     (void)event;
 812 
 813     glass_gdk_master_pointer_ungrab();
 814 
 815     if (GLASS_GDK_DRAG_CONTEXT_GET_SELECTED_ACTION(get_drag_context())) {
 816         gdk_drag_drop(get_drag_context(), GDK_CURRENT_TIME);
 817     } else {
 818         gdk_drag_abort(get_drag_context(), GDK_CURRENT_TIME);
 819         /* let the gdk_drag_abort messages handled before finish */
 820         gdk_threads_add_idle((GSourceFunc) dnd_finish_callback, NULL);
 821     }
 822 }
 823 
 824 static void process_drag_motion(gint x_root, gint y_root, guint state)
 825 {
 826     DragView::move(x_root, y_root);
 827 
 828     GdkWindow *dest_window;
 829     GdkDragProtocol prot;
 830 
 831     gdk_drag_find_window_for_screen(get_drag_context(), NULL, gdk_screen_get_default(),
 832             x_root, y_root, &dest_window, &prot);
 833 
 834     if (prot != GDK_DRAG_PROTO_NONE) {
 835         GdkDragAction action, possible_actions;


 864                 event->keyval == GLASS_GDK_KEY_CONSTANT(Shift_R)) {
 865             new_mod = GDK_SHIFT_MASK;
 866         }
 867 
 868         if (event->type == GDK_KEY_PRESS) {
 869             state |= new_mod;
 870         } else {
 871             state ^= new_mod;
 872         }
 873 
 874         glass_gdk_master_pointer_get_position(&x, &y);
 875         process_drag_motion(x, y, state);
 876 
 877     }
 878 }
 879 
 880 static void process_dnd_source_drag_status(GdkWindow *window, GdkEventDND *event)
 881 {
 882     (void)window;
 883 
 884     GdkDragAction selected = GLASS_GDK_DRAG_CONTEXT_GET_SELECTED_ACTION(event->context);
 885     GdkCursor* cursor;
 886 
 887     if (selected & GDK_ACTION_COPY) {
 888         cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "dnd-copy");
 889         if (cursor == NULL) {
 890             cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "copy");
 891         }
 892     } else if (selected & (GDK_ACTION_MOVE | GDK_ACTION_PRIVATE)) {
 893         cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "dnd-move");
 894         if (cursor == NULL) {
 895             cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "move");
 896         }
 897         if (cursor == NULL) {
 898             cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "closedhand");
 899         }
 900     } else if (selected & GDK_ACTION_LINK) {
 901         cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "dnd-link");
 902         if (cursor == NULL) {
 903             cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "link");
 904         }


1195 }
1196 
1197 DragView::View::View(GdkPixbuf* _pixbuf, gboolean _is_raw_image,
1198                                 gboolean _is_offset_set, gint _offset_x, gint _offset_y) :
1199         pixbuf(_pixbuf),
1200         is_raw_image(_is_raw_image),
1201         is_offset_set(_is_offset_set),
1202         offset_x(_offset_x),
1203         offset_y(_offset_y)
1204 {
1205     width = gdk_pixbuf_get_width(pixbuf);
1206     height = gdk_pixbuf_get_height(pixbuf);
1207 
1208     widget = gtk_window_new(GTK_WINDOW_POPUP);
1209     gtk_window_set_type_hint(GTK_WINDOW(widget), GDK_WINDOW_TYPE_HINT_DND);
1210 
1211     screen_changed();
1212 
1213     gtk_widget_realize(widget);
1214 
1215     GdkRegion* region = gdk_region_new();
1216     gdk_window_input_shape_combine_region(gtk_widget_get_window(widget), region, 0,0);
1217     gdk_region_destroy(region);
1218 
1219     gtk_widget_set_app_paintable(widget, TRUE);
1220 
1221     g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(on_expose), this);
1222     g_signal_connect(G_OBJECT(widget), "screen-changed", G_CALLBACK(on_screen_changed), this);
1223 
1224     gtk_widget_set_size_request(widget, width, height);
1225 
1226     gtk_window_set_decorated(GTK_WINDOW(widget), FALSE);
1227     gtk_window_move(GTK_WINDOW(widget), -10000, -10000);
1228     gtk_window_set_opacity(GTK_WINDOW(widget), .7);
1229     gtk_widget_show_all(widget);
1230 }
1231 
1232 void DragView::View::screen_changed() {
1233     GdkScreen *screen = gtk_widget_get_screen(widget);
1234     GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
1235 
1236     if (!colormap || !gdk_screen_is_composited(screen)) {
1237         if (!is_offset_set) {
1238             offset_x = 1;
1239             offset_y = 1;
1240         }
1241     }
1242 
1243     if (!colormap) {
1244         colormap = gdk_screen_get_rgb_colormap(screen);
1245     }
1246     gtk_widget_set_colormap(widget, colormap);
1247 }
1248 
1249 void DragView::View::expose() {
1250     cairo_t *context = gdk_cairo_create(widget->window);
1251 
1252     cairo_surface_t* cairo_surface;
1253 
1254     guchar* pixels = is_raw_image
1255             ? (guchar*) convert_BGRA_to_RGBA((const int*) gdk_pixbuf_get_pixels(pixbuf),
1256                                                 gdk_pixbuf_get_rowstride(pixbuf),
1257                                                 height)
1258             : gdk_pixbuf_get_pixels(pixbuf);
1259 
1260     cairo_surface = cairo_image_surface_create_for_data(
1261             pixels,
1262             CAIRO_FORMAT_ARGB32,
1263             width, height, width * 4);
1264 
1265     cairo_set_source_surface(context, cairo_surface, 0, 0);
1266     cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
1267     cairo_paint(context);
1268 
1269     if (is_raw_image) {
1270         g_free(pixels);


   1 /*
   2  * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 #include "glass_dnd.h"

  26 #include "glass_general.h"
  27 #include "glass_evloop.h"
  28 
  29 #include "com_sun_glass_events_DndEvent.h"
  30 #include "com_sun_glass_ui_gtk_GtkDnDClipboard.h"
  31 
  32 #include <jni.h>
  33 #include <cstring>
  34 
  35 #include <gtk/gtk.h>
  36 #include <gdk/gdkx.h>
  37 #include <gdk/gdkkeysyms.h>
  38 
  39 /************************* COMMON *********************************************/
  40 static jint translate_gdk_action_to_glass(GdkDragAction action)
  41 {
  42     jint result = 0;
  43     result |= (action & GDK_ACTION_COPY)? com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_COPY : 0;
  44     result |= (action & GDK_ACTION_MOVE)? com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_MOVE : 0;
  45     result |= (action & GDK_ACTION_LINK)? com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_REFERENCE : 0;
  46     return result;
  47 }
  48 
  49 static GdkDragAction translate_glass_action_to_gdk(jint action)
  50 {
  51     int result = 0;
  52     result |= (action & com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_COPY)? GDK_ACTION_COPY : 0;
  53     result |= (action & com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_MOVE)? GDK_ACTION_MOVE : 0;
  54     result |= (action & com_sun_glass_ui_gtk_GtkDnDClipboard_ACTION_REFERENCE)? GDK_ACTION_LINK : 0;
  55     return static_cast<GdkDragAction>(result);
  56 }
  57 


 148 
 149     memset(&enter_ctx, 0, sizeof(enter_ctx));
 150 }
 151 
 152 static void process_dnd_target_drag_enter(WindowContext *ctx, GdkEventDND *event)
 153 {
 154     reset_enter_ctx();
 155     enter_ctx.ctx = event->context;
 156     enter_ctx.just_entered = TRUE;
 157     gdk_window_get_origin(ctx->get_gdk_window(), &enter_ctx.dx, &enter_ctx.dy);
 158     is_dnd_owner = is_in_drag();
 159 }
 160 
 161 static void process_dnd_target_drag_motion(WindowContext *ctx, GdkEventDND *event)
 162 {
 163     if (!enter_ctx.ctx) {
 164         gdk_drag_status(event->context, static_cast<GdkDragAction>(0), GDK_CURRENT_TIME);
 165         return; // Do not process motion events if no enter event was received
 166     }
 167     jmethodID method = enter_ctx.just_entered ? jViewNotifyDragEnter : jViewNotifyDragOver;
 168     GdkDragAction suggested = gdk_drag_context_get_suggested_action(event->context);
 169     GdkDragAction result = translate_glass_action_to_gdk(mainEnv->CallIntMethod(ctx->get_jview(), method,
 170             (jint)event->x_root - enter_ctx.dx, (jint)event->y_root - enter_ctx.dy,
 171             (jint)event->x_root, (jint)event->y_root,
 172             translate_gdk_action_to_glass(suggested)));
 173     CHECK_JNI_EXCEPTION(mainEnv)
 174 
 175     if (enter_ctx.just_entered) {
 176         enter_ctx.just_entered = FALSE;
 177     }
 178     gdk_drag_status(event->context, result, GDK_CURRENT_TIME);
 179 }
 180 
 181 static void process_dnd_target_drag_leave(WindowContext *ctx, GdkEventDND *event)
 182 {
 183     (void)event;
 184 
 185     mainEnv->CallVoidMethod(ctx->get_jview(), jViewNotifyDragLeave, NULL);
 186     CHECK_JNI_EXCEPTION(mainEnv)
 187 }
 188 
 189 static void process_dnd_target_drop_start(WindowContext *ctx, GdkEventDND *event)
 190 {
 191     if (!enter_ctx.ctx || enter_ctx.just_entered) {
 192         gdk_drop_finish(event->context, FALSE, GDK_CURRENT_TIME);
 193         gdk_drop_reply(event->context, FALSE, GDK_CURRENT_TIME);
 194         return; // Do not process drop events if no enter event and subsequent motion event were received
 195     }
 196     GdkDragAction selected = gdk_drag_context_get_selected_action(event->context);
 197 
 198     mainEnv->CallIntMethod(ctx->get_jview(), jViewNotifyDragDrop,
 199             (jint)event->x_root - enter_ctx.dx, (jint)event->y_root - enter_ctx.dy,
 200             (jint)event->x_root, (jint)event->y_root,
 201             translate_gdk_action_to_glass(selected));
 202     LOG_EXCEPTION(mainEnv)
 203 
 204     gdk_drop_finish(event->context, TRUE, GDK_CURRENT_TIME);
 205     gdk_drop_reply(event->context, TRUE, GDK_CURRENT_TIME);
 206 }
 207 
 208 static gboolean check_state_in_drag(JNIEnv *env)
 209 {
 210     if (!enter_ctx.ctx) {
 211         jclass jc = env->FindClass("java/lang/IllegalStateException");
 212         if (!env->ExceptionCheck()) {
 213             env->ThrowNew(jc,
 214                     "Cannot get supported actions. Drag pointer haven't entered the application window");
 215         }
 216         return TRUE;


 228         case GDK_DRAG_MOTION:
 229             process_dnd_target_drag_motion(ctx, event);
 230             break;
 231         case GDK_DRAG_LEAVE:
 232             process_dnd_target_drag_leave(ctx, event);
 233             break;
 234         case GDK_DROP_START:
 235             process_dnd_target_drop_start(ctx, event);
 236             break;
 237         default:
 238             break;
 239     }
 240 }
 241 
 242 jobjectArray dnd_target_get_mimes(JNIEnv *env)
 243 {
 244     if (check_state_in_drag(env)) {
 245         return NULL;
 246     }
 247     if (!enter_ctx.mimes) {
 248         GList* targets = gdk_drag_context_list_targets(enter_ctx.ctx);
 249         jobject set = env->NewObject(jHashSetCls, jHashSetInit, NULL);
 250         EXCEPTION_OCCURED(env);
 251 
 252         while (targets) {
 253             GdkAtom target = GDK_POINTER_TO_ATOM(targets->data);
 254             gchar *name = gdk_atom_name(target);
 255 
 256             if (target_is_text(target)) {
 257                 jstring jStr = env->NewStringUTF("text/plain");
 258                 EXCEPTION_OCCURED(env);
 259                 env->CallBooleanMethod(set, jSetAdd, jStr, NULL);
 260                 EXCEPTION_OCCURED(env);
 261             }
 262 
 263             if (target_is_image(target)) {
 264                 jstring jStr = env->NewStringUTF("application/x-java-rawimage");
 265                 EXCEPTION_OCCURED(env);
 266                 env->CallBooleanMethod(set, jSetAdd, jStr, NULL);
 267                 EXCEPTION_OCCURED(env);
 268             }


 295                 EXCEPTION_OCCURED(env);
 296             }
 297 
 298             g_free(name);
 299             targets = targets->next;
 300         }
 301         enter_ctx.mimes = env->NewObjectArray(env->CallIntMethod(set, jSetSize, NULL),
 302                 jStringCls, NULL);
 303         EXCEPTION_OCCURED(env);
 304         enter_ctx.mimes = (jobjectArray)env->CallObjectMethod(set, jSetToArray, enter_ctx.mimes, NULL);
 305         enter_ctx.mimes = (jobjectArray)env->NewGlobalRef(enter_ctx.mimes);
 306     }
 307     return enter_ctx.mimes;
 308 }
 309 
 310 jint dnd_target_get_supported_actions(JNIEnv *env)
 311 {
 312     if (check_state_in_drag(env)) {
 313         return 0;
 314     }
 315     return translate_gdk_action_to_glass(gdk_drag_context_get_actions(enter_ctx.ctx));
 316 }
 317 
 318 static void wait_for_selection_data_hook(GdkEvent * event, void * data)
 319 {
 320     selection_data_ctx *ctx = (selection_data_ctx*)data;
 321     GdkWindow *dest = glass_gdk_drag_context_get_dest_window(enter_ctx.ctx);
 322     if (event->type == GDK_SELECTION_NOTIFY &&
 323             event->selection.window == dest) {
 324         if (event->selection.property) { // if 0, that we received negative response
 325             ctx->length = gdk_selection_property_get(dest, &(ctx->data), &(ctx->type), &(ctx->format));
 326         }
 327         ctx->received = TRUE;
 328     }
 329 }
 330 
 331 static gboolean dnd_target_receive_data(JNIEnv *env, GdkAtom target, selection_data_ctx *selection_ctx)
 332 {
 333     GevlHookRegistration hookReg;
 334 
 335     memset(selection_ctx, 0, sizeof(selection_data_ctx));
 336 
 337     gdk_selection_convert(glass_gdk_drag_context_get_dest_window(enter_ctx.ctx), gdk_drag_get_selection(enter_ctx.ctx), target,
 338                           GDK_CURRENT_TIME);
 339 
 340     hookReg =
 341             glass_evloop_hook_add(
 342                     (GevlHookFunction) wait_for_selection_data_hook,
 343                     selection_ctx);
 344     if (HANDLE_MEM_ALLOC_ERROR(env, hookReg,
 345                                "Failed to allocate event hook")) {
 346         return TRUE;
 347     }
 348 
 349     do {
 350         gtk_main_iteration();
 351     } while (!(selection_ctx->received));
 352 
 353 
 354     glass_evloop_hook_remove(hookReg);
 355     return selection_ctx->data != NULL;
 356 }
 357 


 543 
 544 static jint dnd_get_performed_action() {
 545     return dnd_performed_action;
 546 }
 547 
 548 static void dnd_pointer_grab(GdkCursor *cursor)
 549 {
 550     glass_gdk_master_pointer_grab(dnd_window, cursor);
 551 }
 552 
 553 static GdkDragContext *get_drag_context() {
 554     GdkDragContext *ctx;
 555     ctx = (GdkDragContext*)g_object_get_data(G_OBJECT(dnd_window), SOURCE_DND_CONTEXT);
 556     return ctx;
 557 }
 558 
 559 static gboolean dnd_finish_callback() {
 560     if (dnd_window) {
 561         dnd_set_performed_action(
 562                 translate_gdk_action_to_glass(
 563                     gdk_drag_context_get_selected_action(
 564                         get_drag_context())));
 565 
 566         gdk_window_destroy(dnd_window);
 567         dnd_window = NULL;
 568         DragView::reset_drag_view();
 569     }
 570 
 571     return FALSE;
 572 }
 573 
 574 gboolean is_in_drag()
 575 {
 576     return dnd_window != NULL;
 577 }
 578 
 579 static void determine_actions(guint state, GdkDragAction *action, GdkDragAction *possible_actions)
 580 {
 581     GdkDragAction suggested = static_cast<GdkDragAction>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dnd_window), SOURCE_DND_ACTIONS)));
 582 
 583     if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {


 768                     jsize nraw = mainEnv->GetArrayLength(byteArray);
 769 
 770                     gdk_property_change(requestor, property, target,
 771                             8, GDK_PROP_MODE_REPLACE, (guchar *) raw, nraw);
 772 
 773                     mainEnv->ReleaseByteArrayElements(byteArray, raw, JNI_ABORT);
 774                     is_data_set = TRUE;
 775                 }
 776             }
 777         }
 778     }
 779 
 780     g_free(target_name);
 781     return is_data_set;
 782 }
 783 
 784 static void process_dnd_source_selection_req(GdkWindow *window, GdkEventSelection* event)
 785 {
 786     (void)window;
 787 
 788 #ifdef GLASS_GTK3
 789     GdkWindow *requestor = (event->requestor);
 790 #else
 791     GdkWindow *requestor =
 792         gdk_x11_window_foreign_new_for_display(gdk_display_get_default(), event->requestor);
 793 #endif
 794 
 795     gboolean is_data_set = FALSE;
 796     if (event->target == TARGET_UTF8_STRING_ATOM
 797             || event->target == TARGET_MIME_TEXT_PLAIN_ATOM) {
 798         is_data_set = dnd_source_set_utf8_string(requestor, event->property);
 799     } else if (event->target == TARGET_STRING_ATOM) {
 800         is_data_set = dnd_source_set_string(requestor, event->property);
 801 //    } else if (event->target == TARGET_COMPOUND_TEXT_ATOM) { // XXX compound text
 802     } else if (target_is_image(event->target)) {
 803         is_data_set = dnd_source_set_image(requestor, event->property, event->target);
 804     } else if (event->target == TARGET_MIME_URI_LIST_ATOM) {
 805         is_data_set = dnd_source_set_uri_list(requestor, event->property);
 806     } else {
 807         is_data_set = dnd_source_set_raw(requestor, event->property, event->target);
 808     }
 809 
 810     gdk_selection_send_notify(event->requestor, event->selection, event->target,
 811                                (is_data_set) ? event->property : GDK_NONE, event->time);
 812 }
 813 
 814 static void process_dnd_source_mouse_release(GdkWindow *window, GdkEventButton *event) {
 815     (void)window;
 816     (void)event;
 817 
 818     glass_gdk_master_pointer_ungrab();
 819 
 820     if (gdk_drag_context_get_selected_action(get_drag_context())) {
 821         gdk_drag_drop(get_drag_context(), GDK_CURRENT_TIME);
 822     } else {
 823         gdk_drag_abort(get_drag_context(), GDK_CURRENT_TIME);
 824         /* let the gdk_drag_abort messages handled before finish */
 825         gdk_threads_add_idle((GSourceFunc) dnd_finish_callback, NULL);
 826     }
 827 }
 828 
 829 static void process_drag_motion(gint x_root, gint y_root, guint state)
 830 {
 831     DragView::move(x_root, y_root);
 832 
 833     GdkWindow *dest_window;
 834     GdkDragProtocol prot;
 835 
 836     gdk_drag_find_window_for_screen(get_drag_context(), NULL, gdk_screen_get_default(),
 837             x_root, y_root, &dest_window, &prot);
 838 
 839     if (prot != GDK_DRAG_PROTO_NONE) {
 840         GdkDragAction action, possible_actions;


 869                 event->keyval == GLASS_GDK_KEY_CONSTANT(Shift_R)) {
 870             new_mod = GDK_SHIFT_MASK;
 871         }
 872 
 873         if (event->type == GDK_KEY_PRESS) {
 874             state |= new_mod;
 875         } else {
 876             state ^= new_mod;
 877         }
 878 
 879         glass_gdk_master_pointer_get_position(&x, &y);
 880         process_drag_motion(x, y, state);
 881 
 882     }
 883 }
 884 
 885 static void process_dnd_source_drag_status(GdkWindow *window, GdkEventDND *event)
 886 {
 887     (void)window;
 888 
 889     GdkDragAction selected = gdk_drag_context_get_selected_action(event->context);
 890     GdkCursor* cursor;
 891 
 892     if (selected & GDK_ACTION_COPY) {
 893         cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "dnd-copy");
 894         if (cursor == NULL) {
 895             cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "copy");
 896         }
 897     } else if (selected & (GDK_ACTION_MOVE | GDK_ACTION_PRIVATE)) {
 898         cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "dnd-move");
 899         if (cursor == NULL) {
 900             cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "move");
 901         }
 902         if (cursor == NULL) {
 903             cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "closedhand");
 904         }
 905     } else if (selected & GDK_ACTION_LINK) {
 906         cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "dnd-link");
 907         if (cursor == NULL) {
 908             cursor = gdk_cursor_new_from_name(gdk_display_get_default(), "link");
 909         }


1200 }
1201 
1202 DragView::View::View(GdkPixbuf* _pixbuf, gboolean _is_raw_image,
1203                                 gboolean _is_offset_set, gint _offset_x, gint _offset_y) :
1204         pixbuf(_pixbuf),
1205         is_raw_image(_is_raw_image),
1206         is_offset_set(_is_offset_set),
1207         offset_x(_offset_x),
1208         offset_y(_offset_y)
1209 {
1210     width = gdk_pixbuf_get_width(pixbuf);
1211     height = gdk_pixbuf_get_height(pixbuf);
1212 
1213     widget = gtk_window_new(GTK_WINDOW_POPUP);
1214     gtk_window_set_type_hint(GTK_WINDOW(widget), GDK_WINDOW_TYPE_HINT_DND);
1215 
1216     screen_changed();
1217 
1218     gtk_widget_realize(widget);
1219 




1220     gtk_widget_set_app_paintable(widget, TRUE);
1221 
1222     g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(on_expose), this);
1223     g_signal_connect(G_OBJECT(widget), "screen-changed", G_CALLBACK(on_screen_changed), this);
1224 
1225     gtk_widget_set_size_request(widget, width, height);
1226 
1227     gtk_window_set_decorated(GTK_WINDOW(widget), FALSE);
1228     gtk_window_move(GTK_WINDOW(widget), -10000, -10000);
1229     gtk_window_set_opacity(GTK_WINDOW(widget), .7);
1230     gtk_widget_show_all(widget);
1231 }
1232 
1233 void DragView::View::screen_changed() {
1234     GdkScreen *screen = gtk_widget_get_screen(widget);
1235     glass_configure_window_transparency(widget, true);
1236 
1237     if (!gdk_screen_is_composited(screen)) {
1238         if (!is_offset_set) {
1239             offset_x = 1;
1240             offset_y = 1;
1241         }
1242     }





1243 }
1244 
1245 void DragView::View::expose() {
1246     cairo_t *context = gdk_cairo_create(gtk_widget_get_window(widget));
1247 
1248     cairo_surface_t* cairo_surface;
1249 
1250     guchar* pixels = is_raw_image
1251             ? (guchar*) convert_BGRA_to_RGBA((const int*) gdk_pixbuf_get_pixels(pixbuf),
1252                                                 gdk_pixbuf_get_rowstride(pixbuf),
1253                                                 height)
1254             : gdk_pixbuf_get_pixels(pixbuf);
1255 
1256     cairo_surface = cairo_image_surface_create_for_data(
1257             pixels,
1258             CAIRO_FORMAT_ARGB32,
1259             width, height, width * 4);
1260 
1261     cairo_set_source_surface(context, cairo_surface, 0, 0);
1262     cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
1263     cairo_paint(context);
1264 
1265     if (is_raw_image) {
1266         g_free(pixels);


< prev index next >