Drop site widgets and CwDropSite objects
A drop site widget is a widget that has been registered as a drop site. The user can drop onto a drop site widget by first dragging from a drag source widget, and then moving the mouse over to the drop site widget and releasing the mouse button.
Preparing to accept drops: CwDropSite
The application registers a widget as a drop site by sending it the dropSiteRegister: message of CwWidget with a CwDropSite create argBlock. Sending dropSiteRegister: creates a CwDropSite which represents the drop site during any drag and drop operation. The following drop site resources can be set in the create argBlock:
dropSiteOperations
The operations that the drop site supports
importTargets
The target types that the drop site supports, in order of drop site preference
dragProc
An optional proc that is received whenever the mouse moves over the drop site widget during a drag. The callData reason can be one of the following:
Enter
XmCRDROPSITEENTERMESSAGE
Motion
XmCRDRAGMOTIONMESSAGE
Leave
XmCRDROPSITELEAVEMESSAGE
Operation changed
XmCROPERATIONCHANGEDMESSAGE
The destination application can check the location of the mouse to determine whether a drop in that location is acceptable, and modify the operation and dropSiteStatus fields of the proc's callData accordingly. Modifications to these fields produces a corresponding change in the appearance of the drag cursor. In addition, a drag proc can be used to provide visual feedback to the user about the drop site. For example, to indicate that the cursor is over some semantic area in the widget, an application might draw a border around the area.
dropProc
A proc that is received when a mouse button is released over the drop site. The destination application must determine whether the drop operation is acceptable in that location. If the operation is valid, it must decide which target types to request and then ask the drag context to start the data transfer using those types.
The following code registers the specified widget as a drop site. This establishes the operations and targets that the drop site supports, as well as hooking a dragProc and a dropProc:
registerDropSite: aCwWidget
"Register the specified widget as a drop site."
aCwWidget
dropSiteRegister: [:dropSite |
dropSite
dropSiteOperations: XmDROPMOVE | XmDROPCOPY;
importTargets: #('STRING');
dragProc: (CwCallbackRec
receiver: self
selector: #dragProc:clientData:callData:
clientData: nil);
dropProc: (CwCallbackRec
receiver: self
selector: #dropProc:clientData:callData:
clientData: nil)].
Note:
OSF/Motif platforms provide default drop behavior in text widgets. Common Widgets drag and drop turns off this behavior in a widget when the widget is registered as a drop site.
The drag proc
The dragProc resource is optional. A destination application can supply a drag proc when it creates a drop site if it wants to perform special drag-under animation, or select the operation based on some internal state or the semantic location of the cursor in the drop site. The parameters to the dragProc are as follows:
widget
The CwWidget that was registered as a drop site
clientData
The clientData specified when the dragProc was hooked
callData
An instance of CwDragProcCallbackData
The following code shows a dragProc handler which does drag-under animation based on the coordinates of the mouse in the callData, and chooses the operation based on the mouse coordinates and the valid operations in the callData. Assume the following:
• dropRectanglesFor: answers a collection of rectangles that are acceptable drop areas in the given widget.
• operationForRectangle:widget:callData: answers the operation chosen by the destination application, given a drop rectangle in the widget, and the operation, operations and dropSiteStatus of the callData.
• animateRectangle:widget:operation: does drag-under animation for a drop rectangle in a given widget, based on the selected operation.
• removeAllAnimation: removes all drag-under animation effects from the given widget.
dragProc: widget clientData: clientData callData: callData
"Handle the drag proc for the receiver."
| point rectangle op |
"Turn off platform drag-under animation."
callData animate: false.
point := callData x @ callData y.
(self dropRectanglesFor: widget)
do: [:rect |
(rect containsPoint: point)
ifTrue: [rectangle := rect]].
rectangle isNil
ifTrue: [op := XmDROPNOOP]
ifFalse: [op := self
operationForRectangle: rectangle
widget: widget
callData: callData].
op = XmDROPNOOP
ifTrue: [
"The point is not in a drop rectangle for the widget,
or the operation is not valid for the drop rectangle."
callData
operation: XmDROPNOOP;
dropSiteStatus: XmDROPSITEINVALID.
self removeAllAnimation: widget]
ifFalse: [
"The point is in one of the drop rectangles for the widget,
and the operation is valid."
callData
operation: op;
dropSiteStatus: XmDROPSITEVALID.
self animateRectangle: rectangle widget: widget operation: op].
callData reason = XmCRDROPSITELEAVEMESSAGE
ifTrue: [self removeAllAnimation: widget].
The drop proc
The destination application must supply a drop proc when it creates a drop site. In the drop proc, the destination determines if the drop is valid, and starts the drop data transfer. The parameters to the dropProc are as follows:
widget
The CwWidget that was registered as a drop site
clientData
The clientData specified when the dropProc was hooked
callData
An instance of CwDropProcCallbackData
Note:
Some platforms allow the user to press the help key (usually F1) during a drag. In this case, a drop proc is sent with the dropAction field in the callData set to XmDROPHELP.
Two different dropProc handlers are shown in the following code. Both examples call startTransfer:targetTypes:clientData:. The code for this method is shown in the following section on the CwDropTransfer object.
The first example shows a very simple dropProc handler. This drop site only supports the 'STRING' target type. If the dropProc is called, the drop site knows that the operation is valid and the drag source also supports 'STRING,' therefore it can simply ask for the data in 'STRING' format.
dropProc: widget clientData: clientData callData: callData
"Handle the drop proc for the receiver."
callData dropAction = XmDROPHELP
ifTrue: [
"Help is not supported, therefore this is an invalid operation."
^callData dropSiteStatus: XmDROPSITEINVALID].
"A valid drop has occurred. Start a transfer, requesting our only target type."
self startTransfer: callData targetTypes: #('STRING') clientData: widget.
The second example dropProc is more complex. This drop site supports more than one target type. In addition, the drop site cares about the semantic location of the mouse, as did the example dragProc in the previous subsection, and it may decide that the operation is invalid. Assume that intersectionOf: answers an OrderedCollection containing the intersection of two collections, and that dropRectanglesFor: and operationForRectangle:widget:callData: are the same methods used in the dragProc example.
dropProc: widget clientData: clientData callData: callData
"Handle the drop proc for the receiver."
| point rectangle op exportTargets importTargets intersection |
callData dropAction = XmDROPHELP
ifTrue: [
"Help is not supported, therefore this is an invalid operation."
^callData dropSiteStatus: XmDROPSITEINVALID].
point := callData x @ callData y.
(self dropRectanglesFor: widget)
do: [:rect |
(rect containsPoint: point)
ifTrue: [rectangle := rect]].
op := rectangle isNil
ifTrue: [XmDROPNOOP]
ifFalse: [self operationForRectangle: rectangle
widget: widget callData: callData].
op = XmDROPNOOP
ifTrue: [
"The point is not in a drop rectangle for the widget,
or the operation is not valid for the drop rectangle."
callData
operation: XmDROPNOOP;
dropSiteStatus: XmDROPSITEINVALID]
ifFalse: [
"The point is in one of the drop rectangles for the widget,
and the operation is valid."
callData operation: op].
callData dropSiteStatus = XmDROPSITEVALID
ifTrue: [
"Valid drop. Start a transfer, requesting all possible target types."
exportTargets := callData dragContext exportTargets.
importTargets := widget dropSite importTargets.
intersection := self intersectionOf: exportTargets and: importTargets.
self startTransfer: callData targetTypes: intersection clientData: widget].