| /* |
| * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| |
| #ifdef HEADLESS |
| #error This file should not be included in headless library |
| #endif |
| |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "jvm.h" |
| #include "jni.h" |
| #include "jni_util.h" |
| #include "jlong.h" |
| |
| #include "awt_DataTransferer.h" |
| #include "awt_XmDnD.h" |
| |
| #include "awt_p.h" |
| |
| #include "java_awt_Cursor.h" |
| #include "java_awt_dnd_DnDConstants.h" |
| #include "java_awt_event_MouseEvent.h" |
| #include "sun_awt_dnd_SunDragSourceContextPeer.h" |
| #include "sun_awt_motif_MComponentPeer.h" |
| #include "sun_awt_motif_MDragSourceContextPeer.h" |
| #include "sun_awt_motif_MDropTargetContextPeer.h" |
| |
| #include <X11/cursorfont.h> |
| #include <X11/Xutil.h> |
| /* |
| * Fix for 4285634. |
| * Include the private Motif header to enable access to lastEventState. |
| */ |
| #include <Xm/DragCP.h> |
| |
| #include "awt_Component.h" |
| #include "awt_Cursor.h" |
| #include "awt_AWTEvent.h" |
| |
| extern struct MComponentPeerIDs mComponentPeerIDs; |
| extern struct CursorIDs cursorIDs; |
| extern struct ContainerIDs containerIDs; |
| |
| /* globals */ |
| |
| |
| /* forwards */ |
| |
| static void awt_XmDropProc(Widget, XtPointer, XmDropProcCallbackStruct*); |
| static void awt_XmDragProc(Widget, XtPointer, XmDragProcCallbackStruct*); |
| |
| static void awt_XmTransferProc(Widget, XtPointer, Atom*, Atom*, XtPointer, |
| unsigned long*, int32_t*); |
| |
| /* for XmDragContext callbacks etc ... */ |
| static void awt_XmDragEnterProc(Widget, XtPointer, |
| XmDropSiteEnterCallbackStruct*); |
| static void awt_XmDragMotionProc(Widget, XtPointer, |
| XmDragMotionCallbackStruct*); |
| static void awt_XmDragLeaveProc(Widget, XtPointer, |
| XmDropSiteLeaveCallbackStruct*); |
| static void awt_XmDropOperationChangedProc(Widget, XtPointer, |
| XmDropStartCallbackStruct*); |
| static void awt_XmDropFinishProc(Widget, XtPointer, |
| XmDropFinishCallbackStruct*); |
| |
| static unsigned char DnDConstantsToXm(jint operations); |
| static jint XmToDnDConstants(unsigned char operations); |
| static unsigned char selectOperation(unsigned char operations); |
| |
| static void flush_cache(JNIEnv* env); |
| static void cacheDropDone(Boolean dropDone); |
| static Boolean isDropDone(); |
| |
| static void setCursor(JNIEnv* env, Display* d, jobject c, jint type, Time t); |
| |
| static Atom MOTIF_DROP_ATOM = None; |
| |
| /* in canvas.c */ |
| extern jint getModifiers(uint32_t state, jint button, jint keyCode); |
| |
| /** |
| * static cache of DropTarget related info. |
| */ |
| |
| static struct { |
| Widget w; /* if NULL, cache invalid */ |
| |
| jobject peer; |
| jobject component; |
| |
| jobject dtcpeer; |
| |
| Widget dt; |
| |
| jlongArray targets; |
| Cardinal nTargets; |
| |
| Boolean dropDone; |
| int32_t transfersPending; |
| Widget transfer; |
| |
| jint dropAction; /* used only on JVM transfers */ |
| |
| Boolean flushPending; |
| |
| Window win; |
| uint32_t state; |
| } _cache; |
| |
| uint32_t |
| buttonToMask(uint32_t button) { |
| switch (button) { |
| case Button1: |
| return Button1Mask; |
| case Button2: |
| return Button2Mask; |
| case Button3: |
| return Button3Mask; |
| case Button4: |
| return Button4Mask; |
| case Button5: |
| return Button5Mask; |
| default: |
| return 0; |
| } |
| } |
| |
| /* Fix for 4215643: extract the values cached on drag start and send |
| ButtonRelease event to the window which originated the drag */ |
| |
| void |
| dragsource_track_release(Widget w, XtPointer client_data, |
| XEvent * event, Boolean * cont) |
| { |
| DASSERT (event != NULL); |
| |
| if (_cache.win != None && |
| (buttonToMask(event->xbutton.button) & _cache.state)) { |
| |
| JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| Window win = event->xbutton.window; |
| event->xbutton.window = _cache.win; |
| awt_put_back_event(env, event); |
| event->xbutton.window = win; |
| _cache.win = None; |
| _cache.state = 0; |
| XtRemoveEventHandler(w, ButtonReleaseMask, False, |
| dragsource_track_release, NULL); |
| } |
| } |
| |
| static void |
| cancel_drag(XtPointer client_data, XtIntervalId* id) { |
| Time time = awt_util_getCurrentServerTime(); |
| Widget dc = XmGetDragContext(awt_root_shell, time); |
| |
| if (dc != NULL) { |
| Boolean sourceIsExternal = True; |
| XtVaGetValues(dc, XmNsourceIsExternal, &sourceIsExternal, NULL); |
| if (!sourceIsExternal) { |
| XEvent xevent; |
| XmDragCancel(dc); |
| |
| /* |
| * When running the internal drag-and-drop event loop |
| * (see DragC.c:InitiatorMainLoop) Motif DnD uses XtAppNextEvent, |
| * that processes all timer callbacks and then returns the next X |
| * event from the queue. Motif DnD doesn't check if the drag |
| * operation is cancelled after XtAppNextEvent returns and processes |
| * the returned event. When the drag operation is cancelled the |
| * XmDragContext widget is destroyed and Motif will crash if the new |
| * event is dispatched to the destroyed XmDragContext. |
| * We cancel the drag operation in the timer callback, so we putback |
| * a dummy X event. This event will be returned from XtAppNextEvent |
| * and Motif DnD will safely exit from the internal event loop. |
| */ |
| xevent.type = LASTEvent; |
| xevent.xany.send_event = True; |
| xevent.xany.display = awt_display; |
| xevent.xany.window = XtWindow(awt_root_shell); |
| XPutBackEvent(awt_display, &xevent); |
| } |
| } |
| } |
| |
| #define DONT_CARE -1 |
| |
| static void |
| awt_popupCallback(Widget shell, XtPointer closure, XtPointer call_data) { |
| XtGrabKind grab_kind = XtGrabNone; |
| |
| if (call_data != NULL) { |
| grab_kind = *((XtGrabKind*)call_data); |
| } |
| |
| if (XmIsVendorShell(shell)) { |
| int input_mode; |
| XtVaGetValues(shell, XmNmwmInputMode, &input_mode, NULL); |
| switch (input_mode) { |
| case DONT_CARE: |
| case MWM_INPUT_MODELESS: |
| grab_kind = XtGrabNonexclusive; break; |
| case MWM_INPUT_PRIMARY_APPLICATION_MODAL: |
| case MWM_INPUT_SYSTEM_MODAL: |
| case MWM_INPUT_FULL_APPLICATION_MODAL: |
| grab_kind = XtGrabExclusive; break; |
| } |
| } |
| |
| if (grab_kind == XtGrabExclusive) { |
| /* |
| * We should cancel the drag on the toolkit thread. Otherwise, it can be |
| * called while the toolkit thread is waiting inside some drag callback. |
| * In this case Motif will crash when the drag callback returns. |
| */ |
| XtAppAddTimeOut(awt_appContext, 0L, cancel_drag, NULL); |
| } |
| } |
| |
| static XtInitProc xt_shell_initialize = NULL; |
| |
| static void |
| awt_ShellInitialize(Widget req, Widget new, ArgList args, Cardinal *num_args) { |
| XtAddCallback(new, XtNpopupCallback, awt_popupCallback, NULL); |
| (*xt_shell_initialize)(req, new, args, num_args); |
| } |
| |
| /* |
| * Fix for 4484572. |
| * Modify the 'initialize' routine for all ShellWidget instances, so that it |
| * will install an XtNpopupCallback that cancels the current drag operation. |
| * It is needed, since AWT doesn't have full control over all ShellWidget |
| * instances (e.g. XmPopupMenu internally creates and popups an XmMenuShell). |
| */ |
| static void |
| awt_set_ShellInitialize() { |
| static Boolean inited = False; |
| |
| DASSERT(!inited); |
| if (inited) { |
| return; |
| } |
| |
| xt_shell_initialize = shellWidgetClass->core_class.initialize; |
| shellWidgetClass->core_class.initialize = (XtInitProc)awt_ShellInitialize; |
| inited = True; |
| } |
| |
| /** |
| * global function to initialize this client as a Dynamic-only app. |
| * |
| * gets called once during toolkit initialization. |
| */ |
| |
| void awt_initialize_Xm_DnD(Display* dpy) { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jclass clazz; |
| |
| XtVaSetValues(XmGetXmDisplay(dpy), |
| XmNdragInitiatorProtocolStyle, XmDRAG_DYNAMIC, |
| XmNdragReceiverProtocolStyle, XmDRAG_DYNAMIC, |
| NULL |
| ); |
| |
| MOTIF_DROP_ATOM = XInternAtom(dpy, _XA_MOTIF_DROP, False); |
| if (XSaveContext(dpy, MOTIF_DROP_ATOM, awt_convertDataContext, |
| (XPointer)NULL) == XCNOMEM) { |
| JNU_ThrowInternalError(env, ""); |
| return; |
| } |
| |
| /* No drop in progress. */ |
| cacheDropDone(True); |
| |
| /* |
| * Fix for BugTraq ID 4407057. |
| * Have to disable Motif default drag support, since it doesn't work |
| * reliably with our event dispatch mechanism. To do this we allow a drag |
| * operation only if it is registered on the awt_root_shell. |
| */ |
| awt_motif_enableSingleDragInitiator(awt_root_shell); |
| |
| awt_set_ShellInitialize(); |
| |
| /* |
| * load the Cursor stuff |
| */ |
| |
| clazz = (*env)->FindClass(env, "sun/awt/motif/MCustomCursor"); |
| |
| if (!JNU_IsNull(env, ((*env)->ExceptionOccurred(env)))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| |
| typedef struct DSInfoRec { |
| Widget widget; |
| |
| Pixmap animation_mask; |
| Pixmap animation_pixmap; |
| int32_t animation_pixmap_depth; |
| unsigned char animation_style; |
| XtPointer client_data; |
| XtCallbackProc drag_proc; |
| XtCallbackProc drop_proc; |
| XRectangle *drop_rectangles; |
| unsigned char drop_site_activity; |
| unsigned char drop_site_operations; |
| unsigned char drop_site_type; |
| Atom *import_targets; |
| Cardinal num_drop_rectangles; |
| Cardinal num_import_targets; |
| |
| struct DSInfoRec* next; |
| } DSInfoRec, * DSInfoPtr; |
| |
| #define ARG_COUNT 14 |
| |
| /* |
| * Allocates DSInfoRect structure, retrieves all attributes of a Motif drop site |
| * registered on the specified widget and puts them into the allocated storage. |
| * The caller should free the storage after use. |
| */ |
| DSInfoPtr get_drop_site_info(Widget w) { |
| Arg arglist[ARG_COUNT]; |
| Cardinal argcount = 0; |
| DSInfoPtr info = ZALLOC(DSInfoRec); |
| |
| if (info == NULL) { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError"); |
| return NULL; |
| } |
| |
| XtSetArg(arglist[argcount], XmNanimationMask, |
| (XtArgVal)&info->animation_mask); argcount++; |
| XtSetArg(arglist[argcount], XmNanimationPixmap, |
| (XtArgVal)&info->animation_pixmap); argcount++; |
| XtSetArg(arglist[argcount], XmNanimationPixmapDepth, |
| (XtArgVal)&info->animation_pixmap_depth); argcount++; |
| XtSetArg(arglist[argcount], XmNanimationStyle, |
| (XtArgVal)&info->animation_style); argcount++; |
| XtSetArg(arglist[argcount], XmNclientData, |
| (XtArgVal)&info->client_data); argcount++; |
| XtSetArg(arglist[argcount], XmNdragProc, |
| (XtArgVal)&info->drag_proc); argcount++; |
| XtSetArg(arglist[argcount], XmNdropProc, |
| (XtArgVal)&info->drop_proc); argcount++; |
| XtSetArg(arglist[argcount], XmNdropSiteActivity, |
| (XtArgVal)&info->drop_site_activity); argcount++; |
| XtSetArg(arglist[argcount], XmNdropSiteOperations, |
| (XtArgVal)&info->drop_site_operations); argcount++; |
| XtSetArg(arglist[argcount], XmNdropSiteType, |
| (XtArgVal)&info->drop_site_type); argcount++; |
| XtSetArg(arglist[argcount], XmNnumDropRectangles, |
| (XtArgVal)&info->num_drop_rectangles); argcount++; |
| XtSetArg(arglist[argcount], XmNnumImportTargets, |
| (XtArgVal)&info->num_import_targets); argcount++; |
| DASSERT(argcount == ARG_COUNT - 2); |
| |
| XmDropSiteRetrieve(w, arglist, argcount); |
| |
| if (info->num_import_targets > 0) { |
| Atom *targets = NULL; |
| |
| info->import_targets = malloc(info->num_import_targets * sizeof(Atom)); |
| |
| if (info->import_targets == NULL) { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| |
| free(info); |
| JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError"); |
| return NULL; |
| } |
| |
| XtSetArg(arglist[0], XmNimportTargets, (XtArgVal)&targets); |
| XmDropSiteRetrieve(w, arglist, 1); |
| |
| memcpy(info->import_targets, targets, |
| info->num_import_targets * sizeof(Atom)); |
| } |
| |
| if (info->drop_site_type == XmDROP_SITE_SIMPLE && info->num_drop_rectangles > 0) { |
| XRectangle *rectangles = NULL; |
| info->drop_rectangles = |
| malloc(info->num_drop_rectangles * sizeof(XRectangle)); |
| |
| if (info->drop_rectangles == NULL) { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| |
| if (info->import_targets != NULL) { |
| free(info->import_targets); |
| } |
| free(info); |
| JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError"); |
| return NULL; |
| } |
| |
| XtSetArg(arglist[0], XmNdropRectangles, (XtArgVal)&rectangles); |
| XmDropSiteRetrieve(w, arglist, 1); |
| |
| memcpy(info->drop_rectangles, rectangles, |
| info->num_drop_rectangles * sizeof(XRectangle)); |
| } else /* if (info->drop_site_type == XmDROP_SITE_COMPOSITE) */ { |
| info->num_drop_rectangles = 1; |
| info->drop_rectangles = NULL; |
| } |
| |
| info->widget = w; |
| return info; |
| } |
| |
| /* |
| * Registers a Motif drop site on a widget given the information |
| * in the passed DSInfoRec structure. |
| */ |
| void restore_drop_site(DSInfoPtr info) { |
| Arg arglist[ARG_COUNT]; |
| Cardinal argcount = 0; |
| |
| if (info->drop_site_type == XmDROP_SITE_COMPOSITE) { |
| info->num_drop_rectangles = 1; |
| info->drop_rectangles = NULL; |
| } |
| |
| XtSetArg(arglist[argcount], XmNanimationMask, |
| (XtArgVal)info->animation_mask); argcount++; |
| XtSetArg(arglist[argcount], XmNanimationPixmap, |
| (XtArgVal)info->animation_pixmap); argcount++; |
| XtSetArg(arglist[argcount], XmNanimationPixmapDepth, |
| (XtArgVal)info->animation_pixmap_depth); argcount++; |
| XtSetArg(arglist[argcount], XmNanimationStyle, |
| (XtArgVal)info->animation_style); argcount++; |
| XtSetArg(arglist[argcount], XmNclientData, |
| (XtArgVal)info->client_data); argcount++; |
| XtSetArg(arglist[argcount], XmNdragProc, |
| (XtArgVal)info->drag_proc); argcount++; |
| XtSetArg(arglist[argcount], XmNdropProc, |
| (XtArgVal)info->drop_proc); argcount++; |
| XtSetArg(arglist[argcount], XmNdropRectangles, |
| (XtArgVal)info->drop_rectangles); argcount++; |
| XtSetArg(arglist[argcount], XmNdropSiteActivity, |
| (XtArgVal)info->drop_site_activity); argcount++; |
| XtSetArg(arglist[argcount], XmNdropSiteOperations, |
| (XtArgVal)info->drop_site_operations); argcount++; |
| XtSetArg(arglist[argcount], XmNdropSiteType, |
| (XtArgVal)info->drop_site_type); argcount++; |
| XtSetArg(arglist[argcount], XmNimportTargets, |
| (XtArgVal)info->import_targets); argcount++; |
| XtSetArg(arglist[argcount], XmNnumDropRectangles, |
| (XtArgVal)info->num_drop_rectangles); argcount++; |
| XtSetArg(arglist[argcount], XmNnumImportTargets, |
| (XtArgVal)info->num_import_targets); argcount++; |
| DASSERT(argcount == ARG_COUNT); |
| |
| XmDropSiteUnregister(info->widget); |
| XmDropSiteRegister(info->widget, arglist, argcount); |
| XmDropSiteConfigureStackingOrder(info->widget, (Widget)NULL, XmABOVE); |
| } |
| |
| #undef ARG_COUNT |
| |
| /* |
| * This routine ensures that hierarchy of Motif drop sites is not broken |
| * when a new drop site is registered or an existing drop site is |
| * unregistered. It unregisters all drop sites registered on the descendants of |
| * the specified widget, then registers or unregisters a Motif drop site on the |
| * root widget depending on the value of registerNewSite. After that the routine |
| * restores all the drop sites on the descendants. |
| * The routine recursively traverses through the hierarchy of descendant Motif |
| * drop sites and stores the info for all drop sites in a list. Then this list |
| * is used to restore all descendant drop sites. |
| * @param w current widget in the hierarchy traversal |
| * @param top root widget of the traversed hierarchy - the one to be inserted or |
| * removed |
| * @param list a list of DSInfoRec structures which keep drop site info for |
| * child drop sites |
| * @param registerNewSite if True a new Motif drop site should be registered on |
| * the root widget. If False an existing drop site of the root widget |
| * should be unregistered. |
| * @param isDropSite if True the widget being currently traversed has an |
| * associated Motif drop site. |
| */ |
| static DSInfoPtr |
| update_drop_site_hierarchy(Widget w, Widget top, DSInfoPtr list, |
| Boolean registerNewSite, Boolean isDropSite) { |
| |
| Widget parent = NULL; |
| Widget *children = NULL; |
| Cardinal num_children = 0; |
| |
| if (w == NULL || !XtIsObject(w) || w->core.being_destroyed) { |
| return NULL; |
| } |
| |
| /* Get the child drop sites of the widget.*/ |
| if (XmDropSiteQueryStackingOrder(w, &parent, &children, |
| &num_children) == 0) { |
| /* |
| * The widget is declared to be a drop site, but the query fails. |
| * The drop site must be corrupted. Truncate traversal. |
| */ |
| if (isDropSite) { |
| return NULL; |
| } |
| } else { |
| /* The query succeded, so the widget is definitely a drop site. */ |
| isDropSite = True; |
| } |
| |
| /* Traverse descendants of the widget, if it is composite. */ |
| if (XtIsComposite(w)) { |
| Cardinal i = 0; |
| |
| /* If it is not a drop site, check all its children. */ |
| if (!isDropSite) { |
| XtVaGetValues(w, XmNchildren, &children, |
| XmNnumChildren, &num_children, NULL); |
| } |
| |
| for (i = 0; i < num_children; i++) { |
| list = update_drop_site_hierarchy(children[i], top, list, |
| registerNewSite, isDropSite); |
| } |
| } |
| |
| /* The storage allocated by XmDropSiteQueryStackingOrder must be freed.*/ |
| if (isDropSite && children != NULL) { |
| XtFree((void*)children); |
| } |
| |
| if (w != top) { |
| if (isDropSite) { |
| /* Prepend drop site info to the list and unregister a drop site.*/ |
| DSInfoPtr info = get_drop_site_info(w); |
| |
| if (info != NULL) { |
| info->next = list; |
| list = info; |
| } |
| XmDropSiteUnregister(w); |
| } |
| } else { |
| /* Traversal is complete.*/ |
| DSInfoPtr info = list; |
| |
| if (isDropSite) { |
| XmDropSiteUnregister(w); |
| } |
| |
| if (registerNewSite) { |
| Arg args[10]; |
| unsigned int nargs = 0; |
| |
| #define SetArg(n, v) args[nargs].name = n; args[nargs++].value = (XtArgVal)(v); |
| |
| SetArg(XmNanimationStyle, XmDRAG_UNDER_NONE); |
| SetArg(XmNdragProc, awt_XmDragProc); |
| SetArg(XmNdropProc, awt_XmDropProc); |
| SetArg(XmNdropSiteActivity, XmDROP_SITE_ACTIVE); |
| |
| SetArg(XmNdropSiteOperations, |
| XmDROP_LINK | XmDROP_MOVE | XmDROP_COPY); |
| |
| SetArg(XmNimportTargets, NULL); |
| SetArg(XmNnumImportTargets, 0); |
| |
| SetArg(XmNdropSiteType, XmDROP_SITE_COMPOSITE); |
| SetArg(XmNdropRectangles, (XRectangle*)NULL); |
| #undef SetArg |
| |
| XmDropSiteRegister(w, args, nargs); |
| XmDropSiteConfigureStackingOrder(w, (Widget)NULL, XmABOVE); |
| } |
| |
| /* Go through the list and restore all child drop sites.*/ |
| while (info != NULL) { |
| restore_drop_site(info); |
| |
| info = info->next; |
| list->next = NULL; |
| if (list->import_targets != NULL) { |
| free(list->import_targets); |
| } |
| if (list->drop_rectangles != NULL) { |
| free(list->drop_rectangles); |
| } |
| free(list); |
| list = info; |
| } |
| } |
| return list; |
| } |
| |
| void |
| register_drop_site(Widget w) { |
| update_drop_site_hierarchy(w, w, NULL, True, False); |
| } |
| |
| void |
| unregister_drop_site(Widget w) { |
| update_drop_site_hierarchy(w, w, NULL, False, True); |
| } |
| |
| DECLARE_JAVA_CLASS(dSCClazz, "sun/awt/motif/MDragSourceContextPeer") |
| DECLARE_JAVA_CLASS(dTCClazz, "sun/awt/motif/MDropTargetContextPeer") |
| |
| static void |
| call_dSCenter(JNIEnv* env, jobject this, jint targetActions, |
| jint modifiers, jint x, jint y) { |
| DECLARE_VOID_JAVA_METHOD(dSCenter, dSCClazz, "dragEnter", "(IIII)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dSCenter, targetActions, modifiers, x, y); |
| } |
| |
| static void |
| call_dSCmotion(JNIEnv* env, jobject this, jint targetActions, |
| jint modifiers, jint x, jint y) { |
| DECLARE_VOID_JAVA_METHOD(dSCmotion, dSCClazz, "dragMotion", "(IIII)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dSCmotion, targetActions, |
| modifiers, x, y); |
| } |
| |
| static void |
| call_dSCchanged(JNIEnv* env, jobject this, jint targetActions, |
| jint modifiers, jint x, jint y) { |
| DECLARE_VOID_JAVA_METHOD(dSCchanged, dSCClazz, "operationChanged", |
| "(IIII)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dSCchanged, targetActions, |
| modifiers, x, y); |
| } |
| |
| static void |
| call_dSCmouseMoved(JNIEnv* env, jobject this, jint targetActions, |
| jint modifiers, jint x, jint y) { |
| DECLARE_VOID_JAVA_METHOD(dSCmouseMoved, dSCClazz, "dragMouseMoved", |
| "(IIII)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dSCmouseMoved, targetActions, |
| modifiers, x, y); |
| } |
| |
| static void |
| call_dSCexit(JNIEnv* env, jobject this, jint x, jint y) { |
| DECLARE_VOID_JAVA_METHOD(dSCexit, dSCClazz, "dragExit", "(II)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dSCexit, x, y); |
| } |
| |
| static void |
| call_dSCddfinished(JNIEnv* env, jobject this, jboolean success, |
| jint operations, jint x, jint y) { |
| DECLARE_VOID_JAVA_METHOD(dSCddfinished, dSCClazz, "dragDropFinished", |
| "(ZIII)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dSCddfinished, success, operations, x, y); |
| } |
| |
| static jobject |
| call_dTCcreate(JNIEnv* env) { |
| DECLARE_STATIC_OBJECT_JAVA_METHOD(dTCcreate, dTCClazz, |
| "createMDropTargetContextPeer", |
| "()Lsun/awt/motif/MDropTargetContextPeer;"); |
| return (*env)->CallStaticObjectMethod(env, clazz, dTCcreate); |
| } |
| |
| static jint |
| call_dTCenter(JNIEnv* env, jobject this, jobject component, jint x, jint y, |
| jint dropAction, jint actions, jlongArray formats, |
| jlong nativeCtxt) { |
| DECLARE_JINT_JAVA_METHOD(dTCenter, dTCClazz, "handleEnterMessage", |
| "(Ljava/awt/Component;IIII[JJ)I"); |
| DASSERT(!JNU_IsNull(env, this)); |
| return (*env)->CallIntMethod(env, this, dTCenter, component, x, y, dropAction, |
| actions, formats, nativeCtxt); |
| } |
| |
| static void |
| call_dTCexit(JNIEnv* env, jobject this, jobject component, jlong nativeCtxt) { |
| DECLARE_VOID_JAVA_METHOD(dTCexit, dTCClazz, "handleExitMessage", |
| "(Ljava/awt/Component;J)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dTCexit, component, nativeCtxt); |
| } |
| |
| static jint |
| call_dTCmotion(JNIEnv* env, jobject this, jobject component, jint x, jint y, |
| jint dropAction, jint actions, jlongArray formats, |
| jlong nativeCtxt) { |
| DECLARE_JINT_JAVA_METHOD(dTCmotion, dTCClazz, "handleMotionMessage", |
| "(Ljava/awt/Component;IIII[JJ)I"); |
| DASSERT(!JNU_IsNull(env, this)); |
| return (*env)->CallIntMethod(env, this, dTCmotion, component, x, y, |
| dropAction, actions, formats, nativeCtxt); |
| } |
| |
| static void |
| call_dTCdrop(JNIEnv* env, jobject this, jobject component, jint x, jint y, |
| jint dropAction, jint actions, jlongArray formats, |
| jlong nativeCtxt) { |
| DECLARE_VOID_JAVA_METHOD(dTCdrop, dTCClazz, "handleDropMessage", |
| "(Ljava/awt/Component;IIII[JJ)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dTCdrop, component, x, y, |
| dropAction, actions, formats, nativeCtxt); |
| } |
| |
| static void |
| call_dTCnewData(JNIEnv* env, jobject this, jlong format, jobject type, |
| jbyteArray data) { |
| DECLARE_VOID_JAVA_METHOD(dTCnewData, dTCClazz, "newData", |
| "(JLjava/lang/String;[B)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dTCnewData, format, type, data); |
| } |
| |
| static void |
| call_dTCtxFailed(JNIEnv* env, jobject this, jlong format) { |
| DECLARE_VOID_JAVA_METHOD(dTCtxFailed, dTCClazz, "transferFailed", "(J)V"); |
| DASSERT(!JNU_IsNull(env, this)); |
| (*env)->CallVoidMethod(env, this, dTCtxFailed, format); |
| } |
| |
| /* |
| * Class: sun_awt_motif_MComponentPeer |
| * Method: addNativeDropTarget |
| * Signature: (Ljava/awt/dnd/DropTarget;)V |
| */ |
| |
| JNIEXPORT void JNICALL Java_sun_awt_motif_MComponentPeer_addNativeDropTarget |
| (JNIEnv *env, jobject this, jobject droptarget) |
| { |
| struct ComponentData* cdata = (struct ComponentData *)NULL; |
| DropSitePtr dropsite = (DropSitePtr)NULL; |
| |
| if (JNU_IsNull(env, droptarget)) { |
| JNU_ThrowNullPointerException(env, "NullPointerException"); |
| return; |
| } |
| |
| AWT_LOCK(); |
| |
| cdata = (struct ComponentData *) |
| JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.pData); |
| |
| if (cdata == NULL || cdata->widget == NULL) { |
| JNU_ThrowNullPointerException(env, "NullPointerException"); |
| AWT_UNLOCK(); |
| return; |
| } |
| |
| /* introduce a new Component as a root of a set of DropTargets */ |
| |
| if ((dropsite = cdata->dsi) == (DropSitePtr)NULL) { |
| dropsite = cdata->dsi = (DropSitePtr)ZALLOC(DropSiteInfo); |
| |
| if (dropsite == (DropSitePtr)NULL) { |
| JNU_ThrowOutOfMemoryError (env, "OutOfMemoryError"); |
| AWT_UNLOCK (); |
| return; |
| } |
| |
| dropsite->component = (*env)->NewGlobalRef |
| (env, (*env)->GetObjectField(env, this, |
| mComponentPeerIDs.target)); |
| dropsite->isComposite = True; |
| |
| /* |
| * Fix for Bug Id 4389284. |
| * Revalidate drop site hierarchy so that this drop site doesn't obscure |
| * drop sites that are already registered on its children. |
| */ |
| register_drop_site(cdata->widget); |
| } |
| |
| dropsite->dsCnt++; |
| |
| AWT_UNLOCK(); |
| } |
| |
| /* |
| * Class: sun_awt_motif_MComponentPeer |
| * Method: removeNativeDropTarget |
| * Signature: (Ljava/awt/dnd/DropTarget;)V |
| */ |
| |
| JNIEXPORT void JNICALL Java_sun_awt_motif_MComponentPeer_removeNativeDropTarget |
| (JNIEnv *env, jobject this, jobject droptarget) |
| { |
| struct ComponentData* cdata; |
| DropSitePtr dropsite; |
| |
| if (JNU_IsNull(env, droptarget)) { |
| JNU_ThrowNullPointerException(env, "NullPointerException"); |
| return; |
| } |
| |
| AWT_LOCK(); |
| |
| cdata = (struct ComponentData *) |
| JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.pData); |
| |
| if (cdata == NULL || cdata->widget == NULL) { |
| JNU_ThrowNullPointerException(env, "NullPointerException"); |
| AWT_UNLOCK(); |
| return; |
| } |
| |
| if ((dropsite = cdata->dsi) == (DropSitePtr)NULL) { |
| JNU_ThrowNullPointerException(env, "NullPointerException"); |
| AWT_UNLOCK(); |
| return; |
| } |
| |
| dropsite->dsCnt--; |
| if (dropsite->dsCnt == 0) { |
| /* |
| * Fix for Bug Id 4411368. |
| * Revalidate drop site hierarchy to prevent crash when a composite drop |
| * site is unregistered before its child drop sites. |
| */ |
| unregister_drop_site(cdata->widget); |
| |
| (*env)->DeleteGlobalRef(env, dropsite->component); |
| |
| free((void *)(cdata->dsi)); |
| cdata->dsi = (DropSitePtr)NULL; |
| } |
| |
| AWT_UNLOCK(); |
| } |
| |
| /** |
| * |
| */ |
| |
| JNIEXPORT void JNICALL |
| Java_sun_awt_motif_MDragSourceContextPeer_setNativeCursor(JNIEnv *env, |
| jobject this, |
| jlong nativeCtxt, |
| jobject cursor, |
| jint type) { |
| /* |
| * NOTE: no need to synchronize on awt_lock here, since we should have |
| * already acquired it in MDragSourceContextPeer.setCursor(). |
| */ |
| setCursor(env, awt_display, cursor, type, CurrentTime); |
| } |
| |
| /** |
| * |
| */ |
| |
| JNIEXPORT jlong JNICALL |
| Java_sun_awt_motif_MDropTargetContextPeer_startTransfer(JNIEnv *env, |
| jobject this, |
| jlong dragContextVal, |
| jlong atom) { |
| XmDropTransferEntryRec trec; |
| Widget dropTransfer; |
| Arg args[3]; |
| Cardinal nargs = 0; |
| jboolean isCopy; |
| Widget dragContext = (Widget)jlong_to_ptr(dragContextVal); |
| |
| AWT_LOCK(); |
| |
| trec.target = (Atom) atom; |
| trec.client_data = (XtPointer)trec.target; |
| |
| |
| #define SetArg(n, v) args[nargs].name = n; args[nargs++].value = (XtArgVal)(v); |
| |
| SetArg(XmNdropTransfers, &trec); |
| SetArg(XmNnumDropTransfers, 1 ); |
| SetArg(XmNtransferProc, awt_XmTransferProc); |
| |
| #undef SetArg |
| |
| _cache.transfer = dropTransfer = |
| XmDropTransferStart(dragContext, args, nargs); |
| |
| _cache.transfersPending++; |
| |
| AWT_NOTIFY_ALL(); |
| AWT_UNLOCK(); |
| |
| return ptr_to_jlong(dropTransfer); |
| } |
| |
| /** |
| * |
| */ |
| |
| JNIEXPORT void JNICALL |
| Java_sun_awt_motif_MDropTargetContextPeer_addTransfer(JNIEnv *env, |
| jobject this, |
| jlong dropTransferVal, |
| jlong atom) { |
| XmDropTransferEntryRec trec; |
| jboolean isCopy; |
| Widget dropTransfer=(Widget)jlong_to_ptr(dropTransferVal); |
| trec.target = (Atom)atom; |
| trec.client_data = (XtPointer)trec.target; |
| |
| AWT_LOCK(); |
| |
| XmDropTransferAdd(dropTransfer, &trec, 1); |
| |
| _cache.transfersPending++; |
| |
| AWT_NOTIFY_ALL(); |
| AWT_UNLOCK(); |
| } |
| |
| /** |
| * |
| */ |
| |
| JNIEXPORT void JNICALL Java_sun_awt_motif_MDropTargetContextPeer_dropDone |
| (JNIEnv *env, jobject this, jlong dragContextVal, jlong dropTransferVal, |
| jboolean isLocal, jboolean success, jint dropAction) |
| { |
| Widget dropTransfer = (Widget)jlong_to_ptr(dropTransferVal); |
| Widget dragContext = (Widget)jlong_to_ptr(dragContextVal); |
| |
| AWT_LOCK(); |
| |
| if (_cache.w == (Widget)NULL) { |
| AWT_UNLOCK(); |
| return; |
| } |
| |
| if (!isDropDone()) { |
| if (dropTransfer != (jlong)NULL) { |
| XtVaSetValues(dropTransfer, |
| XmNtransferStatus, |
| success == JNI_TRUE |
| ? XmTRANSFER_SUCCESS : XmTRANSFER_FAILURE, |
| NULL |
| ); |
| } else { |
| /* |
| * start a transfer that notifies failure |
| * this causes src side callbacks to be processed. |
| * However, you cannot pass an a success, so the workaround is |
| * to set _cache.transferSuccess to the proper value and read it |
| * on the other side. |
| */ |
| |
| |
| Arg arg; |
| |
| /* |
| * this is the workaround code |
| */ |
| _cache.transfer = NULL; |
| _cache.dropAction = dropAction; |
| |
| /* |
| * End workaround code |
| */ |
| |
| arg.name = XmNtransferStatus; |
| arg.value = (XtArgVal)(success == JNI_TRUE ? XmTRANSFER_SUCCESS |
| : XmTRANSFER_FAILURE |
| ); |
| |
| XmDropTransferStart(dragContext, &arg, 1); |
| } |
| |
| /* |
| * bugid# 4146717 |
| * |
| * If this is a local tx, then we never exec the awt_XmTransferProc, |
| * thus we need to flush the cache here as it is our only chance, |
| * otherwise we leave a mess for the next operation to fail on .... |
| * |
| */ |
| |
| if (isLocal == JNI_TRUE) |
| flush_cache(env); /* flush now, last chance */ |
| else |
| _cache.flushPending = True; /* flush pending in transfer proc */ |
| } |
| |
| cacheDropDone(True); |
| |
| AWT_NOTIFY_ALL(); |
| AWT_UNLOCK(); |
| } |
| |
| |
| static Boolean exitIdleProc = False; |
| static int32_t x_root = -1, y_root = -1; |
| |
| extern void waitForEvents(JNIEnv *env, int32_t fdXPipe, int32_t fdAWTPipe); |
| |
| static jint convertModifiers(uint32_t modifiers) { |
| return getModifiers(modifiers, 0, 0); |
| } |
| |
| static void |
| checkMouseMoved(XtPointer client_data) { |
| Window rootWindow, childWindow; |
| int32_t xw, yw, xr, yr; |
| uint32_t modifiers; |
| |
| /* |
| * When dragging over the root window XmNdragMotionCallback is not called |
| * (Motif feature). |
| * Since there is no legal way to receive MotionNotify events during drag |
| * we have to query for mouse position periodically. |
| */ |
| if (XQueryPointer(awt_display, XDefaultRootWindow(awt_display), |
| &rootWindow, &childWindow, |
| &xr, &yr, &xw, &yw, &modifiers) && |
| childWindow == None && (xr != x_root || yr != y_root)) { |
| |
| JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jobject this = (jobject)client_data; |
| |
| call_dSCmouseMoved(env, this, XmDROP_NOOP, convertModifiers(modifiers), |
| xr, yr); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| x_root = xr; |
| y_root = yr; |
| } |
| } |
| |
| static void IdleProc(XtPointer client_data, XtIntervalId* id) { |
| if (!exitIdleProc) { |
| JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| /* The pipe where X events arrive */ |
| int32_t fdXPipe = ConnectionNumber(awt_display) ; |
| |
| /* |
| * Motif DnD internal event loop doesn't process the events |
| * from the AWT putback event queue. So we pass -1 instead |
| * of the AWT read pipe descriptor to disable checking of |
| * the putback event queue. |
| */ |
| waitForEvents(env, fdXPipe, -1); |
| |
| checkMouseMoved(client_data); |
| /* Reschedule the timer callback */ |
| XtAppAddTimeOut(awt_appContext, AWT_DND_POLL_INTERVAL / 10, |
| IdleProc, client_data); |
| } |
| } |
| |
| static void RemoveIdleProc(Widget w, |
| XtPointer client_data, |
| XmDropFinishCallbackStruct* cbstruct) { |
| exitIdleProc = True; |
| } |
| |
| /** |
| * |
| */ |
| |
| JNIEXPORT jlong JNICALL |
| Java_sun_awt_motif_MDragSourceContextPeer_startDrag(JNIEnv *env, |
| jobject this, |
| jobject component, |
| jobject transferable, |
| jobject trigger, |
| jobject cursor, |
| jint ctype, |
| jint actions, |
| jlongArray formats, |
| jobject formatMap) { |
| Arg args[32]; |
| Cardinal nargs = 0; |
| jobject dscp = (*env)->NewGlobalRef(env, this); |
| jbyteArray bdata = |
| (jbyteArray)(*env)->GetObjectField(env, trigger, awtEventIDs.bdata); |
| Atom* targets = NULL; |
| jlong* jTargets; |
| jsize nTargets; |
| Widget dc; |
| XtCallbackRec dsecbr[2]; |
| XtCallbackRec dmcbr[2]; |
| XtCallbackRec occbr[2]; |
| XtCallbackRec dslcbr[2]; |
| XtCallbackRec dscbr[2]; |
| XtCallbackRec ddfcbr[2]; |
| XEvent* xevent; |
| unsigned char xmActions = DnDConstantsToXm(actions); |
| jboolean isCopy=JNI_TRUE; |
| awt_convertDataCallbackStruct* structPtr; |
| |
| #ifndef _LP64 /* Atom and jlong are different sizes in the 32-bit build */ |
| jsize i; |
| jlong* saveJTargets; |
| Atom* saveTargets; |
| #endif |
| |
| if (xmActions == XmDROP_NOOP) { |
| JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException", |
| "Invalid source actions."); |
| return ptr_to_jlong(NULL); |
| } |
| |
| if (JNU_IsNull(env, formats)) { |
| JNU_ThrowNullPointerException(env, "formats"); |
| return ptr_to_jlong(NULL); |
| } |
| |
| if (JNU_IsNull(env, bdata)) { |
| JNU_ThrowNullPointerException(env, |
| "null native data for trigger event"); |
| return ptr_to_jlong(NULL); |
| } |
| |
| nTargets = (*env)->GetArrayLength(env, formats); |
| |
| /* |
| * In debug build GetLongArrayElements aborts with assertion on an empty |
| * array. |
| */ |
| if (nTargets > 0) { |
| jTargets = (*env)->GetLongArrayElements(env, formats, &isCopy); |
| if (!JNU_IsNull(env, ((*env)->ExceptionOccurred(env)))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| if (jTargets != NULL) { |
| targets = (Atom *)malloc(nTargets * sizeof(Atom)); |
| if (targets != NULL) { |
| #ifdef _LP64 |
| memcpy(targets, jTargets, nTargets * sizeof(Atom)); |
| #else |
| saveJTargets = jTargets; |
| saveTargets = targets; |
| for (i = 0; i < nTargets; i++, targets++, jTargets++) { |
| *targets = (Atom)*jTargets; |
| } |
| jTargets = saveJTargets; |
| targets = saveTargets; |
| #endif |
| } |
| (*env)->ReleaseLongArrayElements(env, formats, jTargets, JNI_ABORT); |
| } |
| } |
| if (targets == NULL) { |
| nTargets = 0; |
| } |
| |
| #define SetCB(cbr, cb, cl) cbr[0].callback = (XtCallbackProc)cb; cbr[0].closure = (XtPointer)cl; cbr[1].callback = (XtCallbackProc)NULL; cbr[1].closure = (XtPointer)NULL |
| |
| #define SetArg(n, v) args[nargs].name = n; args[nargs++].value = (XtArgVal)(v); |
| |
| SetCB(dsecbr, awt_XmDragEnterProc, dscp); |
| SetCB(dmcbr, awt_XmDragMotionProc, dscp); |
| SetCB(occbr, awt_XmDropOperationChangedProc, dscp); |
| SetCB(dslcbr, awt_XmDragLeaveProc, dscp); |
| SetCB(ddfcbr, awt_XmDropFinishProc, dscp); |
| |
| SetArg(XmNblendModel, XmBLEND_NONE ); |
| SetArg(XmNdragOperations, xmActions ); |
| /* No incremental transfer */ |
| SetArg(XmNconvertProc, awt_convertData ); |
| SetArg(XmNdropSiteEnterCallback, dsecbr ); |
| SetArg(XmNdragMotionCallback, dmcbr ); |
| SetArg(XmNoperationChangedCallback, occbr ); |
| SetArg(XmNdropSiteLeaveCallback, dslcbr ); |
| SetArg(XmNdropFinishCallback, ddfcbr ); |
| SetArg(XmNexportTargets, targets ); |
| SetArg(XmNnumExportTargets, (Cardinal)nTargets ); |
| |
| { |
| jsize len = (*env)->GetArrayLength(env, bdata); |
| if (len <= 0) { |
| free(targets); |
| return ptr_to_jlong(NULL); |
| } |
| |
| xevent = calloc(1, len); |
| |
| if (xevent == NULL) { |
| free(targets); |
| JNU_ThrowOutOfMemoryError(env, ""); |
| return ptr_to_jlong(NULL); |
| } |
| |
| (*env)->GetByteArrayRegion(env, bdata, 0, len, (jbyte *)xevent); |
| |
| DASSERT(JNU_IsNull(env, (*env)->ExceptionOccurred(env))); |
| } |
| |
| if (xevent->type != ButtonPress && |
| xevent->type != ButtonRelease && |
| xevent->type != KeyRelease && |
| xevent->type != KeyPress && |
| xevent->type != MotionNotify) { |
| |
| JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException", |
| "A drag can only be initiated in response to an InputEvent."); |
| free(xevent); |
| free(targets); |
| return ptr_to_jlong(NULL); |
| } |
| |
| /* This call causes an UnsatisfiedLinkError on Linux. |
| * This function is a no-op for Motif 2.1. |
| * Since Linux only links against Motif 2.1, we can safely remove |
| * this function altogether from the Linux build. |
| * bchristi 1/22/2001 |
| */ |
| |
| #ifdef __solaris__ |
| awt_motif_adjustDragTriggerEvent(xevent); |
| #endif |
| |
| AWT_LOCK(); |
| |
| /* |
| * Fix for BugTraq ID 4357905. |
| * Drop is processed asynchronously on the event dispatch thread. |
| * Reject all drag attempts until the current drop is done. |
| */ |
| if (!isDropDone()) { |
| JNU_ThrowByName(env, "java/awt/dnd/InvalidDnDOperationException", |
| "Drop transfer in progress."); |
| free(xevent); |
| free(targets); |
| AWT_UNLOCK(); |
| return ptr_to_jlong(NULL); |
| } |
| |
| if (XFindContext(awt_display, MOTIF_DROP_ATOM, awt_convertDataContext, |
| (XPointer*)&structPtr) == XCNOMEM || structPtr != NULL) { |
| free(xevent); |
| free(targets); |
| AWT_UNLOCK(); |
| return ptr_to_jlong(NULL); |
| } |
| |
| structPtr = calloc(1, sizeof(awt_convertDataCallbackStruct)); |
| if (structPtr == NULL) { |
| free(xevent); |
| free(targets); |
| JNU_ThrowOutOfMemoryError(env, ""); |
| AWT_UNLOCK(); |
| return ptr_to_jlong(NULL); |
| } |
| |
| structPtr->source = (*env)->NewGlobalRef(env, component); |
| structPtr->transferable = (*env)->NewGlobalRef(env, transferable); |
| structPtr->formatMap = (*env)->NewGlobalRef(env, formatMap); |
| structPtr->formats = (*env)->NewGlobalRef(env, formats); |
| |
| if (XSaveContext(awt_display, MOTIF_DROP_ATOM, awt_convertDataContext, |
| (XPointer)structPtr) == XCNOMEM) { |
| free(structPtr); |
| free(xevent); |
| free(targets); |
| AWT_UNLOCK(); |
| return ptr_to_jlong(NULL); |
| } |
| |
| dc = XmDragStart(awt_root_shell, xevent, args, nargs); |
| |
| /* Fix for 4215643: remember the window corresponding to the drag source |
| and the button mask after the event which triggered drag start */ |
| |
| if (xevent->type == ButtonPress || xevent->type == MotionNotify) { |
| _cache.win = xevent->xbutton.window; |
| if (xevent->type == ButtonPress) { |
| _cache.state = buttonToMask(xevent->xbutton.button); |
| } else { |
| _cache.state = xevent->xmotion.state & (Button1Mask | Button2Mask); |
| } |
| XtAddEventHandler(dc, ButtonReleaseMask, False, |
| dragsource_track_release, NULL); |
| } |
| |
| free(targets); |
| |
| if (dc != (Widget)NULL) { |
| setCursor(env, awt_display, cursor, ctype, xevent->xbutton.time); |
| } |
| |
| free(xevent); |
| |
| /* |
| * With the new synchronization model we don't release awt_lock |
| * in the DragContext callbacks. During drag-n-drop operation |
| * the events processing is performed not by our awt_MToolkit_loop, |
| * but by internal Motif InitiatorMainLoop, which returns only |
| * when the operation is completed. So our polling mechanism doesn't |
| * have a chance to execute and even if there are no events in |
| * the queue AWT_LOCK will still be held by the Toolkit thread |
| * and so other threads will likely be blocked on it. |
| * |
| * The solution is to schedule a timer callback which checks |
| * for events and if the queue is empty releases AWT_LOCK and polls |
| * the X pipe for some time, then acquires AWT_LOCK back again |
| * and reschedules itself. |
| */ |
| if (dc != NULL) { |
| exitIdleProc = False; |
| XtAddCallback(dc, XmNdragDropFinishCallback, |
| (XtCallbackProc)RemoveIdleProc, NULL); |
| XtAppAddTimeOut(awt_appContext, AWT_DND_POLL_INTERVAL / 10, |
| IdleProc, (XtPointer)dscp); |
| } |
| |
| AWT_UNLOCK(); |
| |
| return ptr_to_jlong(dc); |
| |
| #undef SetArg |
| #undef SetCB |
| } |
| |
| /*****************************************************************************/ |
| |
| /** |
| * |
| */ |
| |
| static void setCursor(JNIEnv* env, Display* dpy, jobject cursor, jint type, |
| Time time) |
| { |
| Cursor xcursor = None; |
| |
| if (JNU_IsNull(env, cursor)) return; |
| |
| XChangeActivePointerGrab(dpy, |
| ButtonPressMask | |
| ButtonMotionMask | |
| ButtonReleaseMask | |
| EnterWindowMask | |
| LeaveWindowMask, |
| getCursor(env, cursor), |
| time |
| ); |
| |
| XSync(dpy, False); |
| } |
| |
| /** |
| * Update the cached targets for this widget |
| */ |
| |
| static Boolean updateCachedTargets(JNIEnv* env, Widget dt) { |
| Atom* targets = (Atom*)NULL; |
| Cardinal nTargets = (Cardinal)0; |
| Arg args[2]; |
| |
| /* |
| * Get the targets for this component |
| */ |
| args[0].name = XmNexportTargets; args[0].value = (XtArgVal)&targets; |
| args[1].name = XmNnumExportTargets; args[1].value = (XtArgVal)&nTargets; |
| XtGetValues(_cache.dt = dt, args, 2); |
| |
| /* |
| * Free the previous targets if there were any |
| */ |
| if (!JNU_IsNull(env, _cache.targets)) { |
| (*env)->DeleteGlobalRef(env, _cache.targets); |
| _cache.targets = (jlongArray)NULL; |
| } |
| |
| _cache.nTargets = nTargets; |
| |
| /* |
| * If the widget has targets (atoms) then copy them to the cache |
| */ |
| if (nTargets > 0) { |
| jboolean isCopy; |
| jlong* jTargets; |
| |
| #ifndef _LP64 /* Atom and jlong are different sizes in the 32-bit build */ |
| jlong* saveJTargets; |
| Cardinal i; |
| #endif |
| |
| _cache.targets = (*env)->NewLongArray(env, nTargets); |
| if (_cache.targets == NULL) { |
| _cache.nTargets = 0; |
| return False; |
| } |
| |
| _cache.targets = (*env)->NewGlobalRef(env, _cache.targets); |
| if (_cache.targets == NULL) { |
| _cache.nTargets = 0; |
| return False; |
| } |
| |
| jTargets = (*env)->GetLongArrayElements(env, _cache.targets, &isCopy); |
| if (jTargets == NULL) { |
| (*env)->DeleteGlobalRef(env, _cache.targets); |
| _cache.targets = NULL; |
| _cache.nTargets = 0; |
| return False; |
| } |
| |
| #ifdef _LP64 |
| memcpy(jTargets, targets, nTargets * sizeof(Atom)); |
| #else |
| saveJTargets = jTargets; |
| for (i = 0; i < nTargets; i++, jTargets++, targets++) { |
| *jTargets = (*targets & 0xFFFFFFFFLU); |
| } |
| jTargets = saveJTargets; |
| #endif |
| |
| (*env)->ReleaseLongArrayElements(env, _cache.targets, jTargets, 0); |
| return True; |
| } |
| |
| return False; |
| } |
| |
| |
| /** |
| * |
| */ |
| |
| static void flush_cache(JNIEnv* env) { |
| _cache.w = (Widget)NULL; |
| _cache.dt = (Widget)NULL; |
| |
| (*env)->DeleteGlobalRef(env, _cache.peer); |
| _cache.peer = (jobject)NULL; |
| |
| (*env)->DeleteGlobalRef(env, _cache.component); |
| _cache.component = (jobject)NULL; |
| |
| if (_cache.dtcpeer != (jobject)NULL) { |
| (*env)->DeleteGlobalRef(env, _cache.dtcpeer); |
| |
| _cache.dtcpeer = (jobject)NULL; |
| } |
| |
| _cache.nTargets = (Cardinal)0; |
| if (_cache.targets != (jlongArray)NULL) { |
| (*env)->DeleteGlobalRef(env, _cache.targets); |
| _cache.targets = (jlongArray)NULL; |
| } |
| |
| _cache.transfersPending = 0; |
| _cache.flushPending = False; |
| _cache.transfer = (Widget)NULL; |
| cacheDropDone(True); |
| } |
| |
| /** |
| * |
| */ |
| |
| static void update_cache(JNIEnv* env, Widget w, Widget dt) { |
| if(w != _cache.w) { |
| struct ComponentData* cdata = (struct ComponentData *)NULL; |
| Arg args[1] = |
| {{ XmNuserData, (XtArgVal)&_cache.peer}}; |
| |
| flush_cache(env); |
| |
| if (w == (Widget)NULL) return; |
| |
| XtGetValues(w, args, 1); |
| |
| if (JNU_IsNull(env, _cache.peer)) { |
| _cache.w = NULL; |
| |
| return; |
| } |
| |
| cdata = (struct ComponentData *) |
| JNU_GetLongFieldAsPtr(env, _cache.peer, mComponentPeerIDs.pData); |
| |
| if (cdata == NULL || |
| cdata->widget != w || |
| cdata->dsi == (DropSitePtr)NULL) { |
| _cache.w = NULL; |
| |
| return; |
| } |
| |
| _cache.w = w; |
| _cache.component = (*env)->NewGlobalRef(env, cdata->dsi->component); |
| _cache.peer = (*env)->NewGlobalRef(env, _cache.peer); |
| /* SECURITY: OK to call this on privileged thread - peer is secure */ |
| { |
| jobject dtcpeer = call_dTCcreate(env); |
| if (!JNU_IsNull(env, dtcpeer)) { |
| _cache.dtcpeer = (*env)->NewGlobalRef(env, dtcpeer); |
| (*env)->DeleteLocalRef(env, dtcpeer); |
| } else { |
| _cache.dtcpeer = NULL; |
| } |
| } |
| |
| _cache.transfersPending = 0; |
| cacheDropDone(True); |
| } |
| |
| if (_cache.w != (Widget)NULL) updateCachedTargets(env, dt); |
| } |
| |
| |
| /** |
| * |
| */ |
| |
| static void |
| cacheDropDone(Boolean dropDone) { |
| _cache.dropDone = dropDone; |
| } |
| |
| static Boolean |
| isDropDone() { |
| return _cache.dropDone; |
| } |
| |
| /** |
| * |
| */ |
| |
| static jint XmToDnDConstants(unsigned char operations) { |
| jint src = java_awt_dnd_DnDConstants_ACTION_NONE; |
| |
| if (operations & XmDROP_MOVE) src |= java_awt_dnd_DnDConstants_ACTION_MOVE; |
| if (operations & XmDROP_COPY) src |= java_awt_dnd_DnDConstants_ACTION_COPY; |
| if (operations & XmDROP_LINK) src |= java_awt_dnd_DnDConstants_ACTION_LINK; |
| |
| return src; |
| } |
| |
| static unsigned char selectOperation(unsigned char operations) { |
| if (operations & XmDROP_MOVE) return XmDROP_MOVE; |
| if (operations & XmDROP_COPY) return XmDROP_COPY; |
| if (operations & XmDROP_LINK) return XmDROP_LINK; |
| |
| return XmDROP_NOOP; |
| } |
| |
| /** |
| * |
| */ |
| |
| static unsigned char DnDConstantsToXm(jint actions) { |
| unsigned char ret = XmDROP_NOOP; |
| |
| if (actions & java_awt_dnd_DnDConstants_ACTION_COPY) ret |= XmDROP_COPY; |
| if (actions & java_awt_dnd_DnDConstants_ACTION_MOVE) ret |= XmDROP_MOVE; |
| if (actions & java_awt_dnd_DnDConstants_ACTION_LINK) ret |= XmDROP_LINK; |
| |
| return ret; |
| } |
| |
| /** |
| * |
| */ |
| |
| typedef struct DragExitProcStruct { |
| XtIntervalId timerId; |
| jobject dtcpeer; /* global reference */ |
| jobject component; /* global reference */ |
| jlong dragContext; /* pointer */ |
| } DragExitProcStruct; |
| |
| static DragExitProcStruct pending_drag_exit_data = |
| { (XtIntervalId)0, NULL, NULL, (jlong)0 }; |
| |
| static void drag_exit_proc(XtPointer client_data, XtIntervalId* id) { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| |
| DASSERT(!JNU_IsNull(env, pending_drag_exit_data.dtcpeer)); |
| DASSERT(!JNU_IsNull(env, pending_drag_exit_data.component)); |
| DASSERT(pending_drag_exit_data.dragContext != NULL); |
| |
| if (pending_drag_exit_data.timerId != (XtIntervalId)0) { |
| if (id == NULL) { |
| XtRemoveTimeOut(pending_drag_exit_data.timerId); |
| } |
| if (id == NULL || pending_drag_exit_data.timerId == *id) { |
| |
| /* SECURITY: OK to call this on privileged thread - |
| peer is secure */ |
| call_dTCexit(env, pending_drag_exit_data.dtcpeer, |
| pending_drag_exit_data.component, |
| pending_drag_exit_data.dragContext); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| } |
| |
| /* cleanup */ |
| (*env)->DeleteGlobalRef(env, pending_drag_exit_data.dtcpeer); |
| (*env)->DeleteGlobalRef(env, pending_drag_exit_data.component); |
| |
| memset(&pending_drag_exit_data, 0, sizeof(DragExitProcStruct)); |
| } |
| |
| static void awt_XmDragProc(Widget w, XtPointer closure, |
| XmDragProcCallbackStruct* cbstruct) |
| { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jobject component = (jobject)NULL; |
| jint src = java_awt_dnd_DnDConstants_ACTION_NONE; |
| jint usrAction = java_awt_dnd_DnDConstants_ACTION_NONE; |
| jint ret = java_awt_dnd_DnDConstants_ACTION_NONE; |
| unsigned char srcOps = XmDROP_NOOP; |
| |
| /* |
| * Fix for BugTraq ID 4395290. |
| * We should dispatch any pending java upcall right now |
| * to keep the order of upcalls. |
| */ |
| if (pending_drag_exit_data.timerId != (XtIntervalId)0) { |
| drag_exit_proc(NULL, NULL); |
| } |
| |
| /* |
| * Fix for BugTraq ID 4357905. |
| * Drop is processed asynchronously on the event dispatch thread. |
| * We reject other drop attempts to protect the SunDTCP context |
| * from being overwritten by an upcall before the drop is done. |
| */ |
| if (!isDropDone()) { |
| cbstruct->operation = XmDROP_NOOP; |
| cbstruct->dropSiteStatus = XmINVALID_DROP_SITE; |
| return; |
| } |
| |
| if (cbstruct->dragContext == NULL) { |
| cbstruct->operation = XmDROP_NOOP; |
| cbstruct->dropSiteStatus = XmINVALID_DROP_SITE; |
| return; |
| } |
| |
| (*env)->PushLocalFrame(env, 0); |
| |
| /* |
| * Fix for BugTraq ID 4285634. |
| * If some modifier keys are pressed the Motif toolkit initializes |
| * cbstruct->operations this field to the bitwise AND of the |
| * XmDragOperations resource of the XmDragContext for this drag operation |
| * and the drop action corresponding to the current modifiers state. |
| * We need to determine the drag operations supported by the drag source, so |
| * we have to get XmNdragOperations value of the XmDragSource. |
| */ |
| XtVaGetValues(cbstruct->dragContext, XmNdragOperations, &srcOps, NULL); |
| src = XmToDnDConstants(srcOps); |
| usrAction = XmToDnDConstants(selectOperation(cbstruct->operations)); |
| |
| update_cache(env, w, cbstruct->dragContext); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| flush_cache(env); |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| goto wayout; |
| } |
| |
| switch (cbstruct->reason) { |
| case XmCR_DROP_SITE_ENTER_MESSAGE: { |
| |
| /* SECURITY: OK to call this on privileged thread - |
| peer is secure */ |
| ret = call_dTCenter(env, _cache.dtcpeer, _cache.component, |
| cbstruct->x, cbstruct->y, |
| usrAction, src, |
| _cache.targets,ptr_to_jlong(cbstruct->dragContext)); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| flush_cache(env); |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| break; |
| |
| case XmCR_DROP_SITE_LEAVE_MESSAGE: { |
| |
| DASSERT(pending_drag_exit_data.timerId == (XtIntervalId)0); |
| DASSERT(JNU_IsNull(env, pending_drag_exit_data.dtcpeer)); |
| DASSERT(JNU_IsNull(env, pending_drag_exit_data.component)); |
| DASSERT(pending_drag_exit_data.dragContext == (jlong)0); |
| |
| DASSERT(!JNU_IsNull(env, _cache.dtcpeer)); |
| DASSERT(!JNU_IsNull(env, _cache.component)); |
| DASSERT(cbstruct->dragContext != NULL); |
| |
| pending_drag_exit_data.dtcpeer = |
| (*env)->NewGlobalRef(env, _cache.dtcpeer); |
| pending_drag_exit_data.component = |
| (*env)->NewGlobalRef(env, _cache.component); |
| pending_drag_exit_data.dragContext = |
| ptr_to_jlong(cbstruct->dragContext); |
| |
| /* |
| * Fix for BugTraq ID 4395290. |
| * Postpone upcall to java, so that we can abort it in case |
| * if drop immediatelly follows. |
| */ |
| if (!JNU_IsNull(env, pending_drag_exit_data.dtcpeer) && |
| !JNU_IsNull(env, pending_drag_exit_data.component)) { |
| pending_drag_exit_data.timerId = |
| XtAppAddTimeOut(awt_appContext, 0, drag_exit_proc, NULL); |
| DASSERT(pending_drag_exit_data.timerId != (XtIntervalId)0); |
| } else { |
| JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError"); |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| if (!JNU_IsNull(env, pending_drag_exit_data.dtcpeer)) { |
| (*env)->DeleteGlobalRef(env, pending_drag_exit_data.dtcpeer); |
| } |
| if (!JNU_IsNull(env, pending_drag_exit_data.component)) { |
| (*env)->DeleteGlobalRef(env, pending_drag_exit_data.component); |
| } |
| memset(&pending_drag_exit_data, 0, sizeof(DragExitProcStruct)); |
| } |
| |
| ret = java_awt_dnd_DnDConstants_ACTION_NONE; |
| |
| /* now cleanup */ |
| |
| flush_cache(env); |
| } |
| break; |
| |
| case XmCR_DROP_SITE_MOTION_MESSAGE: { |
| |
| /* SECURITY: OK to call this on privileged thread - |
| peer is secure */ |
| ret = call_dTCmotion(env, _cache.dtcpeer, _cache.component, |
| cbstruct->x, cbstruct->y, |
| usrAction, src, |
| _cache.targets, |
| ptr_to_jlong(cbstruct->dragContext)); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| flush_cache(env); |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| } |
| break; |
| |
| case XmCR_OPERATION_CHANGED: { |
| |
| /* SECURITY: OK to call this on privileged thread - |
| peer is secure */ |
| ret = call_dTCmotion(env, _cache.dtcpeer, _cache.component, |
| cbstruct->x, cbstruct->y, |
| usrAction, src, |
| _cache.targets, |
| ptr_to_jlong(cbstruct->dragContext)); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| flush_cache(env); |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| } |
| break; |
| |
| default: break; |
| } |
| |
| wayout: |
| |
| /* |
| * Fix for BugTraq ID 4285634. |
| * If some modifier keys are pressed the Motif toolkit initializes |
| * cbstruct->operations this field to the bitwise AND of the |
| * XmDragOperations resource of the XmDragContext for this drag operation |
| * and the drop action corresponding to the current modifiers state. |
| * We should allow the drop target to select a drop action independent of |
| * the current modifiers state. |
| */ |
| cbstruct->operation = DnDConstantsToXm(ret); |
| |
| if (cbstruct->reason != XmCR_DROP_SITE_LEAVE_MESSAGE) { |
| Arg arg; |
| arg.name = XmNdropSiteOperations; |
| arg.value = (XtArgVal)cbstruct->operation; |
| |
| XmDropSiteUpdate(w, &arg, 1); |
| } |
| |
| if (ret != java_awt_dnd_DnDConstants_ACTION_NONE) { |
| cbstruct->dropSiteStatus = XmVALID_DROP_SITE; |
| } else { |
| cbstruct->dropSiteStatus = XmINVALID_DROP_SITE; |
| } |
| |
| (*env)->PopLocalFrame(env, NULL); |
| } |
| |
| static void drop_failure_cleanup(JNIEnv* env, Widget dragContext) { |
| Arg arg; |
| |
| DASSERT(dragContext != NULL); |
| _cache.transfer = NULL; |
| _cache.dropAction = XmDROP_NOOP; |
| |
| arg.name = XmNtransferStatus; |
| arg.value = (XtArgVal)XmTRANSFER_FAILURE; |
| XmDropTransferStart(dragContext, &arg, 1); |
| |
| /* Flush here, since awt_XmTransferProc won't be called. */ |
| flush_cache(env); |
| } |
| |
| /** |
| * |
| */ |
| |
| static void awt_XmDropProc(Widget w, XtPointer closure, |
| XmDropProcCallbackStruct* cbstruct) |
| { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jint src = java_awt_dnd_DnDConstants_ACTION_NONE; |
| unsigned char operation = selectOperation(cbstruct->operations); |
| unsigned char srcOps = XmDROP_NOOP; |
| unsigned char dstOps = XmDROP_NOOP; |
| Arg arg; |
| Boolean sourceIsExternal = False; |
| |
| arg.name = XmNdropSiteOperations; |
| arg.value = (XtArgVal)&dstOps; |
| XmDropSiteRetrieve(w, &arg, 1); |
| arg.value = (XtArgVal)(XmDROP_COPY | XmDROP_MOVE | XmDROP_LINK); |
| XmDropSiteUpdate(w, &arg, 1); |
| |
| /* |
| * Fix for BugTraq ID 4357905. |
| * Drop is processed asynchronously on the event dispatch thread. |
| * We reject other drop attempts to protect the SunDTCP context |
| * from being overwritten by an upcall before the drop is done. |
| */ |
| if (!isDropDone()) { |
| return; |
| } |
| |
| if (cbstruct->dragContext == NULL) { |
| cbstruct->operation = XmDROP_NOOP; |
| cbstruct->dropSiteStatus = XmINVALID_DROP_SITE; |
| return; |
| } |
| |
| /* |
| * Fix for BugTraq ID 4492640. |
| * Because of the Motif bug #4528191 XmNdragOperations resource is always |
| * equal to XmDROP_MOVE | XmDROP_COPY when the drag source is external. |
| * The workaround for this bug is to assume that an external drag source |
| * supports all drop actions. |
| */ |
| XtVaGetValues(cbstruct->dragContext, |
| XmNsourceIsExternal, &sourceIsExternal, NULL); |
| |
| if (sourceIsExternal) { |
| srcOps = XmDROP_LINK | XmDROP_MOVE | XmDROP_COPY; |
| } else { |
| /* |
| * Fix for BugTraq ID 4285634. |
| * If some modifier keys are pressed the Motif toolkit initializes |
| * cbstruct->operations to the bitwise AND of the |
| * XmDragOperations resource of the XmDragContext for this drag operation |
| * and the drop action corresponding to the current modifiers state. |
| * We need to determine the drag operations supported by the drag source, so |
| * we have to get XmNdragOperations value of the XmDragSource. |
| */ |
| XtVaGetValues(cbstruct->dragContext, XmNdragOperations, &srcOps, NULL); |
| } |
| |
| src = XmToDnDConstants(srcOps); |
| |
| if ((srcOps & dstOps) == 0) { |
| cbstruct->operation = XmDROP_NOOP; |
| cbstruct->dropSiteStatus = XmINVALID_DROP_SITE; |
| drop_failure_cleanup(env, cbstruct->dragContext); |
| return; |
| } |
| |
| (*env)->PushLocalFrame(env, 0); |
| |
| update_cache(env, w, cbstruct->dragContext); |
| |
| cacheDropDone(False); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| (*env)->PopLocalFrame(env, NULL); |
| drop_failure_cleanup(env, cbstruct->dragContext); |
| return; |
| } |
| |
| /* |
| * Fix for BugTraq ID 4395290. |
| * Abort a pending upcall to dragExit. |
| */ |
| pending_drag_exit_data.timerId = (XtIntervalId)0; |
| |
| /* SECURITY: OK to call this on privileged thread - peer is secure */ |
| call_dTCdrop(env, _cache.dtcpeer, _cache.component, |
| cbstruct->x, cbstruct->y, |
| XmToDnDConstants(operation), src, _cache.targets, |
| ptr_to_jlong(cbstruct->dragContext)); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| flush_cache(env); |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| (*env)->PopLocalFrame(env, NULL); |
| } |
| |
| /** |
| * |
| */ |
| |
| static void awt_XmTransferProc(Widget w, XtPointer closure, Atom* selection, |
| Atom* type, XtPointer value, |
| unsigned long* length, int32_t* format) |
| { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| Atom req = (Atom)closure; |
| Display* dpy = XtDisplayOfObject(w); |
| jobject tName = NULL; |
| |
| /* |
| * Note: this method is only called to transfer data between clients |
| * in different JVM's or native apps. For Intra-JVM transfers the peer |
| * code shares the sources Transferable with the destination. |
| */ |
| |
| if (_cache.w == (Widget)NULL || _cache.transfer != w) { |
| if (value != NULL) { |
| XtFree(value); |
| value = NULL; |
| } |
| /* we have already cleaned up ... */ |
| return; |
| } |
| |
| (*env)->PushLocalFrame(env, 0); |
| |
| if (*type == None || *type == XT_CONVERT_FAIL) { |
| /* SECURITY: OK to call this on privileged thread - peer is secure |
| */ |
| call_dTCtxFailed(env, _cache.dtcpeer, (jlong)req); |
| } else { |
| switch (*format) { |
| case 8: |
| case 16: |
| case 32: { |
| jsize size = (*length <= INT_MAX) ? (jsize)*length : INT_MAX; |
| jbyteArray arry = (*env)->NewByteArray(env, size); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| |
| /* SECURITY: OK to call this on privileged thread - |
| peer is secure */ |
| call_dTCtxFailed(env, _cache.dtcpeer, (jlong)req); |
| |
| goto wayout; |
| } |
| |
| (*env)->SetByteArrayRegion(env, arry, 0, size, (jbyte*)value); |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| |
| /* SECURITY: OK to call this on privileged thread - |
| peer is secure */ |
| call_dTCtxFailed(env, _cache.dtcpeer, (jlong)req); |
| goto wayout; |
| } |
| |
| arry = (*env)->NewGlobalRef(env, arry); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| { |
| char* tn = XGetAtomName(dpy, *type); |
| |
| tName = (*env)->NewStringUTF(env, (const char *)tn); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| XFree((void *)tn); |
| } |
| |
| /* SECURITY: OK to call this on privileged thread - peer is |
| secure */ |
| call_dTCnewData(env, _cache.dtcpeer, (jlong)req, tName, arry); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| } |
| |
| default: |
| break; |
| } |
| } |
| |
| wayout: |
| if (value != NULL) { |
| XtFree(value); |
| value = NULL; |
| } |
| |
| _cache.transfersPending--; |
| while (_cache.transfersPending == 0 && !isDropDone()) { |
| AWT_WAIT(0); |
| } |
| |
| if (isDropDone() && _cache.flushPending) { |
| flush_cache(env); |
| } |
| |
| (*env)->PopLocalFrame(env, NULL); |
| } |
| |
| /** |
| * |
| */ |
| |
| static void awt_XmDragEnterProc(Widget w, XtPointer closure, |
| XmDropSiteEnterCallbackStruct* cbstruct) |
| { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jobject this = (jobject)closure; |
| |
| /* This should only be valid, but Im leaving this part of the old code */ |
| jboolean valid = cbstruct->dropSiteStatus == XmVALID_DROP_SITE |
| ? JNI_TRUE : JNI_FALSE; |
| |
| if (valid == JNI_TRUE) { |
| /* |
| * Workaround for Motif bug id #4457656. |
| * Pointer coordinates passed in cbstruct are incorrect. |
| * We have to make a round-trip query. |
| */ |
| Window rootWindow, childWindow; |
| int32_t xw, yw, xr, yr; |
| uint32_t modifiers; |
| |
| XQueryPointer(awt_display, XtWindow(w), |
| &rootWindow, &childWindow, &xr, &yr, &xw, &yw, &modifiers); |
| |
| (*env)->PushLocalFrame(env, 0); |
| |
| /* SECURITY: OK to call this on privileged thread - peer is secure */ |
| call_dSCenter(env, this, XmToDnDConstants(cbstruct->operation), |
| convertModifiers(modifiers), xr, yr); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| (*env)->PopLocalFrame(env, NULL); |
| } |
| } |
| |
| /** |
| * |
| */ |
| |
| static void awt_XmDragMotionProc(Widget w, XtPointer closure, |
| XmDragMotionCallbackStruct* cbstruct) |
| { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jobject this = (jobject)closure; |
| |
| /* This should only be valid, but Im leaving this part of the old code */ |
| jboolean valid = cbstruct->dropSiteStatus == XmVALID_DROP_SITE |
| ? JNI_TRUE : JNI_FALSE; |
| Window rootWindow, childWindow; |
| int32_t xw, yw, xr, yr; |
| uint32_t modifiers; |
| |
| XQueryPointer(awt_display, XtWindow(w), |
| &rootWindow, &childWindow, &xr, &yr, &xw, &yw, &modifiers); |
| /* |
| * Fix for 4285634. |
| * Use the cached modifiers state, since the directly queried state can |
| * differ from the one associated with this dnd notification. |
| */ |
| modifiers = ((XmDragContext)w)->drag.lastEventState; |
| if (xr != x_root || yr != y_root) { |
| call_dSCmouseMoved(env, this, XmToDnDConstants(cbstruct->operation), |
| convertModifiers(modifiers), xr, yr); |
| |
| if ((*env)->ExceptionCheck(env) == JNI_TRUE) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| x_root = xr; |
| y_root = yr; |
| } |
| |
| if (valid == JNI_TRUE) { |
| |
| (*env)->PushLocalFrame(env, 0); |
| |
| /* SECURITY: OK to call this on privileged thread - peer is secure */ |
| call_dSCmotion(env, this, XmToDnDConstants(cbstruct->operation), |
| convertModifiers(modifiers), xr, yr); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| (*env)->PopLocalFrame(env, NULL); |
| } else { |
| (*env)->PushLocalFrame(env, 0); |
| |
| /* SECURITY: OK to call this on privileged thread - peer is secure */ |
| call_dSCexit(env, this, xr, yr); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| (*env)->PopLocalFrame(env, NULL); |
| } |
| } |
| |
| /** |
| * |
| */ |
| |
| static void awt_XmDragLeaveProc(Widget w, XtPointer closure, |
| XmDropSiteLeaveCallbackStruct* cbstruct) |
| { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jobject this = (jobject)closure; |
| Window rootWindow, childWindow; |
| int32_t xw, yw, xr, yr; |
| uint32_t modifiers; |
| |
| XQueryPointer(XtDisplay(w), XtWindow(w), |
| &rootWindow, &childWindow, &xr, &yr, &xw, &yw, &modifiers); |
| |
| (*env)->PushLocalFrame(env, 0); |
| |
| /* SECURITY: OK to call this on privileged thread - peer is secure */ |
| call_dSCexit(env, this, xr, yr); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| (*env)->PopLocalFrame(env, NULL); |
| } |
| |
| /** |
| * |
| */ |
| |
| static void awt_XmDropOperationChangedProc(Widget w, XtPointer closure, |
| XmDropStartCallbackStruct* cbstruct) |
| { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jobject this = (jobject)closure; |
| Window rootWindow, childWindow; |
| int32_t xw, yw, xr, yr; |
| uint32_t modifiers; |
| |
| XQueryPointer(XtDisplay(w), XtWindow(w), |
| &rootWindow, &childWindow, &xr, &yr, &xw, &yw, &modifiers); |
| |
| (*env)->PushLocalFrame(env, 0); |
| |
| |
| /* SECURITY: OK to call this on privileged thread - peer is secure */ |
| call_dSCchanged(env, this, XmToDnDConstants(cbstruct->operation), |
| convertModifiers(modifiers), xr, yr); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| (*env)->PopLocalFrame(env, NULL); |
| } |
| |
| /** |
| * |
| */ |
| |
| static void awt_XmDropFinishProc(Widget w, XtPointer closure, |
| XmDropFinishCallbackStruct* cbstruct) |
| { |
| JNIEnv* env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); |
| jobject this = (jobject)closure; |
| unsigned char completionStatus = cbstruct->completionStatus; |
| jint dropAction = XmToDnDConstants(cbstruct->operation); |
| Window rootWindow, childWindow; |
| int32_t xw, yw, xr, yr; |
| uint32_t modifiers; |
| |
| XQueryPointer(XtDisplay(w), XtWindow(w), |
| &rootWindow, &childWindow, &xr, &yr, &xw, &yw, &modifiers); |
| |
| /* cleanup */ |
| |
| if (_cache.transfer == NULL) { |
| dropAction = _cache.dropAction; |
| } |
| |
| _cache.dropAction = java_awt_dnd_DnDConstants_ACTION_NONE; |
| _cache.win = None; |
| _cache.state = 0; |
| XtRemoveEventHandler(w, ButtonReleaseMask, False, |
| dragsource_track_release, NULL); |
| |
| /* SECURITY: OK to call this on privileged thread - peer is secure */ |
| call_dSCddfinished(env, this, completionStatus, dropAction, xr, yr); |
| |
| if (!JNU_IsNull(env, (*env)->ExceptionOccurred(env))) { |
| (*env)->ExceptionDescribe(env); |
| (*env)->ExceptionClear(env); |
| } |
| |
| awt_cleanupConvertDataContext(env, MOTIF_DROP_ATOM); |
| } |