/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $XConsortium: DndDrag.c /main/5 1996/09/27 19:00:40 drk $ */ /********************************************************************* * * File: DndDrag.c * * Description: Implemenation of DND Drag Initator * ********************************************************************* * *+SNOTICE * * RESTRICTED CONFIDENTIAL INFORMATION: * * The information in this document is subject to special * restrictions in a confidential disclosure agreement between * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this * document outside HP, IBM, Sun, USL, SCO, or Univel without * Sun's specific written approval. This documment and all copies * and derivative works thereof must be returned or destroyed at * Sun's request. * * Copyright 1993 Sun Microsystems, Inc. All rights reserved. * * (c) Copyright 1993, 1994 Hewlett-Packard Company * (c) Copyright 1993, 1994 International Business Machines Corp. * (c) Copyright 1993, 1994 Sun Microsystems, Inc. * (c) Copyright 1993, 1994 Novell, Inc. * *+ENOTICE */ #include #include #include #include #include #include #include #include #include #include
#include "Dnd.h" #include "DndP.h" #include "DtSvcLock.h" /* * Drag Initiator Callbacks */ static Boolean dndConvertProc(Widget, Atom*, Atom*, Atom*, XtPointer *, unsigned long*, int*); static void dndAppConvert(Widget, int, XEvent*, DtDragInfo*); static void dndDropStartCallback(Widget, XtPointer, XtPointer); static void dndDropFinishCallback(Widget, XtPointer, XtPointer); static void dndDragDropFinishCallback(Widget, XtPointer, XtPointer); static void dndTopLevelEnterCallback(Widget, XtPointer, XtPointer); static void dndTopLevelLeaveCallback(Widget, XtPointer, XtPointer); extern int _DtDndCountVarArgs(va_list vaList); extern void _DtDndArgListFromVarArgs(va_list vaList, Cardinal maxArgs, ArgList *argListReturn, Cardinal *argCountReturn); extern void _XmDragOverChange(Widget w, unsigned char dropSiteStatus); /* * Drag Initiator Resources */ typedef struct { XtCallbackList dropOnRootCallback; Widget sourceIcon; Boolean bufferIsText; } DragSettings; #define Offset(field) XtOffsetOf(DragSettings, field) static XtResource dragResources[] = { { DtNdropOnRootCallback, DtCDropOnRootCallback, XtRCallback, sizeof(XtCallbackList), Offset(dropOnRootCallback), XtRImmediate, (XtPointer)NULL}, { DtNsourceIcon, DtCSourceIcon, XtRWidget, sizeof(Widget), Offset(sourceIcon), XtRImmediate, (XtPointer)NULL }, { DtNbufferIsText, DtCBufferIsText, XtRBoolean, sizeof(Boolean), Offset(bufferIsText), XtRImmediate, (XtPointer)False }, }; #undef Offset /* * DtDndVaDragStart * * Drag Start - varargs version */ Widget DtDndVaDragStart( Widget dragInitiator, XEvent* event, DtDndProtocol protocol, Cardinal numItems, unsigned char operations, XtCallbackList dragConvertCallback, XtCallbackList dragFinishCallback, ...) { Widget dragContext; va_list vaList; ArgList argList; Cardinal argCount; _DtSvcWidgetToAppContext(dragInitiator); _DtSvcAppLock(app); va_start(vaList, dragFinishCallback); argCount = _DtDndCountVarArgs(vaList); va_end(vaList); va_start(vaList, dragFinishCallback); _DtDndArgListFromVarArgs(vaList, argCount, &argList, &argCount); va_end(vaList); dragContext = DtDndDragStart(dragInitiator, event, protocol, numItems, operations, dragConvertCallback, dragFinishCallback, argList, argCount); XtFree((char *)argList); _DtSvcAppUnlock(app); return dragContext; } /* * DtDndVaDragStart * * Drag Start - arglist version */ Widget DtDndDragStart( Widget dragInitiator, XEvent* event, DtDndProtocol protocol, Cardinal numItems, unsigned char operations, XtCallbackList dragConvertCallback, XtCallbackList dragFinishCallback, ArgList argList, Cardinal argCount) { XtCallbackRec dragDropFinishCbRec[] = { {dndDragDropFinishCallback, NULL}, {NULL, NULL} }; XtCallbackRec topLevelEnterCbRec[] = { {dndTopLevelEnterCallback, NULL}, {NULL, NULL} }; XtCallbackRec topLevelLeaveCbRec[] = { {dndTopLevelLeaveCallback, NULL}, {NULL, NULL} }; XtCallbackRec dropStartCbRec[] = { {dndDropStartCallback, NULL}, {NULL, NULL} }; XtCallbackRec dropFinishCbRec[] = { {dndDropFinishCallback, NULL}, {NULL, NULL} }; Display * display = XtDisplayOfObject(dragInitiator); Screen * screen = XtScreenOfObject(dragInitiator); Window rootWindow = RootWindowOfScreen(screen); DtDragInfo * dtDragInfo; DragSettings settings; DtDndDragSource sourceType; DtDndTransfer * transfer; Arg * args; int ii, nn, savedEventType; Atom * exportTargets; Cardinal numExportTargets; _DtSvcWidgetToAppContext(dragInitiator); _DtSvcAppLock(app); /* * Reject the drag if noop or multiple protocols specified */ switch (protocol) { case DtDND_BUFFER_TRANSFER: case DtDND_FILENAME_TRANSFER: case DtDND_TEXT_TRANSFER: break; case DtDND_NOOP_TRANSFER: default: _DtSvcAppUnlock(app); return (Widget)NULL; } /* * Parse resources into dragResources */ XtGetSubresources(dragInitiator, &settings, (String)NULL, (String)NULL, dragResources, XtNumber(dragResources), argList, argCount); /* * Initialize DragInfo */ dtDragInfo = (DtDragInfo *) XtMalloc(sizeof(DtDragInfo)); dtDragInfo->dragInitiator = dragInitiator; dtDragInfo->dragContext = NULL; dtDragInfo->protocol = protocol; dtDragInfo->numItems = numItems; dtDragInfo->operations = operations; dtDragInfo->sourceIcon = settings.sourceIcon; dtDragInfo->bufferIsText = settings.bufferIsText; dtDragInfo->dragData = NULL; dtDragInfo->inRoot = False; dtDragInfo->status = DtDND_SUCCESS; dtDragInfo->clientData = NULL; dtDragInfo->backdropWindow = DtWsmGetCurrentBackdropWindow(display, rootWindow); dtDragInfo->dragConvertCallback = _DtDndCopyCallbackList(dragConvertCallback); dtDragInfo->dragFinishCallback = _DtDndCopyCallbackList(dragFinishCallback); dtDragInfo->dropOnRootCallback = _DtDndCopyCallbackList(settings.dropOnRootCallback); dtDragInfo->dragData = (DtDndContext *)XtCalloc(1,sizeof(DtDndContext)); dtDragInfo->dragData->protocol = dtDragInfo->protocol; dtDragInfo->dragData->numItems = 0; /* * Get data transfer method * Use the transfer targets as export targets */ dtDragInfo->transfer = _DtDndCreateExportTransfer(dtDragInfo); exportTargets = dtDragInfo->transfer->targets; numExportTargets = dtDragInfo->transfer->numTargets; #ifdef DEBUG printf("DtDndDragStart: drag from widget 0x%p\n", dragInitiator); _DtDndPrintTransfers(display,dtDragInfo->transfer,1); #endif /* * Set up drag icon */ if (numItems > 1) { sourceType = DtDND_DRAG_SOURCE_MULTIPLE; } else { sourceType = dtDragInfo->transfer->methods->sourceType; } _DtDndSelectDragSource(dragInitiator, sourceType, dtDragInfo->sourceIcon); /* * Construct argument list */ #define NUM_DRAG_ARGS 30 args = (Arg *) XtMalloc(sizeof(Arg) * (NUM_DRAG_ARGS + argCount)); #undef NUM_DRAG_ARGS /* * Copy in passed arguments */ nn = 0; for (ii = 0; ii < argCount; ii++) { XtSetArg(args[nn], argList[ii].name, argList[ii].value); nn++; } /* * Set basic drag start arguments */ XtSetArg(args[nn], XmNexportTargets, exportTargets); nn++; XtSetArg(args[nn], XmNnumExportTargets, numExportTargets); nn++; XtSetArg(args[nn], XmNdragOperations, operations); nn++; XtSetArg(args[nn], XmNblendModel, XmBLEND_ALL); nn++; XtSetArg(args[nn], XmNcursorBackground, WhitePixelOfScreen(screen)); nn++; XtSetArg(args[nn], XmNcursorForeground, BlackPixelOfScreen(screen)); nn++; XtSetArg(args[nn], XmNclientData, dtDragInfo); nn++; if (dtDragInfo->sourceIcon != NULL) { XtSetArg(args[nn],XmNsourcePixmapIcon, dtDragInfo->sourceIcon); nn++; XtSetArg(args[nn],XmNsourceCursorIcon, dtDragInfo->sourceIcon); nn++; } /* * Set up DnD callbacks for Motif */ XtSetArg(args[nn], XmNconvertProc, dndConvertProc); nn++; dragDropFinishCbRec[0].closure = (XtPointer) dtDragInfo; dropFinishCbRec[0].closure = (XtPointer) dtDragInfo; dtDragInfo->dragDropFinishCallback = _DtDndCopyCallbackList(dragDropFinishCbRec); dtDragInfo->dropFinishCallback = _DtDndCopyCallbackList(dropFinishCbRec); XtSetArg(args[nn], XmNdragDropFinishCallback, dtDragInfo->dragDropFinishCallback); nn++; XtSetArg(args[nn], XmNdropFinishCallback, dtDragInfo->dropFinishCallback); nn++; /* * Only use top-level-enter/leave callbacks if also doing drop-on-root */ if (dtDragInfo->dropOnRootCallback != NULL) { topLevelEnterCbRec[0].closure = (XtPointer) dtDragInfo; topLevelLeaveCbRec[0].closure = (XtPointer) dtDragInfo; dropStartCbRec[0].closure = (XtPointer) dtDragInfo; dtDragInfo->topLevelEnterCallback = _DtDndCopyCallbackList(topLevelEnterCbRec); dtDragInfo->topLevelLeaveCallback = _DtDndCopyCallbackList(topLevelLeaveCbRec); dtDragInfo->dropStartCallback = _DtDndCopyCallbackList(dropStartCbRec); XtSetArg(args[nn], XmNtopLevelEnterCallback, dtDragInfo->topLevelEnterCallback); nn++; XtSetArg(args[nn], XmNtopLevelLeaveCallback, dtDragInfo->topLevelLeaveCallback); nn++; XtSetArg(args[nn], XmNdropStartCallback, dtDragInfo->dropStartCallback); nn++; } /* * Fake a button press. This is necessary because Motif requires * a drag to start on a button press. We need to be able to start * a drag on a mouse motion event when Bselect is held down. Since * the motion event has the fields necessary for Motif this works. */ savedEventType = event->type; if (event->type == MotionNotify) { event->type = ButtonPress; } /* * Start the drag */ dtDragInfo->dragContext = XmDragStart(dragInitiator, event, args, nn); XtFree((char *)args); event->type = savedEventType; _DtSvcAppUnlock(app); return (dtDragInfo->dragContext); } /********************************************************************* * * Drag Initiator Callbacks * *********************************************************************/ /* * dndDropStartCallback * * */ static void dndDropStartCallback( Widget dragContext, XtPointer clientData, XtPointer callData) { DtDragInfo *dtDragInfo = (DtDragInfo *) clientData; DtDndContext *dragData; XmDragContext xmDragContext = (XmDragContext)dtDragInfo->dragContext; XmDropStartCallbackStruct *xmDropInfo = (XmDropStartCallback) callData; DtDndTransferCallbackStruct dropCallData; int posOffsetX, posOffsetY; /* * If the user has cancelled the drop, or the drop isn't on the * root, or there are no dropOnRoot or convert callbacks * then reject the drop. */ if (xmDragContext->drag.dragCompletionStatus == XmDROP_CANCEL || dtDragInfo->inRoot == False || dtDragInfo->dropOnRootCallback == NULL || dtDragInfo->dragConvertCallback == NULL ) { xmDropInfo->dropSiteStatus = XmINVALID_DROP_SITE; xmDropInfo->dropAction = XmDROP_CANCEL; return; } /* * The following is to handle the dropOnRoot situation. * We handle both the convert and transfer sides of the * transaction here. First we get the application drag data * and then we call the application dropOnRoot callback. */ /* * Initialize protocol specific dragData */ dtDragInfo->dragData->numItems = dtDragInfo->numItems; (*dtDragInfo->transfer->methods->convertInit)(dtDragInfo); /* * Invoke the application convert callback */ dndAppConvert(dragContext, DtCR_DND_CONVERT_DATA, xmDropInfo->event, dtDragInfo); if (dtDragInfo->status == DtDND_FAILURE) { return; } /* * Setup dropOnRootcall data and invoke the dropOnroot callback */ _DtDndGetIconOffset(dtDragInfo->dragContext, dtDragInfo->transfer->methods->sourceType, &posOffsetX, &posOffsetY); dropCallData.reason = DtCR_DND_ROOT_TRANSFER; dropCallData.event = xmDropInfo->event; dropCallData.x = xmDropInfo->x + posOffsetX; dropCallData.y = xmDropInfo->y + posOffsetY; dropCallData.operation = xmDropInfo->operation; dropCallData.dropData = dtDragInfo->dragData; dropCallData.completeMove = False; dropCallData.status = DtDND_SUCCESS; _DtDndCallCallbackList(dragContext, dtDragInfo->dropOnRootCallback, (XtPointer)&dropCallData); /* * Tell Motif that the root is a valid drop site */ xmDropInfo->dropSiteStatus = XmVALID_DROP_SITE; xmDropInfo->dropAction = XmDROP; } /* * dndConvertProc * * */ static Boolean dndConvertProc( Widget dragContext, Atom *selection, Atom *target, Atom *returnType, XtPointer *returnValue, unsigned long *returnLength, int *returnFormat) { Atom realSelectionAtom; /* Motif hides the selection atom */ DtDragInfo *dtDragInfo = NULL; XSelectionRequestEvent *selectionRequestEvent; Boolean status; #ifdef DEBUG { Display *display = XtDisplayOfObject(dragContext); char *atomname = XGetAtomName(display,*target); printf("dndConvertProc: target = %s\n",(atomname ? atomname : "Null")); if (atomname) XFree(atomname); } #endif /* * Get the DtDragInfo */ XtVaGetValues(dragContext, XmNclientData, &dtDragInfo, NULL); if (dtDragInfo == NULL || dtDragInfo->status == DtDND_FAILURE) { return False; } /* * Get selection request event */ XtVaGetValues(dragContext, XmNiccHandle, &realSelectionAtom, NULL); selectionRequestEvent = XtGetSelectionRequest(dragContext, realSelectionAtom, NULL); /* REMIND: NULL for atomic transfer */ /* * Get the application drag data if necessary */ if (dtDragInfo->dragData->numItems == 0) { dtDragInfo->dragData->numItems = dtDragInfo->numItems; (*dtDragInfo->transfer->methods->convertInit)(dtDragInfo); dndAppConvert(dragContext, DtCR_DND_CONVERT_DATA, (XEvent *)selectionRequestEvent, dtDragInfo); if (dtDragInfo->status == DtDND_FAILURE) { return False; } } /* * Handle transfer protocol independent target conversions */ if (*target == XA_TARGETS) { /* * TARGETS Construct a list of targets consisting of those * the dnd library supports plus those supported by * the drag initiator. */ int ii, LIBRARY_TARGETS = 6; Atom * availTargets; Atom * allTargets; Cardinal numAvailTargets; Cardinal numAllTargets; (*dtDragInfo->transfer->methods->getAvailTargets)(dtDragInfo, &availTargets, &numAvailTargets); numAllTargets = numAvailTargets + LIBRARY_TARGETS; allTargets = (Atom *)XtMalloc(sizeof(Atom) * numAllTargets); for (ii = 0; ii < numAvailTargets; ii++) { allTargets[ii] = availTargets[ii]; } XtFree((char *)availTargets); ii = numAvailTargets; allTargets[ii++] = XA_TARGETS; allTargets[ii++] = XA_TIMESTAMP; allTargets[ii++] = XA_MULTIPLE; allTargets[ii++] = XA_HOST_NAME; allTargets[ii++] = XA_SUN_FILE_HOST_NAME; allTargets[ii++] = XA_DELETE; *returnType = XA_ATOM; *returnFormat = 32; *returnValue = (XtPointer)allTargets; *returnLength = numAllTargets * sizeof(Atom)/4; status = True; } else if (*target == XA_TIMESTAMP || *target == XA_MULTIPLE) { /* * TIMESTAMP and MULTIPLE are handled by the Intrinsics */ status = True; } else if (*target == XA_HOST_NAME || *target == XA_SUN_FILE_HOST_NAME) { /* * HOST_NAME, _SUN_FILE_HOST_NAME The name of this host */ *returnType = XA_STRING; *returnValue = (XtPointer)XtNewString(_DtDndGetHostName()); *returnLength = strlen((char *)*returnValue) + 1; *returnFormat = 8; status = True; } else if (*target == XA_DELETE) { /* * DELETE Set up convert callback data to specify * deletion and invoke the application-defined * convertCallback() to perform the delete. */ *returnType = XA_NULL; *returnFormat = 32; *returnValue = (XtPointer) NULL; *returnLength = 0; dndAppConvert(dragContext, DtCR_DND_CONVERT_DELETE, (XEvent *)selectionRequestEvent, dtDragInfo); status = True; } else if (*target == XA_SUN_ENUM_COUNT) { /* * _SUN_ENUMERATION_COUNT The number of items available */ int *count = XtNew(int); if (dtDragInfo->dragData->numItems == 1) { count[0] = 1; } else { count[0] = 0; dtDragInfo->status = DtDND_FAILURE; } *returnType = XA_INTEGER; *returnValue = (XtPointer)count; *returnLength = 1; *returnFormat = 32; status = True; } else { /* * Invoke protocol specific convert method */ status = (*dtDragInfo->transfer->methods->convert)( dragContext, dtDragInfo, selection, target, returnType, returnValue, returnLength, returnFormat, selectionRequestEvent); } return status; } /* * dndAppConvert * * Call the application convert callback */ static void dndAppConvert( Widget dragContext, int reason, XEvent * event, DtDragInfo * dtDragInfo) { DtDndConvertCallbackStruct convertCallData; convertCallData.reason = reason; convertCallData.event = event; convertCallData.dragData = dtDragInfo->dragData; convertCallData.status = DtDND_SUCCESS; _DtDndCallCallbackList(dragContext, dtDragInfo->dragConvertCallback, (XtPointer)&convertCallData); dtDragInfo->status = convertCallData.status; if (reason == DtCR_DND_CONVERT_DATA && dtDragInfo->dragData->numItems <= 0) { dtDragInfo->status = DtDND_FAILURE; } } /* * dndDropFinishCallback * * Handle drop-on-root case */ static void dndDropFinishCallback( Widget dragContext, XtPointer clientData, XtPointer callData) { DtDragInfo *dtDragInfo = (DtDragInfo *) clientData; XmDropFinishCallbackStruct *xmDropFinishCallData = (XmDropFinishCallbackStruct *) callData; if (dtDragInfo->dropOnRootCallback != NULL && dtDragInfo->inRoot && xmDropFinishCallData->dropSiteStatus == XmVALID_DROP_SITE) { xmDropFinishCallData->completionStatus = XmDROP_SUCCESS; XtVaSetValues(dtDragInfo->dragContext, XmNblendModel, XmBLEND_NONE, NULL); } } /* * dndDragDropFinishCallback * * Call the application dragFinishCallback */ static void dndDragDropFinishCallback( Widget dragContext, XtPointer clientData, XtPointer callData) { XmDragDropFinishCallbackStruct *xmDndFinishInfo = (XmDragDropFinishCallbackStruct *)callData; DtDragInfo *dtDragInfo = (DtDragInfo *)clientData; DtDndDragFinishCallbackStruct dragFinishCallData; /* * Invoke application dragFinishCallback */ dragFinishCallData.reason = DtCR_DND_DRAG_FINISH; dragFinishCallData.event = xmDndFinishInfo->event; dragFinishCallData.sourceIcon = dtDragInfo->sourceIcon; dragFinishCallData.dragData = dtDragInfo->dragData; _DtDndCallCallbackList(dragContext, dtDragInfo->dragFinishCallback, (XtPointer)&dragFinishCallData); /* * Restore motif default drag cursors */ _DtDndSelectDragSource(dragContext, DtDND_DRAG_SOURCE_DEFAULT, NULL); /* * Invoke protocol specific convertFinish */ (*dtDragInfo->transfer->methods->convertFinish)(dtDragInfo); /* * Free data structures allocated during the drag */ XtFree((char *)dtDragInfo->transfer->targets); XtFree((char *)dtDragInfo->transfer); XtFree((char *)dtDragInfo->dragConvertCallback); XtFree((char *)dtDragInfo->dragFinishCallback); XtFree((char *)dtDragInfo->dragDropFinishCallback); XtFree((char *)dtDragInfo->dropFinishCallback); if (dtDragInfo->dropOnRootCallback != NULL) { XtFree((char *)dtDragInfo->topLevelEnterCallback); XtFree((char *)dtDragInfo->topLevelLeaveCallback); XtFree((char *)dtDragInfo->dropStartCallback); } XtFree((char *)dtDragInfo->dropOnRootCallback); XtFree((char *)dtDragInfo->dragData); XtFree((char *)dtDragInfo); } /* * dndTopLevelEnterCallback -- Support for drop-on-root callback. * When a drop-on-root callback has been set, determines if * the drag has entered the root window (or equivalents) * and sneakily changes Motif's idea that the root is an * invalid drop site to think that it's really a valid one. * Also updates dtDragInfo.inRoot as needed. */ static void dndTopLevelEnterCallback( Widget dragContext, XtPointer clientData, XtPointer callData) { XmTopLevelEnterCallbackStruct *xmEnterInfo = (XmTopLevelEnterCallbackStruct *) callData; DtDragInfo *dtDragInfo = (DtDragInfo *) clientData; XmDragContext xmDragContext = (XmDragContext) dragContext; XmDragOverShellWidget dragOverShell = xmDragContext->drag.curDragOver; if (xmEnterInfo->window == RootWindowOfScreen(xmEnterInfo->screen) || dtDragInfo->backdropWindow == xmEnterInfo->window ) { dragOverShell->drag.cursorState = XmVALID_DROP_SITE; _XmDragOverChange((Widget)dragOverShell, dragOverShell->drag.cursorState); dtDragInfo->inRoot = True; } else { dtDragInfo->inRoot = False; } } /* * dndTopLevelLeaveCallback -- Support for drop-on-root callback. * When a drop-on-root callback has been set, determines if * the drag is exiting the root window and restores Motif's * internal state back to thinking that the root window is * an invalid drop site. We don't update dtDragInfo->inRoot * here since the top-level-leave callback is called before * the drop callback which needs to know if we're in the root * or not. */ static void dndTopLevelLeaveCallback( Widget dragContext, XtPointer clientData, XtPointer callData) { XmTopLevelLeaveCallbackStruct *xmLeaveInfo = (XmTopLevelLeaveCallbackStruct *) callData; DtDragInfo *dtDragInfo = (DtDragInfo *) clientData; XmDragContext xmDragContext = (XmDragContext) dragContext; XmDragOverShellWidget dragOverShell = xmDragContext->drag.curDragOver; if (xmLeaveInfo->window == RootWindowOfScreen(xmLeaveInfo->screen) || dtDragInfo->backdropWindow == xmLeaveInfo->window ) { dragOverShell->drag.cursorState = XmINVALID_DROP_SITE; _XmDragOverChange((Widget)dragOverShell, dragOverShell->drag.cursorState); } }