Chapter 15. Drag and Drop

Drag and drop allows the user to "pick up" objects on the screen, "drag" them around the display, and "drop" them at a new location, possibly in another application.

With drag and drop the user can

This chapter first provides an overview of the drag and drop process and concepts from both the user's and the application developer's perspectives, then explains the actions of both initiator and receiver clients during the drag and at the drop, giving code samples.

User Overview of Drag and Drop

This section describes what the user does and sees during a drag and drop transaction.

Overview of User Interaction

A drag and drop transaction consists of the following actions:

  1. A user presses and holds BTransfer, usually mouse button 2, over a source object starting a drag transaction. The application owning that object is the initiator of the drag. The current pointer is replaced by a drag icon—a picture representing the item being dragged.

  2. The user moves the pointer. From now until a drop occurs, the drag icon replaces the mouse pointer.

  3. The user drops the object, usually by releasing the mouse button.

    Locations on the screen that can accept drops are drop sites, and the application owning that drop site is the destination or receiver.

    The drag icon can be dropped anywhere on the screen. However, only certain widgets have registered themselves as drop sites and are able to process the drop.

    The receiver application usually performs some action on the information represented by the dragged icon. The initiator application may also perform some action based on the results of a drag transaction.

A drop can be between applications or within the same application. An application can be both source and destination of a drop, source only, destination only, or not participate in drag and drop at all.

The user can request help about a drop site, if available, by dragging to the drop site, and pressing KHelp (usually F1).

The user can cancel the drag at any time by pressing KCancel, usually Escape.

Overview of Drag-Over Effects

The drag icon consists of three parts:

  • The source icon is a picture representing the type of the source object, such as text.

  • The state icon can be used to show whether or not the object being dragged can be dropped at its current location on the screen.

  • The operation icon can be used to show what action should happen when the drop takes place.

In the following illustration, the running figure is the source icon, the arrow in the upper left is the state icon, and the rectangles with the corner folded over indicate a Copy is desired.

Figure 15-1. A Drag Icon

Figure 15-1 
A Drag Icon

These parts can be combined (blended) and attached to each other in different ways. The default blending and attachment are shown in the previous illustration.

Parts of the drag icon may change shape or color as it is being dragged through potential drop sites, providing visual feedback about possible drop sites to the user. These changes are drag-over effects.

Applications can use the default drag icon effects, or provide more sophisticated or custom drag icons. The application or user can customize these drag-over effects in resource files.

Drag States

During a drag, there are three states that describe the relationship of a drag icon to what is under it at the time:

Valid drop site 


The drag icon is over a drop site on which it can potentially be dropped (this is only a hint; when the drop is actually attempted, further processing may show that the drop cannot actually be done).

Invalid drop site  


The drag icon is over a drop site, but it cannot be dropped there.

No drop site 


The drag icon is not over a registered drop site.

The default state icon for all three states is the same: an arrow in the upper left corner of the drag icon. Because the icon is the same for all three states, it appears not to change during the drag. The application or the user can provide custom state icons or colors in a resource file.

Drag Operations

The user specifies what action is to take place when the drop occurs by pressing certain keys when the drag starts or while the drag is in process:

Shift only 

Force a move from the initiator to the receiver client (Move)

Ctrl only 

Force a copy from the initiator to the receiver client (Copy)

Shift and Ctrl 


Force a link between the initiator and receiver clients (Link)

The operation chosen by the user must be valid for both the drag source and the drop site, or the drop site will be considered invalid.

If the user does not specify an operation, one is chosen by the toolkit. It choses an operation that is valid for both the drag source and drop site. Move is the first choice, Copy is the second, and Link is the third. If the system cannot find a valid operation, the drop site is considered invalid.

The operation icon reflects the operation chosen by the user or by the system. If the operation is changed by the user during the drag, the operation icon changes also.

The operation icon may change as the drag icon moves to different drop sites if the drop sites accept different operations.

Overview of Drag-Under Effects

A widget registered as a drop site may change visually as a drag icon passes over it. These visual cues are drag-under effects. The sensitive area of the widget is the part that responds to drag and drop. By default it is the whole widget, but applications can specify that only parts of the widget respond to drag and drop.

Various highlighting styles are possible:

  • A border around the sensitive area of the drop site widget. This is the default value.

  • The sensitive area of the drop site widget looks pushed out.

  • The sensitive area of the drop site widget looks pushed in.

  • A special pixmap is displayed within the sensitive area of the drop site widget, overwriting what is normally there.

  • No drag-under effects are used for the drop site widget.

Applications can use the default drag-under visual effects, or create more sophisticated or custom effects, such as special animation or sound effects.

Overview of Drop Effects

Visual effects also take place during the drop:

  • The drag icon appears to sit over the drop site while the processing for the drop is finishing, but the standard cursor is restored and can be used normally.

  • The source icon appears to melt into the drop site if the drop is successful.

  • The source icon appears to snap back to the source if the drop is unsuccessful.

  • A dialog window containing information about a drop site should appear if the user has requested help and the receiver client provides help, otherwise nothing happens.

  • The source icon appears to snap back to the source and the previous X cursor returns if Cancel is requested. All drag-under and drag-over effects are removed.

These drop effects cannot be changed by the application or the user.

Technical Overview of Drag and Drop

This section explains some drag and drop concepts, and provides a general view of the initiator and receiver duties during the drag and at the drop.

The Motif toolkit for drag and drop consists of

  • Widgets and widget classes that provide resources containing details about the source and destination of the drag

  • Functions that applications use to manage the widgets and widget classes

  • Protocols that specify how interactions between source and destination clients are to take place

  • Functions that manage messages, call callbacks, decide on the valid operations for a potential drop, and keep the drop site status updated

If the initiator and receiver are in the same client, they share the same toolkit. If the initiator and receiver are different clients, each client has a version of the toolkit.

An application can allow any widget to be a drag source or initiator by specifying a translation for BTransfer Press in that widget. The corresponding action creates a DragContext which starts the drag and drop transaction. The toolkit on the initiator side in charge during the drag and manages all drag messages and callbacks.

An application can register any widget as a drop site. The drop site widget may change visually as a drag icon moves in and out of it, providing drag-under visual clues to the status of the drag. The application controlling the current drop site is known as the receiver. The toolkit on the receiver side is in charge of the drop operation, and manages all drop messages and callbacks.

Each drag source and drop site specifies the types of data it is prepared to handle and what operations it can perform on that data.

The state of the drag indicates whether the drag icon is over a valid drop site, an invalid drop site, or no drop site. For a drop site to be valid, there must be at least one target type and one operation in common between the drag source and drop site.

Complexity of Drag and Drop Programs

Applications can use drag and drop functionality on any of several levels:

  • Text, List, Label, and Button widgets are already defined as drag sources. Text and TextField widgets are registered as drop sites. Therefore, at the simplest, an application can compile with the Motif libraries, and have those widgets participate in drag and drop. For example, text could be selected from one application and moved into a text area in another application.

  • On a slightly more advanced level, applications can let the toolkit do most of the work, but provide some customization. For example, an application could register a pushbutton as a drop site, but still use default visual effects. In this case, the application would register a widget as a DropSite and provide code to handle drop and transfer duties. The example programs DNDlabel.c in "A Simple Drag Receiver" and DNDscroll.c in "A Simple Drag Source" are at this level.

  • A complex application can take much of the control of the drag and drop itself. It can provide custom visuals for both drag icon and drop site. It can manage overlapping drop sites and can include complex transfers of information. The example program DNDdemo in Appendix B, "Drag and Drop Example Program," contains extensive customization.

A Simple Drag Receiver

This sample program displays a Label widget and registers it as a drop site. It accepts compound text, and supports only the Copy operation (that is, it does not support Move or Link).

When a valid drop is made on the Label widget, its HandleDrop routine changes the Label widget's label to compound text passed from the initiator.

The appropriate include files, the DropTransferCallback routine, the HandleDrop routine, and a few lines in the main routine to register the drop site are all that is needed to customize a Label widget to accept a drop and change its label in response. The details of this additional code are covered in later sections of this chapter.

Figure 15-2. A Label Widget Receiver Before and After Drag

Figure 15-2 
A Label Widget Receiver Before and After Drag

/*      file: DNDlabel.c     */
#include <signal.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <Xm/Xm.h>
#include <Xm/BulletinB.h>
#include <Xm/AtomMgr.h>
#include <Xm/Label.h>
#include <Xm/DragDrop.h>
#include <X11/Xatom.h>
#define MAX_ARGS        10
/* global variables */
Widget   myDC;
Atom     COMPOUND_TEXT;
/* This routine transfers information from the initiator */
static void TransferProc(w, closure, seltype, type, value,
                           length, format) 
Widget           w;
XtPointer        closure;
Atom             *seltype;
Atom             *type;
XtPointer        value;
unsigned long    *length;
int              format;
{
   int         n;
   Arg         args[MAX_ARGS];
   /* information from the drag initiator is passed in
    * compound compound text format.  Convert it to compound
    * string and replace the  Label label. */
   if (*type = COMPOUND_TEXT) {
      n = 0;
      XtSetArg(args[n], XmNlabelString,
                 XmCvtCTToXmString(value)); 
      n++;
      XtSetValues(closure, args, n);
      } 
}
/* This routine is performed when a drop is made.  It decides
what information it wants and uses TransferProc to transfer
the data from the initiator */
static void HandleDrop(w, client_data, call_data)
Widget          w;
XtPointer       client_data, call_data;
{
   XmDropProcCallback      DropData;
   XmDropTransferEntryRec  transferEntries[2];
   XmDropTransferEntry     transferList;
   Arg                     args[MAX_ARGS];
   int                     n;
   DropData = (XmDropProcCallback)call_data;
   /* set the transfer resources */
   n = 0; 
   /* if the action is not Drop or the operation is not Copy,
    * cancel the drop */
   if ((DropData->dropAction != XmDROP) || 
       (DropData->operation != XmDROP_COPY)) 
      XtSetArg(args[n], XmNtransferStatus, XmTRANSFER_FAILURE);
        n++;
   else {
      /* the drop can continue.  Establish the transfer list
       * and start the transfer */
      transferEntries[0].target = COMPOUND_TEXT;
      transferEntries[0].client_data = (XtPointer)w;
      transferList = transferEntries;
      XtSetArg(args[n], XmNdropTransfers, transferList); n++;
      XtSetArg(args[n], XmNnumDropTransfers, 1); n++;
      XtSetArg(args[n], XmNtransferProc, TransferProc); n++;
      }
   /* start the transfer or cancel */ 
   XmDropTransferStart(DropData->dragContext, args, n);
}
/* This program creates a Label widget, which is 
 * registered as a drop site.  The label changes when compound 
 * text is dropped on it.  */
void main (argc, argv)
unsigned int argc;
char **argv;
{
   Arg                     args[MAX_ARGS];
   int                     n;
   Widget                  topLevel, BulletinB, Label;
   XtAppContext            app_context;
   Atom                    importList[1];
   /* make supporting widget structure for the Label widget */
   topLevel = XtAppInitialize(&app_context, "XMTest", NULL, 0, 
                              &argc, argv, NULL, NULL, 0);
   n = 0;
   BulletinB = XmCreateBulletinBoard(topLevel, "BulletinB", 
                                     args, n);
   XtManageChild(BulletinB);
   COMPOUND_TEXT = XmInternAtom(XtDisplay(topLevel), 
                                "COMPOUND_TEXT", False);
   /* create a Label widget */
   n = 0;
   Label = XmCreateLabel(BulletinB, "title", args, n);
   XtManageChild(Label);
   /* register the label as a drop site */
   importList[0] = COMPOUND_TEXT;
   n = 0;
   XtSetArg(args[n], XmNimportTargets, importList); n++;
   XtSetArg(args[n], XmNnumImportTargets, 1); n++;
   XtSetArg(args[n], XmNdropSiteOperations, XmDROP_COPY); n++;
   XtSetArg(args[n], XmNdropProc, HandleDrop); n++;
   XmDropSiteRegister(Label, args, n);
   XtRealizeWidget(topLevel);
   XtAppMainLoop(app_context);
}

A Simple Drag Source

This program creates a ScrollBar widget which is to be used as a drag source. The normal action for Button 2 Press has been overridden to cause it to call the StartDrag routine, which causes the drag to begin. The program allows only the Copy operation, and will reply to requests for compound text.

When a drag is started on the ScrollBar, the default drag icons are used.

When a transfer request is received by the DragConvertProc routine, it returns the value of the scrollbar slider converted into compound text.

The code necessary to make a normal ScrollBar widget into a source for drag and drop is the appropriate include files, the DragConvertProc routine, the StartDrag routine, and translation and action commands. The details of this additional code are covered in later sections of this chapter.

Figure 15-3. A ScrollBar Widget as Drag Source

Figure 15-3 
A ScrollBar Widget as Drag Source

/*      file: DNDscroll.c        */
#include <signal.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <Xm/Xm.h>
#include <Xm/BulletinB.h>
#include <Xm/ScrollBar.h>
#include <Xm/AtomMgr.h>
#include <Xm/DragDrop.h>
#include <X11/Xatom.h>
#define MAX_ARGS        10
/* global variables */
Widget                  scrollbar;
Atom                    COMPOUND_TEXT;
/* this routine returns the value of the scrollbar slider, 
 * converted into compound text. */
static
Boolean DragConvertProc(w, selection, target, typeRtn, 
                        valueRtn, lengthRtn, formatRtn, 
                        max_lengthRtn, client_data, 
                        request_id)
Widget              w;
Atom                *selection;
Atom                *target;
Atom                *typeRtn;
XtPointer           *valueRtn;
unsigned long       *lengthRtn;
int                 *formatRtn;
unsigned long       *max_lengthRtn;
XtPointer           client_data;
XtRequestId         *request_id;
{
   Widget      dc;
   XmString    cstring;
   static char tmpstring[100];
   int         *value;
   int         n;
   Arg         args[MAX_ARGS];
   char          *ctext;
   char        *passtext;
   /* this routine processes only compound text */
   if (*target != COMPOUND_TEXT)
      return(False);
   /* get the value of the scrollbar slider */
   n = 0;
   XtSetArg(args[n], XmNvalue, &value); n++;
   XtGetValues(scrollbar, args, n);
   /* convert the slider value to compound text */
   sprintf(tmpstring, "%d", value);
   cstring = XmStringCreateLocalized(tmpstring);
   ctext = XmCvtXmStringToCT(cstring);
   passtext = XtMalloc(strlen(ctext)+1);
   memcpy(passtext, ctext, strlen(ctext)+1);
   /* format the value for transfer.  convert the value from
   * compound string to compound text for the transfer */
   *typeRtn = COMPOUND_TEXT;
   *valueRtn = (XtPointer) passtext;
   *lengthRtn = strlen(passtext);
   *formatRtn = 8;
   return(True);
}
/* This routine is performed by the initiator when a drag
 * starts  (in this case, when mouse button 2 was pressed).
 * It starts  the drag processing, and establishes a drag
 * context. */
static void StartDrag(w, event)
Widget  w;
XEvent  *event;
{
   Arg             args[MAX_ARGS];
   Cardinal        n;
   Atom            exportList[1];
   /* establish the list of valid target types */
   exportList[0] = COMPOUND_TEXT;
   n = 0; 
   XtSetArg(args[n], XmNexportTargets, exportList); n++;
   XtSetArg(args[n], XmNnumExportTargets, 1); n++;
   XtSetArg(args[n], XmNdragOperations, XmDROP_COPY); n++;
   XtSetArg(args[n], XmNconvertProc, DragConvertProc); n++;
   XmDragStart(w, event, args, n);
}
/* translations and actions.  Pressing mouse button 2
 * overrides  the normal scrollbar action and calls StartDrag
 * to start a drag transaction */
static char dragTranslations[] = 
     "#override <Btn2Down>: StartDrag()";
static XtActionsRec dragActions[] = 
     { {"StartDrag", (XtActionProc)StartDrag} };
/* This routine creates a window with a scrollbar in it. */
void main (argc, argv)
unsigned int argc;
char **argv;
{
   Arg                     args[MAX_ARGS];
   int                     n;
   Widget                  topLevel, BulletinB;
   XtAppContext            app_context;
   Atom                    importList[1];
   XtTranslations          parsed_xlations;
   /* create widget structure for scrollbar widget */
   topLevel = XtAppInitialize(&app_context, "DNDscroll", NULL,
                              0, &argc, argv, NULL, NULL, 0);
   COMPOUND_TEXT = XmInternAtom(XtDisplay(topLevel), 
                                "COMPOUND_TEXT", False);
   n = 0;
   BulletinB = XmCreateBulletinBoard(topLevel, "BBoard",
                                       args, n);
   XtManageChild(BulletinB);
   /* override button two press to start a drag */
   parsed_xlations = XtParseTranslationTable(dragTranslations); 
   XtAppAddActions(app_context, dragActions,
                     XtNumber(dragActions));
   /* create a scroll bar widget */ 
   n = 0;
   XtSetArg(args[n], XmNtranslations, parsed_xlations); n++;
   XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++;
   XtSetArg(args[n], XmNwidth, 150); n++;
   scrollbar = XmCreateScrollBar(BulletinB, "testscroll",
                                   args, n);   
   XtManageChild(scrollbar);
   XtRealizeWidget(topLevel);
   XtAppMainLoop(app_context);
}

Drag Sources and Drop Sites

Text, List, Label, and Button widgets are automatically drag sources. Applications need do nothing further to use them. An application can allow any widget to be a drag source by establishing a callback when BTransfer is pressed within that widget. The application that owns the widget in which the drop was started is the initiator.

A drag icon, which is a pictorial representation of the data being dragged, replaces the normal cursor while the drag is in effect. The icon may change as it moves around the screen. The actual data is not being dragged, only a representation of it. The data is transferred to a new location only when the drop is made, through the drop transfer protocol.

Any widget can be registered as a drop site. Text and TextField widgets are automatically registered as drop sites. If an application wants to use these as drop sites, it does not need to register them separately.

The DropSite registry contains information about widgets that have been registered as drop sites. Although the drag icon can be dropped anywhere on the screen, only widgets that have been registered as drop sites can accept information from the initiator. The receiver is the application controlling the current drop site.

The "sensitive area" is the part of the widget that responds to drag and drop. By default, the sensitive area is the whole widget. However, the application can specify that only part of the widget is sensitive.

Widgets that are drop sites can be stacked on each other, with one widget partially or completely within the boundary of another. The sensitive areas of lower drop sites are clipped if they are covered by a higher widget.

The stacking order of the widgets with drop sites can be changed by the application.

Protocols

The protocol describes how the initiator and receiver clients interact through the toolkit with each other.

Drag Protocols

There are two types of drag protocol:

Preregister 

Does not require messaging

Dynamic 

Requires messaging

Applications can support either, both, or neither. If possible, clients should support both to allow the most flexibility for users. The Motif toolkit automatically supports both unless a user or client sets resources to force the use of one or the other.

The user can specify which drag protocol to use when the client is the initiator or receiver. The application can specify drag protocol in an application-class defaults file. If neither the application nor the user specifies a protocol, the preregister drag protocol is used.

The toolkit uses the requested protocols and the protocols allowed by the initiator and receiver clients to arrive at the protocol actually being used. Therefore, the protocol can change as the drag icon moves from window to window, depending on which protocols each window supports. If the initiator and receiver cannot agree on a protocol, no drag-over or drag-under visual effects are shown.

Even if no drag-over or drag-under visual effects are shown, a drop can still occur with the drop protocol, unless a client has specified that that window does not participate in drag and drop.

Drop Protocol

The drop protocol is based on the Xt Selection transfer protocol. The transfer between either client and the toolkit can be incremental or nonincremental, regardless of how the other client is transferring. Each client has a procedure to process transfers: XmNconvertProc for the initiator, and XmNtransferProc for the receiver. Incremental transfer is indicated by a resource value.

The receiver creates a list of information and target types desired from the initiator, along with an XmNtransferProc to handle any processing needed during the transfer. It then calls XmDropTransferStart to start the transfer process. Even if there is no transfer, the receiver should call this routine, so that the status can be updated correctly for the initiator.

For each item in the transfer list, the initiator's XmNconvertProc is called. This procedure reads and processes the request and returns the information.

When the transfer has finished, the toolkit on the receiver side updates the XmNtransferStatus DropTransfer resource to indicate if the transfer was successful. The receiver's XmNtransferProc routine can also update this resource.

Drag and Drop Widget Classes

Motif provides a number of Xt objects and widgets to encapsulate the underlying protocol; however, these are not mapped onto the screen:

XmDisplay 

An object that contains display-specific information, such as the initiator and receiver protocol styles.

XmScreen 

An object that describes screen-specific information, such as font and default drag-over icons.

XmDragIcon 

A widget that describes the pixmap, mask, and attachment of an icon. The source icon, state icon, operation icon, and the resulting blended drag icon are all Drag Icons.

XmDragContext  


A widget that describes the resources specified by each drag initiator, such as target type, custom icons, custom colors, blending model, permitted operations, and callback routines for various situations encountered during the drag and drop transaction.

XmDropSite 

A drop site database that maintains a registry of the resources unique to each drop site, such as animation for drag-under effects, valid target types and operations, and callback routines for situations encountered during a drag and drop transaction. It is not an Xt object, although it acts like one with respect to resource fetching.

XmDropTransfer 


A widget that describes the information desired from the initiator client and the procedure used to process the results.

Drag and Drop Functions

Motif provides the following functions to support drag and drop processing:

XmCreateDragIcon  


Creates any of the parts of a drag icon (status icon, operation icon, or source icon) from a cursor or pixmap. This allows custom icons for all or part of the drag icon, rather than the default icons.

XmDragCancel 


Cancels a drag that is in progress. This function is called when the user presses KCancel.

XmDragStart 


This function is called in the routine that is performed when the user starts a drag. Resources describing the initiator are established. This function creates a DragContext object, which is referenced by other functions whenever information about the drag initiator is needed.

XmDropSiteConfigureStackingOrder  


Sets the order of overlapping drop sites. The default order is with the first-registered drop site on the bottom and the last-declared drop site on top.

XmDropSiteEndUpdate  


Causes the XmDropSiteUpdate requests made after XmDropSiteStartUpdate to take place.

XmDropSiteQueryStackingOrder  


Provides information about the stacking order of overlapping drop sites. The order can be changed with XmDropSiteConfigureStackingOrder.

XmDropSiteRegister  


Registers a drop site. Resources describing the drop site are defined.

XmDropSiteRetrieve  


Retrieves the values of drop site resources.

XmDropSiteStartUpdate  


Signals the toolkit to wait until XmDropSiteEndUpdate is called to process drop site changes requested by XmDropSiteUpdate. This provides a more efficient way to update several drop sites than changing them one at a time.

XmDropSiteUpdate  


Updates drop site resources for a single drop site. If a series of XmDropSiteUpdate requests are surrounded by XmDropSiteStartUpdate and XmDropSiteEndUpdate, then the changes will be made all at once after the end update request.

XmDropSiteUnregister  


Removes a drop site. After a drop site has been unregistered, it is unavailable as a destination for a drag.

XmDropTransferAdd  


Adds additional transfer requests once a transfer has started.

XmDropTransferStart  


Specifies what information should be requested from the drag initiator, and starts the process to get the information.

XmGetDragContext  


Returns the DragContext ID associated with a particular time stamp.

XmGetXmDisplay  


Returns the ID for the specified display.

XmGetXmScreen  


Returns the ID for a specified screen. Some resources, such as the drag icons, are screen-specific.

XmTargetsAreCompatible  


Checks if there are any matching targets between the initiator and destination to help determine the correct drag state.

Targets

Each drag source and drop site specifies what kinds of data types it can process, called targets. These targets are atoms, such as XA_STRING.

The DragContext resources XmNexportTargets and XmNnumExportTargets provide a list and number of the data types provided by the drag source. These are export targets.

The DropSite resources XmNimportTargets and XmNnumImportTargets provide a list and number of the data types accepted by the drop site. These are known as import targets.

Any number of targets may be listed for each source and site. A drop site is considered valid for a particular drag if at least one of its targets matches any of the source's targets and if the source and drop site operations are compatible.

An application can define anything it wants as a target. Be aware, however, that other applications might not recognize that target.

Operations

There are three ways that the initiator and receiver can interact with each other:

  • Data can be moved from the initiator to the receiver (Move).

  • Data can be copied from the initiator to the receiver (Copy).

  • Data can be linked from the receiver to the initiator (Link).

When a drag is started, the initiator provides a list of valid operations in the DragContext XmNdragOperations resource. When a drop site is registered, the receiver provides a list of operations it supports in the DropSite XmNdropSiteOperations resource. These lists are the values XmDROP_MOVE, XmDROP_COPY, or XmDROP_LINK, connected by the bitwise OR operator (|). For example, the following value means that Move and Copy are valid operations, but Link is not:

XmDROP_MOVE | XmDROP_COPY

The value XmDROP_NOOP indicates that there are no operations possible for a drop at the current site.

Callback structures for both DragContext and DropSite have operation and operations fields. The operations field lists all valid operations if a drop were to occur at this point. The operation field shows the operations that would happen if a drop occurred at this point. As the drag icon moves over different potential drop sites, the values in its callback structures change in response to what operations the drop sites allow. If there are no common operations between a drag source and a drop site, the operation and operations fields are set to XmDROP_NOOP, and the dropSiteStatus field is set to XmDROP_SITE_INVALID.

The user can specify an operation using key combinations discussed earlier in this chapter. The user can also change the operation at any time until the drop starts.

The initiator and the receiver need to be able to handle all the operations their application supports. If the operation is Move, the receiver first gets a copy of the data, then tells the initiator that it can delete the data. If the operation is Copy, both applications have the data, making two copies of it. If the operation is Link, there is only one copy of the data, and the receiver establishes a link to that copy.

Drop Site Status

The drag and drop callbacks for both receiver and initiator contain a dropSiteStatus field. This field is initialized and maintained by the receiver through the toolkit, although the receiver's drag and drop procedures can update it if they wish. This field is used by the toolkit to determine what drag-over and drag-under visual effects to use.

The dropSiteStatus field indicates the relationship of the drag source to the drop site over which the drag icon is located:

XmDROP_SITE_VALID  


A drop can take place. There is at least one matching target and operation between the drag source and the drop site.

XmDROP_SITE_INVALID  


A drop cannot take place. Either there were no matching targets, no matching operations, or the receiver's XmNdragProc or XmNdropProc discovered some other problem that would make a drop impossible.

XmNO_DROP_SITE  


The drag icon is not over a drop site.

If the toolkit on the receiver's side has set either the operation or the operations field to XmDROP_NOOP, it also sets the dropSiteStatus field to XmDROP_SITE_INVALID.

Overview of Programmer Responsibilities

This section provides an overview of the actions of the initiator client and the receiver client while a drag and drop transaction is in progress. The actions are covered in more detail later in the chapter.

Before a drag starts

  • The user or client uses the protocol resources to indicate the type of protocol and visual effects to be used for the initiator and receiver if possible.

  • The initiator client creates any special icons it wants to use for drag-over effects, using XmCreateDragIcon.

    The initiator establishes translation or event handlers to react to BTransfer Press.

  • The receiver client registers widgets as potential drop sites using XmDropSiteRegister, providing information about

    • The shape of the area of the widget sensitive to drag and drop, if it is not the whole widget

    • Valid targets

    • Optional drag-under visual effects

    • An optional XmNdragProc to receive messages during the drag

    • An XmNdropProc to be performed at the drop

    The receiver can check and change the stacking order of overlapping drop sites with XmDropSiteQueryStackingOrder and XmDropSiteConfigureStackingOrder.

    The receiver can update drop site information using the functions XmDropSiteUpdate, XmDropSiteStartUpdate, and XmDropSiteEndUpdate.

    The receiver can unregister a drop site with XmDropSiteUnregister.

When the drag starts (typically a BTransfer Press event)

  • The toolkit on the initiator's side is in charge during the drag until a drop is made. The initiator client

    • Receives an indication that the user has started a drag

    • Creates a DragContext using XmDragStart, specifying:

      - Valid targets

      - Optional callbacks to be performed during the drag

      - An XmNconvertProc to process transfer requests from the receiver

      - Optional custom drag-over visuals

      - Optional drop callbacks to be performed when a drop occurs

  • The receiver client does nothing.

During a drag

  • The user can cancel the drag or change operation.

  • The receiver is not involved unless the pointer is within one of its registered drop sites.

    The toolkit on the receiver's side initializes the dropSiteStatus, operation, and operations fields in the callback structure .

    The receiver's XmNdragProc routine (if one was registered) is notified of drag source actions within the drop site: drop site enter, drop site leave, drag icon motion, or change of operation. This XmNdragProc routine is called only if the drag protocol is dynamic. It handles any special processing and drag-under visuals.

    If the protocol is preregister, drag-under visuals are handled by the toolkit on the initiator side.

  • By default, the initiator needs to do nothing during a drag.

    If the initiator client has registered the appropriate callback routines, it is notified after the receiver's XmNdragProc when the drag is entering or leaving a top-level window, entering or leaving a drop site, is in motion, or the user has changed the desired operation. The values of dropSiteStatus, operation, and operations in the drag callbacks are initialized by the toolkit on the receiver side, by the XmNdragProc, or by the toolkit on the initiator side if the pointer is not over a registered drop site.

    The initiator can activate custom drag-over effects or other special processing.

    The initiator can cancel the drag in progress by using XmDragCancel.

  • Either client can check the compatibility of export and import targets with XmTargetsAreCompatible.

    Either client can obtain information about the drop site that the drag icon is over (if any) with XmDropSiteRetrieve.

When the drop occurs

  • The toolkit on the receiver side is in charge during the drop and transfer.

    The receiver's XmNdropProc routine makes a final check that a drop is possible and updates the dropSiteStatus, operations, and operation fields in the XmNdropProc callback structure for the initiator to read in its XmNdropStartCallback callback structure.

    If the drop was the result of the user requesting help, the receiver's XmNdropProc displays information in a dialog and waits for a response from the user before either continuing or cancelling the drop.

    If the drop is valid, the receiver requests transfer information from the initiator.

    Only the receiver can cancel a drop.

  • The initiator's XmNdropStartCallback callback routine is called after the receiver's XmNdropProc has finished. The values of the dropSiteStatus, operation, and operations fields in the callback structure were set by the toolkit on the receiver side or XmNdropProc.

When data is transferred between initiator and receiver

  • The receiver's XmNdropProc establishes a list of data and target formats it wants to receive, and calls the XmDropTransferStart function. The list can be updated with XmDropTransferAdd during the transfer.

    The receiver registers an XmNtransferProc to process each transfer from the initiator.

    The receiver can cancel the drop while the transfer is in progress.

    If there is no information to be transferred, or if the drop is cancelled, the receiver must still call XmDropTransferStart. The initiator is unable to proceed until it is notified that a transfer has ended. Only the receiver can cancel a drop

  • The initiator's XmNconvertProc routine is executed in response to a request from the XmDropTransferStart function called by the receiver. It returns the information formatted according to the requested target to the receiver's XmNtransferProc.

After the drop has finished

  • The initiator's XmNdropFinishCallback is called when the transfer is complete. The initiator's XmNdragDropFinishCallback is called after the whole drag and drop transaction has finished.

Drag and Drop Protocols

The protocols refer to how the initiator and receiver clients use the toolkit to communicate with each other. There are two drag protocol styles that are available. The drop protocol is based on the Xt selection protocol.

Drag Protocols

The toolkit on the initiator side is in charge during the drag. The protocol in effect determines how it will find the information about drop sites that it needs to manage visuals, and how extensively the initiator and receiver clients are involved during the drag.

There are two kinds of drag protocol styles:

Preregister 

Stores drop site information in a database when the drop site is registered. The receiver is not involved in the drag until a drop occurs. All drag-over and drag-under visual effects are managed by the toolkit on the initiator side.

Dynamic 

Uses messages from the toolkit to the receiver to find out drop site information. The toolkit on the receiver side can reply to the messages, or the application can take action based on these messages. The receiver manages the drag-under effects.

The code for the initiator is the same regardless of the protocol. The code for the receiver applications is the same except that in the dynamic mode, the receiver's XmNdragProc is called.

The drag protocol in use can change during the course of a drag. When the drag icon enters or leaves a top-level window, the source and potential drop receiver negotiate a mutually acceptable drag protocol, as described in Section 15.3.2, "Choosing the Protocol and Visual Style."

The Preregister Drag Protocol

When a receiver supports the preregister protocol, the toolkit on the receiver side stores drop site information in a database. The toolkit on the initiator side manages all drag-under effects based on the information in the drop site database. By setting some DropSite resources appropriately, the receiver can have the toolkit use different highlighting or pixmaps, but the receiver does not participate directly in the drag-under effects.

With the preregister protocol

  • The toolkit uses pixmap source icons if the client provides them. If not, it uses bitmap source icons if the client provides them. If the client provides neither, the toolkit uses XmScreen icons. The XmScreen icons can be either the default icons or ones provided by the client or user.

  • The server is grabbed.

  • The only customization a receiver can perform is providing custom values for the DropSite visual resources.

  • The drag icon can be any size supported by the system on which the application is running.

The Dynamic Drag Protocol

With the dynamic drag protocol, the initiator and receiver communicate with messages through the toolkit.

As the drag icon moves within the receiver's window, messages are sent from the toolkit on the initiator side to the toolkit on the receiver side. Based on these messages, the receiver determines whether the drag icon is entering, within, or leaving a drop site. Although the toolkit on the receiver side initializes state and operation information, the receiver can check and update this information further if it registers a XmNdragProc for the drop site. The initiator receives the updated message in one of its drag-related callbacks (described later in this chapter), and can take action accordingly.

The dynamic drag protocol allows the receiver to provide more sophisticated visual effects using the XmNdragProc than the toolkit can provide alone.

With the dynamic drag procotol

  • The receiver can provide custom drag processing and drag-under visual effects.

  • The drag icon must fit in the largest cursor size supported by the system running the application. If it is too large, it will be truncated to fit.

Choosing the Protocol and Visual Style

The user can specify which drag protocol to use or the application can specify the drag protocol in resource file.

The preregister drag protocol can be used with a minimum of additional coding in an application, because the toolkit manages the drag-over visual effects using the default drag icons specified in the XmScreen object. Or the application can override the default XmScreen icons with custom icons, but still allow the toolkit to manage the effects.

The dynamic drag protocol requires more work for the application program, but allows a receiver application to provide visual effects beyond the capabilities of the toolkit.

The drag protocol in use has an effect on the system performance as described later in this section.

Specifying Drag Protocols

Two Display resources specify which protocol the toolkit should try to use when a client is an initiator or receiver. These resources can be set by the client in a resource file or by the user.

  • XmNdragInitiatorProtocolStyle

  • XmNdragReceiverProtocolStyle

These resources can take the following values (the letter in brackets following the value is used in Table 15-1 ):

XmDRAG_NONE [N]  


Does not participate in drag and drop. There are no drag-under effects. The drag-over effects depend on the value of XmNdragInitiatorProtocolStyle.

XmDRAG_DROP_ONLY [X]  


Does not support either the preregister mode or the dynamic mode, but does data transfer after the drop occurs. There are no drag-over or drag-under visual effects.

XmDRAG_PREREGISTER [P]  


Supports the preregister mode only. The visual effects are managed by the toolkit.

XmDRAG_PREFER_PREREGISTER [PP]  


Supports both protocols, but prefers the preregister protocol. This is the default for receivers. The visual effects are determined by the protocol actually used.

XmDRAG_PREFER_RECEIVER [R]  


Used by initiators only. Uses the protocol that the receiver specifies. This is the default for initiators. The visual effects are determined by the protocol actually used.

XmDRAG_PREFER_DYNAMIC [PD]  


Supports both protocols, but prefers the dynamic mode. The visual effects are determined by the protocol actually used.

XmDRAG_DYNAMIC [D]  


Supports the dynamic protocol only. The drag-over and drag-under visual effects are managed by the clients.

For example:

myclient*dragInitiatorProtocolStyle: DRAG_PREFER_DYNAMIC
myclient*dragReceiverProtocolStyle:  DRAG_PREFER_DYNAMIC

If the initiator and receiver have specified the same protocol, that protocol is used. If they specify different protocols, the protocol that is used is shown in the following table.

Table 15-1. Initiator and Receiver Protocols

Initiator Protocol

 

 

Receiver Protocol

 

 

 

P

PP

PD

D

X

N

P

P

P

P

X

X

N

PP

P

P

P

D

X

N

R

P

P

D

D

X

N

PD

P

D

D

D

X

N

D

X

D

D

D

X

N

X

X

X

X

X

X

N

N

N

N

N

N

N

N

The XmGetXmDisplay function returns the Display object ID associated with a specific display. XtGetValues can be used to check the protocol style resources.

If an XmNdragProc is specified for a drop site, it will be performed only if the protocol is dynamic. In this case, the application should set the XmNdragReceiverProtocolStyle resource to the value XmDRAG_PREFER_DYNAMIC in the application-class defaults file rather than use the default value.

Protocols and Visuals

When the resulting protocol is preregister, a preregister visual style is used, and the server is grabbed. The drag-over visual can be a pixmap with an arbitrary size whose depth and colormap are the same as those of the widget associated with the drag source. The pixmap is specified in the DragContext XmNsourcePixmapIcon resource.

When the resulting protocol is dynamic, a dynamic visual style is always used. The drag-over visual is implemented with the X cursor, which must be a bitmap, and often has limited size (use XQueryBestSize to find out the largest size available per screen). The cursor is specified using XmNsourceCursorIcon.

Users will specify one of the preregister values for XmNdragInitiatorProtocolStyle because they want good performance when network loading or context switching are problems, or because they want better drag-over visuals rather than more sophisticated drag-under visuals.

Users will specify one of the dynamic values for XmNdragInitiatorProtocolStyle because there are clients that use use the dynamic effects, and for visual consistency, they want to use a dynamic visual style whenever possible.

Consequently, when the resulting protocol is XmDRAG_NONE or XmDRAG_DROP_ONLY, the visual style depends upon the value of XmNdragInitiatorProtocolStyle. When it is XmDRAG_DYNAMIC or XmDRAG_PREFER_DYNAMIC, the dynamic visual style is used; otherwise, the preregister visual style is used.

Drop Protocol

When a drop is made, the receiver checks what action should happen:

  • If the user requested help, the receiver should display a dialog explaining the consequences of a drop on the site and determine if the user wants to continue or cancel the drop.

  • If the user requests a cancel from the help dialog or presses KCancel, or if the receiver determines that the drop cannot continue, the receiver sets the number of transfers to zero and the status to failed to cancel the drop.

  • If the drop can continue normally, the receiver starts a transfer.

The drop protocol is a superset of the Xt incremental and nonincremental protocol, with two main differences:

  • The receiver and initiator need only one XmNtransferProc and XmNconvertProc (the Xt selection process requires separate procedures for incremental and nonincremental transfer). They each specify whether the transfer is incremental or not from their side of the transfer with DropTransfer and DragContext resources. If the initiator and receiver use the same incremental or nonincremental protocol, the toolkit deals with each in the requested protocol.

  • The initiator and receiver are both notified of the completion of the entire transfer, regardless of how many subtransfers were involved.

The drop protocol is handled by a DropTransfer widget created by XmDropTransferStart in the receiver client. The receiver creates a list of information and target types desired from the initiator, along with an XmNtransferProc to handle any processing needed during the transfer. The toolkit processes the requests one at a time, until it has finished with the list.

The receiver must call XmDropTransferStart, even if the number of transfer requests is zero. Otherwise, the initiator will keep waiting for a transfer request.

For each transfer request, the initiator's XmNconvertProc is called. This procedure reads and processes the request and returns the information.

Drop Receiver Responsibilities for Dragging

The drop receiver responsibilities are covered first in this chapter, because in the dynamic protocol, motion messages go first to the receiver client. The receiver evaluates the state of the drag and sends an updated message to the initiator, which then manages its drag-over visuals based on the results.

The drag receiver has some responsibilities before a drag even starts:

  • It registers widgets as drop sites, providing information about valid operations, target types accepted, and drag-under effects (animation style). The application can use the default values for this information, or provide its own values.

  • It registers an XmNdropProc that is called when a drop occurs and which starts the transfer of information from the initiator. This XmNdropProc also processes any Help information the application provides about a drop site.

  • It optionally registers an XmNdragProc for use with the dynamic protocol that is called for events while a drag is within the widget's boundaries.

If the drag protocol in effect is preregister, the drop site information is put in the database as the drop sites are registered and the receiver client does nothing until a drop is made. All visual effects are handled by the toolkit.

If the drag protocol is dynamic, messaging begins when the pointer enters the window containing the drop site. The receiver is given the opportunity to provide additional processing in its XmNdragProc. The XmNdragProc

  • Receives messages when the drag icon enters or leaves the drop site, the operation changes, the drag icon is in motion, or the drag is cancelled.

  • Provides information back to the toolkit about the state of the drag (valid drop site, invalid drop site, no drop site) and the operation to be performed when a drop is made.

  • Manages any custom drag-under visual effects.

Establishing a Drop Site

Text and TextField widgets register themselves as drop sites. An application must register any other widgets it wants to use for drop sites. A widget may be registered as only one drop site.

XmDropSiteRegister registers a widget as a drop site, establishes callbacks to be used when a drag is made through the drop site or a drop is made in the drop site, and provides target and operation information. If the protocol is preregister, the information is stored in a database, which is read by the toolkit during the drag. If the drag protocol is dynamic, messaging is used to check for possible drop sites within a widget.

The application must register an XmNdropProc routine to establish a list of transfer requests and start the transfer. The other resources can be left at their default values if those values are acceptable to the application.

The optional XmNdragProc routine is executed only if the drag protocol is dynamic. It is called in response to events during the drag, and allows the receiver to provide additional drag-under effects or additional drag processing.

The XmNdropSiteOperations resource lists all operations that the drop site will support, combined by the bitwise OR operations (|). For example, the default value

XmDROP_COPY | XmDROP_MOVE

means that Copy and Move are valid operations, but Link is not. During a drag, the toolkit on the receiver side compares this list with the DragContext's XmNdragOperations list and the user-selected operation to arrive at the operation that will be performed if a drop occurs on this site, along with a list of all operations possible between the initiator and the current drop site.

If an application wants to use only one operation, such as Copy, it should set the XmNdropSiteOperations field to just that operation to ensure that the toolkit chooses the correct operation and drag icon during the drag and drop transaction.

Drop sites that represent copying devices, such as printers, or transformation devices, such as compilers, should perform a Copy rather than a Move if both are possible.

The XmNdropSiteActivity resource indicates whether the drop site is available for use:

XmDROP_SITE_ACTIVE  


The drop site is available for use. This is the default value.

XmDROP_SITE_INACTIVE  


The drop site is not available for use. If the drag icon is moved over the drop site, both the icon and drop site act as if the icon were not over a drop site.

The XmDropSiteUnregister function removes a widget from the DropSite registry. Once a widget is unregistered, it displays no drag-under visual effects and cannot accept a drop.

The difference between an unregistered drop site and an inactive drop site is that the inactive drop site is still registered; it still uses memory, but does not engage in any drag and drop transactions. One use for inactive drop sites is to provide the correct clipping on overlapping drop sites. An unregistered drop site is no longer involved in the drag and drop system. It is the same as a widget that was never registered.

The following code from the main routine in DNDlabel.c in "A Simple Drag Receiver" generates a simple drop site on a Label widget. The only target type it recognizes is compound text. The only operation it will accept is Copy. The other resources, including drag-under effects, are left at their default values.

Figure 15-4. A Label Widget

Figure 15-4 
A Label Widget

Label = XmCreateLabel(BulletinB, "title", args, n);
XtManageChild(Label);
/* register the label as a drop site */
importList[0] = COMPOUND_TEXT;
n = 0;
XtSetArg(args[n], XmNimportTargets, importList); n++;
XtSetArg(args[n], XmNnumImportTargets, 1); n++;
XtSetArg(args[n], XmNdropSiteOperations, XmDROP_COPY); n++;
XtSetArg(args[n], XmNdropProc, HandleDrop); n++;
XmDropSiteRegister(Label, args, n);
XtRealizeWidget(topLevel);
XtAppMainLoop(app_context);

Changing a Drop Site

The XmDropSiteUpdate function is used to change drop site resources for a single drop site. For multiple requests, XmDropSiteStartUpdate signals that a series of XmDropSiteUpdate requests will follow, and XmDropSiteEndUpdate ends the series and processes the requests at one time.

XmDropSiteUpdate can also be used to change the resource values of the widgets that register themselves as drop sites (Text and TextField). For instance, an application can change Text's XmNdropProc to call a procedure in the application.

Specially Shaped Drop Sites

The shape of a simple drop site can be specified as the union of a set of specified rectangles clipped by the associated widget.

If only part of the widget is to be sensitive to a drop, it is defined by a list of rectangles in the XmNdropRectangles resource. If the resource is NULL, the drop site is the smallest enclosing widget and the shape of the drop site is the shape of the widget.

The rectangles that make up the drop site do not need to be contiguous. All the noncontiguous segments of the drop site act as one; they are all highlighted the same way at the same time. A drop on one segment is the same as a drop on any of the other segments. This might look to the user as if there were several drop sites on a single widget, but the application handles nested drop sites differently from drop sites made of noncontiguous segments. Nested drop sites, whether simulated or real, may have different drag-under effects, targets, operations, or callback procedures.

The following example establishes a sensitive area shaped like a plus sign on a DrawnButton widget named Button2. Even if the drag icon is within the Button2 widget, no drag-under effects are shown until the drag icon is within the sensitive area. The area is visible only when a drag icon enters it and highlighting occurs. The sensitive area is the only part of the widget that accepts a drop. This code is not in one of the three example programs included in Section 15.2.1, "Complexity of Drag and Drop Programs," and Appendix B, "Drag and Drop Example Program."

Figure 15-5. Specially Shaped Drop Site

Figure 15-5 
Specially Shaped Drop Site

XRectangle plus[] = {  
    {30, 0, 30, 30},   
    {0, 30, 90, 30},   
    {30, 60, 30, 30},  
    };
     .
     .
     .
n = 0;
XtSetArg(args[n], XmNimportTargets, importList); n++;
XtSetArg(args[n], XmNnumImportTargets, 1); n++;
XtSetArg(args[n], XmNdropProc, HandleDrop); n++;
XtSetArg(args[n], XmNdropRectangles, plus); n++;
XtSetArg(args[n], XmNnumDropRectangles, 3); n++;
XmDropSiteRegister(Button2, args, n);

Nested Drop Sites

A widget can be registered as only one drop site. However, widgets that are registered as drop sites can be nested within each other, providing nested drop sites.

The XmNdropSiteType indicates the complexity of the drop site:

XmDROP_SITE_SIMPLE  


The drop site contains no other drop sites.

XmDROP_SITE_COMPOSITE  


The drop site contains other drop sites. This value is generally associated with a Manager.

A composite drop site must be registered before any of its children are registered. If a composite drop site is inactive, so are all of its children.

The composite and children drop sites do not need to have the same operations or targets.

A manager that contains a number of widgets with their associated drop sites does not need to be a composite drop site unless it is possible to drop in the background of the manager.

It is possible for an application to simulate nested drop sites on a single widget, for example, a DrawingArea. The process is described as part of the discussion of the duties of the optional XmNdragProc routine in Section 15.4.2, "XmNdragProc."

Overlapping Drop Sites

Drop sites can overlap. Their stacking order is assumed to correspond to the order in which they are registered, with the first-registered one on top. XmDropSiteQueryStackingOrder checks the stacking order, whereas XmDropSiteConfigureStackingOrder changes it.

When a drop site is overlapped by another drop site, the drag-under effects of the drop site underneath are clipped as appropriate by the obscuring drop site.

A widget or gadget that is not a drop site can overlap and partially obscure a drop site. To ensure that the drop-site's drag-under visuals are appropriately clipped by the obscuring widget, such sibling widgets should be registered as inactive drop sites. Parent widgets, whether drop sites or not, will clip their children's drop site visuals. If a parent has some active and some inactive drop site children, it should be registered as an active drop site.

Drag-Under Visual Effects

Drag-under visual effects are displayed only when the pointer is within the sensitive area of the drop site widget. Various drag-under styles can be chosen in the XmNanimationStyle DropSite resource:

XmDRAG_UNDER_HIGHLIGHT  


A solid border around the sensitive area of the drop site is used to show the drop site is valid. This is the default value.

XmDRAG_UNDER_SHADOW_OUT  


The sensitive area of the drop site looks pushed out when it is valid.

XmDRAG_UNDER_SHADOW_IN  


The sensitive are of the drop site looks pushed in when it is valid.

XmDRAG_UNDER_PIXMAP  


A custom pixmap is used to indicate the drop site is valid. The pixmap is specified in XmNanimationPixmap.

XmDRAG_UNDER_NONE  


No indication is given that the drop site is valid.

The following illustration shows the default drag-under animation around the Label widget drop site.

Figure 15-6. Default Drag-Under Animation

Figure 15-6 
Default Drag-Under Animation

If the value of XmNanimationStyle is XmDRAG_UNDER_PIXMAP, the resources XmNanimationPixmap, XmNanimationMask, and XmNanimationPixmapDepth are used to provide more information about the pixmap. If the depth does not match the depth of the window controlling the drop site widget, no animation occurs. Except for XmDRAG_UNDER_PIXMAP, the colors used for the visual effects are based on the colors of the widget associated with the drop site.

The dynamic protocol provides the most control over the drop site animation. It is the only way to get visual effects that do not remain the same for the duration of the drag icon's stay in the drop site, for example, a background that flashes.

XmNdragProc

The procedure registered in the DropSite's XmNdragProc resource is called only when the dynamic protocol is in effect. This procedure is optional. Applications that need to provide special drag-under effects or other special processing during a drag can do so with this procedure.

The XmNdragProc procedure is called in response to messages from the toolkit, before the initiator's equivalent drag callback. Fields in the callback structure provide information to the receiver about the drag.

The reason field in the callback structure indicates why the procedure was called.

XmCR_DROP_SITE_ENTER_MESSAGE  


The drag icon hotspot has entered the drop site.

XmCR_DROP_SITE_LEAVE_MESSAGE  


The drag icon hotspot has left the drop site.

XmCR_DRAG_MOTION_MESSAGE  


The drag icon hotspot has moved.

XmCR_OPERATION_CHANGED_MESSAGE  


The operation has changed.

The operations field lists all the operations that are valid for the drop site with the current drag source. The operations field is initialized by the toolkit as follows:

  • If the user has selected an operation, the value of operations is initialized to that operation if it is in the DragContext's XmNdragOperations list.

  • Otherwise, the operations field is initialized to the list in the DragContext's XmNdragOperations list.

The operation field indicates the type of action a successful drop will perform. The toolkit initializes the operation field by taking the following steps, in order of precedence from highest to lowest:

  1. If Move is a valid operation (in both the operations field and the DropSite's XmNdropSiteOperations list), operation is initialized to XmDROP_MOVE.

  2. If Copy is a valid operation, operation is initialized to XmDROP_COPY.

  3. If Link is a valid operation, operation is initialized to XmDROP_LINK.

  4. Otherwise, operation is initialized to XmDROP_NOOP.

The dropSiteStatus field provides an indication of whether a transfer between the initiator and this drop site could occur. The value that the toolkit selects for the dropSiteStatus field depends on the reason the XmNdragProc procedure was entered:

  • If the reason is motion or drop site leave, and the drop site is the same as in the last call to XmNdragProc, the dropSiteStatus field is the same as at the end of the previous call.

  • Otherwise, if there is at least one target in common and at least one operation in common, the value is initialized to XmDROP_SITE_VALID. If not, the value is initialized to XmDROP_SITE_INVALID.

  • If the operation field is XmDROP_NOOP, the dropSiteStatus field is initialized to XmDROP_SITE_INVALID.

The XmNdragProc procedure can update operation, operations, or dropSiteStatus further during its execution. The final values for these fields are available to the initiator in its drag callback structures. If the receiver's XmNdragProc procedure is called more than once while the drag icon is within the drop site (for example, because of motion events), the values used by the toolkit when it initializes the drag callback operations, operation, and dropSiteStatus fields are the ones at the end of the previous call to XmNdragProc.

The animate field tells the toolkit who should provide the drag-under visual effects. It is initially set to True, but the XmNdragProc routine can set it to False.

True 

The toolkit provides the drag-under visuals as if the protocol were preregister.

False 

The receiver provides the drag-under visuals. The application can provide special visual effects, such as a blinking background, that are not possible with the toolkit.

The DragProcCallback routine in the DNDDemo.c program in Appendix B, "Drag and Drop Example Program," is an example of a DragProc routine. It can process every drag message, changes the operations, operation, and dropSiteStatus as necessary, and sets the animate field to True, allowing the toolkit to manage the drag-under effects. The DragProcCallback routine is shown in the next section of this chapter.

Simulating Nested Drop Sites

A widget can be registered as only a single drop site. However, if the application needs one or more drop sites entirely enclosed within another drop site, there are two ways to accomplish this:

  • Widgets that contain other widgets that are drop sites should be registered as composite drop sites as described earlier in this chapter.

    This method allows the toolkit to manage drop site messages and drag-under effects for each nested drop site.

  • An application can simulate multiple drop sites on a single widget in the XmNdragProc and XmNdropProc routines. Because the XmNdragProc routine is executed only in the dynamic drag protocol mode, this method would not work if the drag procotol chosen is preregister.

    This method requires that the application manage all drag-under effects, because the toolkit is not aware of the simulated nesting.

To simulate nested drop sites on a single widget:

  1. Register the widget as a single active drop site. Set XmNdropSiteOperations to all the operations possible for any of the nested drop sites. Set XmNimportTargets to all the targets possible for any of the nested drop sites. Register an XmNdragProc routine to provide any special drag-under effects for the simulated drop sites.

    The operations, operation, and dropSiteStatus fields are initialized by the toolkit only when this outer drop site is entered or left. The simulated drop sites must be managed by the application.

  2. When either XmNdragProc or XmNdropProc is called, check the x and y fields in the callback structure to determine which of the nested drop sites contains the pointer.

  3. If the pointer is within a simulated nested drop site, update the callback fields as follows:

    • When the pointer enters the simulated nested drop site, save the value of the operations and operation fields.

    • Remove any operations from the operations field that do not apply to the simulated drop site.

    • Set operation to the valid operation preferred by the simulated drop site, or to XmDROP_NOOP if the operations list does not contain the preferred operation.

    • The dropSiteStatus field must reflect the status of the simulated drop site so that the initiator can manage drag-over effects correctly:

      - Set the dropSiteStatus to XmDROP_SITE_VALID if the operation is allowed in the simulated drop site and if there is at least one target in common between the simulated drop site and the initiator. (Use the XmTargetsAreCompatible routine to check the targets.)

      - Set the dropSiteStatus to XmDROP_SITE_INVALID if the operation is not allowed in the simulated drop site, if there are no targets in common, or if the operation is XmDROP_NOOP.

    • Display appropriate drag-under visual effects.

    • When the pointer leaves the simulated drop site, restore the original values of operations and operation that apply to the outer drop site.

  4. If the pointer is not within a simulated drop site, but drops are allowed in the outer drop site, update the fields as described in the previous step.

  5. If the pointer is not within a simulated drop site, and drops are not allowed in the outer drop site, set the dropSiteStatus field to XmDROP_SITE_INVALID.

If the preregister protocol is in effect, the simulated drop sites cannot be managed during the move, because XmNdragProc is not performed; but they can be managed at the drop with XmNdropProc.

In the following example, only the top-level window, DNDDemo, is registered as a drop site. The user can create rectangles within the window that then act like drop sites themselves. The user can drag and drop colors from one of the six buttons in the lower part of the window onto the rectangles to change the color of the rectangle. However, these rectangles are not registered drop sites, they are simulated.

The user can also drag these rectangles to new locations.

Figure 15-7. Simulated Drop Sites

Figure 15-7 
Simulated Drop Sites

The RegisterDropSite routine registers the DrawingArea widget as a drop site. The list of operations and targets may not be valid for each simulated drop site, but are valid for other simulated drop sites.

RegisterDropSite(w)
Widget w;
{
    Display *display = XtDisplay(w);
    Atom    targets[3];
    Arg     args[5];
    int     n = 0;
    /* Only accept moves or copies */
    XtSetArg(args[n], XmNdragOperations,
               XmDROP_COPY | XmDROP_MOVE); 
    n++;
    /* set all possible targets for any of the
     * nested drop sites
     */
    targets[0] = XmInternAtom(display, "_MY_RECTANGLE", False);
    targets[1] = XmInternAtom(display, "BACKGROUND", False);
    targets[2] = XmInternAtom(display, "PIXMAP", False);
    XtSetArg(args[n], XmNimportTargets, targets); n++;
    XtSetArg(args[n], XmNnumImportTargets, 3); n++;
    /* register a dragProc - necessary for simulating nested
     * drop sites
     */
    XtSetArg(args[n], XmNdragProc, DragProcCallback); n++;
    /* register a dropProc */
    XtSetArg(args[n], XmNdropProc, DropProcCallback); n++;
    XmDropSiteRegister(w, args, n);
}

The XmNdragProc routine, DragProcCallback, is called whenever a drag icon enters the registered drop site (the top level window). The RectFind routine from DNDDraw.c in Appendix B, "Drag and Drop Example Program," determines if the pointer is in a simulated drop site. The CheckTargets routine determines if the object being dragged is one of the six colors (bgFound) or one of the created rectangles (rectFound). (The value pixFound to represent a pixmap being dragged is coded in this routine, but not in the rest of the program.)

The only drag-under visual is displayed when a color is dragged to a rectangle. The outline of the rectangle is highlighted.

The entire DragProcCallback routine is too long to be listed in its entirety here. The section dealing with the drop site enter message is used as an example.

static void DragProcCallback(w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
    XmDragProcCallbackStruct *cb =
                           (XmDragProcCallbackStruct *) call;
    Display                     *display = XtDisplay(w);
    Boolean                     rectFound, bgFound, pixFound;
    static unsigned char        initial_operations;
    static unsigned char        initial_operation;
    RectPtr                     rect;
    CheckTargets(cb->dragContext, display, &rectFound,
        &bgFound, &pixFound);
    switch(cb->reason) {
        case XmCR_DROP_SITE_ENTER_MESSAGE:
            /* save the value of the operations and operation 
             * fields
             */
            initial_operations = cb->operations;
            initial_operation = cb->operation;
            rect = RectFind(cb->x, cb->y);
            /* Remove any operations for the operations field 
             * which do not apply to the simulated drop site.
             */
            if (rect) {
                if (bgFound || pixFound) {
                    cb->operations = XmDROP_COPY;
                    RectHighlight(w, rect);
                }
                else if (rectFound) {
                    cb->operations = cb->operations & 
                                  (XmDROP_COPY | XmDROP_MOVE);
                    RectUnhighlight(w);
                }
            }
            else {
                cb->operations = initial_operations & 
                                 (XmDROP_COPY | XmDROP_MOVE);
                RectUnhighlight(w);
            }
            /* Set operation to the valid operation preferred
             * by the simulated drop site or to XmDROP_NOOP
             * if the operations list does not contain the
             * preferred operation.
             */
            if (rect) {
                if (bgFound || pixFound) {
                    if (cb->operations & XmDROP_COPY)
                        cb->operation = XmDROP_COPY;
                    else
                        cb->operation = XmDROP_NOOP;
                }
                else if (rectFound) {
                    if (cb->operations & XmDROP_MOVE)
                        cb->operation = XmDROP_MOVE;
                    else if (cb->operations & XmDROP_COPY)
                        cb->operation = XmDROP_COPY;
                    else
                        cb->operation = XmDROP_NOOP;
                }
            }
            else {
                if (rectFound) {
                    if (cb->operations & XmDROP_MOVE)
                        cb->operation = XmDROP_MOVE;
                    else if (cb->operations & XmDROP_COPY)
                        cb->operation = XmDROP_COPY;
                    else
                        cb->operation = XmDROP_NOOP;
                }
                else
                    cb->operation = initial_operation;
            }
            /*
             * Set dropSiteStatus to XmDROP_SITE_INVALID if
             * the operation field is XmDROP_NOOP, or if there
             * are no  common targets between the source and
             * the nested  drop site.  Otherwise, set
             * dropSiteStatus to  XmDROP_SITE_VALID. 
             */
            if (cb->operation == XmDROP_NOOP ||
                (rect && (!rectFound && !bgFound && !pixFound))
                || (!rect && !rectFound))
                cb->dropSiteStatus = XmINVALID_DROP_SITE;
            else
                cb->dropSiteStatus = XmVALID_DROP_SITE;
            /*
             * Display appropriate drag-under visuals.  Only 
             * highlight the rectangle if we are changing 
             * rectangle attributes.
             */
            if (rect && bgFound || pixFound &&
                cb->dropSiteStatus == XmVALID_DROP_SITE)
                RectHighlight(w, rect);
            break;
        case XmCR_DROP_SITE_LEAVE_MESSAGE:
            .
            .
            .

Drag Initiator Responsibilities for Dragging

The application in which the user initiates the drag is considered the drag initiator.

The drag initiator

  • Recognizes the start of a drag (BTransfer Press) within a widget controlled by the application.

  • Establishes a DragContext for the widget, providing information about operations, targets, and drag-over visuals, using the XmDragStart function.

  • Optionally, provides special drag-over effects.

These steps are described in the following sections.

Recognizing a Drag Has Started

The initiator client must be able to recognize the BTransfer Press event within a widget it allows to be a drag source. It may have to override an already-assigned translation for the widget.

The following example from the main routine of DNDscroll.c in "A Simple Drag Source" overrides the existing mouse button 2 translation for the ScrollBar widget, and maps it to the StartDrag routine, which will start the drag transaction.

static char dragTranslations[] = 
     "#override <Btn2Down>: StartDrag()";
static XtActionsRec dragActions[] = 
     { {"StartDrag", (XtActionProc)StartDrag}, };
     .
     .
XtTranslations parsed_xlations;
     .
     .
/* override button two press to start a drag */
parsed_xlations = XtParseTranslationTable(dragTranslations); 
XtAppAddActions(app_context, dragActions,
                  XtNumber(dragActions));
/* create a scroll bar widget */ 
n = 0;
XtSetArg(args[n], XmNtranslations, parsed_xlations); n++;
scrollbar = XmCreateScrollBar(BulletinB, "testscroll",
                                args, n);   
XtManageChild(scrollbar);

Translation may be more complicated in some editable widgets, in which BTransfer Click is used for primary transfer, and BTransfer Motion is used for drag and drop.

Starting a Drag With XmDragStart

Not every widget in an application can be a drag source. Text, Label, Button, and List widgets are automatically defined as drag sources. Other widgets must have a translation for BTransfer assigned to them, establish DragContext resources for the widget, and call the XmDragStart routine to become drag sources. If the user tries to drag objects from a widget that is not recognized as a drag source by either the toolkit or the source application, nothing happens.

The XmDragStart function initiates a drag and creates a DragContext widget. At a minimum, the XmNconvertProc DragContext resource, must be specified. Other resources are optional, for example, those specifying drag-over visual effects.

The XmNdragOperations resource lists all the operations that the initiator will support for this drag source, combined by the bitwise OR operation (|). During a drag, the toolkit compares this list with the receiver's XmNdropSiteOperations list and the user-selected operation to arrive at the operation that will be performed if a drop occurs on this site.

If an application wants to use only one operation, it should set the XmNdragOperations resource to just that operation to ensure that the correct operation and drag icon are chosen by the toolkit during the drag and drop transaction.

The following example from DNDscroll.c in "A Simple Drag Source" establishes a target type of compound text and an operation of Copy, then establishes a DragContext for this transaction with XmDragStart. This drag source does not have any custom drag icons or any drag callbacks.

static void StartDrag(w, event)
Widget  w;
XEvent  *event;
{
   Arg             args[MAX_ARGS];
   Cardinal        n;
   Atom            exportList[1];
   /* establish the list of valid target types */
   exportList[0] = COMPOUND_TEXT;
   n = 0; 
   XtSetArg(args[n], XmNexportTargets, exportList); n++;
   XtSetArg(args[n], XmNnumExportTargets, 1); n++;
   XtSetArg(args[n], XmNdragOperations, XmDROP_COPY); n++;
   XtSetArg(args[n], XmNconvertProc, DragConvertProc); n++;
   XmDragStart(w, event, args, n);
}

If drag or drop callbacks are desired, they are added to the DragContext's callback resources. For example, a callback procedure named EnterCallBack that is performed when the pointer enters an active drop site could be added as follows:

Widget         dc;
   .
   .
   .
   dc = XmDragStart(w, event, args, n);
   XtAddCallback(dc, XmNdropSiteEnterCallback, EnterCallBack,
                   NULL); 

Overriding Existing Drag Sources

XtGetValues is used to check the values of widgets resources established as drag sources earlier in the application, and XtSetValues is used to update these values. The widget ID used is the DragContext, not the source widget ID, so that the change applies only to the widget during the drag.

If the widget is a predefined drag source (Text, Label, Button, or List), overriding the default behavior becomes more complex. The widget calls XmDragStart when the drag starts, and the application cannot call XmDragStart again for the widget. Instead, it must update the existing DragContext. First it must find the DragContext for the widget, then establish the new behavior. One possible means to accomplish this is as follows:

  • Override the existing Btn2Down translation with a new translation that calls the widget's action and also an action supplied by the application. For the Text widget, this new translation might look as follows:

    <Btn2Down> : process-bdrag() my-drag-start()
    

  • Register the new action, using XtAppAddActions.

  • In the new action procedure, call XmGetDragContext to get the DragContext, and then call XtSetValues to change resource values. The timestamp argument to XmGetDragContext can be the timestamp from the event passed to the action routine.

For instance, Text allows the Copy and Move operations. If an application can support only Copy, it must update the DragContext's XmNdragOperations resource.

Drag-Over Visual Effects

When the user moves the mouse, a drag icon representing the object being dragged moves around the screen instead of the usual pointer. As the icon is dragged over portions of the screen, the icon may change to show the status of a possible drop. These drag-over visual effects help the user know how to proceed with the drag.

There are four ways to provide drag-over visual effects:

  • Use the default drag-over visuals, specified in the XmScreen object. The toolkit manages all the drag-over effects.

  • Put custom icons and pixmaps in the XmScreen visual resources to be used as default icons for all drag and drop transactions running on that XmScreen. The toolkit manages all the drag-over effects using these new icons. These resources can be modified by the application or the user in a resource file.

  • Put custom icons and pixmaps in the DragContext visual resources for source, state, or operation icons. The application must monitor the state of the drag using the drag callbacks and update the DragContext icon values as necessary. The default icons specified in the XmScreen object are used only if the value for the equivalent DragContext visual resource is NULL.

  • Manage the drag-over effects entirely in the application by drawing directly to the screen. The toolkit is not used, nor are the XmScreen and DragContext visual resources.

If the application provides custom icons and they are unsuitable for some reason, the toolkit defaults to the XmScreen drag-over visuals.

The drag icon consists of a source icon, optionally combined with a state icon and an operation icon.

Each drag icon has a hotspot. Since a drag icon could be quite large, the hotspot provides a single pixel that is used in providing drag-over and drag-under effects. For example, if the drag icon moves into the area of a valid drop site, neither the drag icon or the drop site will provide visual clues until the hotspot has moved into the area. By default, the hotspot is the upper left corner of the state icon.

In the following illustration, the running figure is the source icon, the state icon is the arrow in the corner, and the operation icon shows a Copy will happen if a drop is made. The default blending and attachment values are used.

Figure 15-8. A Drag Icon

Figure 15-8 
A Drag Icon

Source Icon

The source icon is a picture representing the object being dragged. It can be either a pixmap or cursor. The client can specify a custom pixmap in the DragContext resource XmNsourcePixmapIcon or a custom cursor in the XmNsourceCursorIcon resource. If these resources are NULL or not usable (too large, not a bitmap, or created on a different screen, for example), the default cursor given in the XmScreen resource XmNdefaultSourceCursorIcon is used.

The pixmap icon is used with the preregister visual style. The colormap is based on the source widget. The cursor icon is used for the dynamic visual style.

The following illustration shows the default source icons for general purpose, List, Label, and Text widgets.

Figure 15-9. Source Icons

Figure 15-9 
Source Icons

State Icon

The state icon is a cursor that indicates if the drag is over a valid drop site, invalid drop site, or no drop site. The default state icons are in the XmScreen resources XmNdefaultValidCursorIcon, XmNdefaultInvalidCursorIcon, and XmNdefaultNoneCursorIcon.

A custom state icon can be specified in the DragContext resource XmNstateCursorIcon. This icon must be changed appropriately as the state of the drag changes, using the drag callbacks. If XmNstateCursorIcon is NULL, not a bitmap, or not defined on the same screen as XmScreen, the default XmScreen icons are used.

The default state icon for all three states is an arrow, usually shown at the upper left corner of the operation icon.

Three DragContext resources can be used to change the color of the drag icon based on the state of the drag: XmNvalidCursorForeground, XmNinvalidCursorForeground, XmNnoneCursorForeground. This allows visual feedback about the drag to the user, without changing the icon shape. For example, the following lines in a resource file would make the drag icon green when it was over a valid drop site, red when it was over an invalid drop site, and yellow when it was not over any drop site:

*.validCursorIcon:          green
*.invalidCursorIcon:        red
*.noneCursorIcon:           yellow

Operation Icon

The operation icon is a cursor that indicates what operation is to happen when the drop is made. The default operation icons are the values of the XmScreen resources XmNdefaultMoveCursorIcon, XmNdefaultCopyCursorIcon, and XmNdefaultLinkCursorIcon.

A custom operation icon can be specified in the DragContext resource XmNoperationCursorIcon. The icon should be changed as the operation changes, using the drag callbacks. If this resource is NULL, not a bitmap, or not defined on the same screen as XmScreen, the default XmScreen icons are used.

The following illustration shows the default Copy, Link, and Move operation icons.

Figure 15-10. Operation Icons

Figure 15-10 
Operation Icons

If the operation in effect is XmDROP_NOOP, meaning that no operation is possible, then the operation icon is left blank, as shown in the following illustration. This condition also sets the dropSiteStatus to XmDROP_SITE_INVALID.

Figure 15-11. Copy and Noop Drag Icons

Figure 15-11 
Copy and Noop Drag Icons

Drag Icon Blending and Attachment

The client can specify which of the three icons to mix together to form the drag icon with the XmNblendModel DragContext resource:

XmBLEND_ALL  


Use the source icon, state icon, and operation icon. The hotspot comes from the state icon. This is the default value. The order listed is also the order of the blend.

XmBLEND_STATE_SOURCE  


Use only the source icon and state icon. The hotspot comes from the state icon.

XmBLEND_JUST_SOURCE  


Use only the source icon. The hotspot comes from the source icon.

XmBLEND_NONE  


Do not display any drag icon. The client handles all drag-over effects.

The XmNattachment DragIcon resource specifies where the state and operation icons will be placed on the source icon. The default placement is both the state and operation icons at the attachment point of the source icon, with the operation icon on top. The default value is XmATTACH_NORTH_WEST.

XmNoffsetX and XmNoffsetY are used to place the icon relative to the attachment point.

If the attachment point is XmATTACH_HOT, the state and operation icons are attached to the source icon at a point the same x and y distance from the upper left corner of the source icon as the pointer is from the upper left corner of the widget containing the source. This attachment style is particularly useful when the application makes a custom source icon that exactly reflects the source widget at the time the drag starts.

In the following illustration, the custom source icon is an outline of the scrollbar. When the drag was started, the pointer was on the slider. The operation and state icons are placed at the same location on the source icon.

Figure 15-12. An Attach_Hot Icon

Figure 15-12 
An Attach_Hot Icon

When the state or operation icon is blended with a source icon, a specified point of the icon's XmNpixmap is aligned with the upper left corner of the source icon. The resulting XmNpixmap is large enough to include both, and the resulting XmNmask has 1 bits wherever either the source icon or source mask did.

If a dynamic cursor style is being used, and the resulting blended cursor is too large for the screen, the blending is done with the XmScreen XmNdefaultSourceCursorIcon instead of the DragContext's XmNsourceCursorIcon. If it is still too large, it is clipped relative to the hotspot (that is, if the hotspot is at an edge, the other edge is clipped; if the hotspot is in the center, opposite edges are clipped equally).

Visual Style Notes

If XmNsourcePixmapIcon is used, the colormap used for rendering is that of the DragContext's reference widget.

If the DragContext XmNblendModel is XmBLEND_NONE, and the dynamic cursor style is in use, the application must use XChangeActivePointerGrab to change the cursor. If XmBLEND_NONE is specified, and the preregister cursor style is in use, the application can render the cursor directly onto the screen, saving and restoring the image underneath.

The cursor style can change as the pointer moves from window to window. An application can tell which style is in use by looking at the dragProtocolStyle field in the XmNtopLevelEnterCallback structure, or looking at the XmNdragInitiatorProtocolStyle Display resource in the case of XmDRAG_NONE or XmDRAG_DROP_ONLY.

The resolution and best cursor size can vary from screen to screen. This is why the default cursor icons are XmScreen resources. An application that wants its source cursor or pixmap to be screen dependent can look for changes in the screen field in the XmNtopLevelEnterCallback struct, and update the various icon DragContext resources appropriately.

Creating a Drag Icon

Any of the three parts of a drag icon can be customized: the source icon, the state icon, and the operation icon.

Use the XmCreateDragIcon function to create any of these parts. The XmNattachment resource is not used for the source icon. The other resources specify pixmap, size, and hotspot details. The DragContext XmNblendModel resource indicates which hotspot is used for the entire drag icon.

The following example from DNDDemo.c in Appendix B, "Drag and Drop Example Program," creates a source icon from a bitmap. The source icon is the palette and the state icon is the paintbrush. (Actually, the state icon is not shown when the drag starts, because the blend style is XmBLEND_JUST_SOURCE. It is shown here as if the blend style were XmBLEND_ALL.)

Figure 15-13. Custom Source Icon

Figure 15-13 
Custom Source Icon

The ColorRect function is called when a drag starts from one of the color rectangles in the lower portion of the window. Among its other duties, it establishes the drag icon from source bits from the DNDDraw.c file in Appendix B, "Drag and Drop Example Program."

    /* If the server will handle a large icon, create
       one */
    if (appInfo->maxCursorWidth >= ICON_WIDTH &&
        appInfo->maxCursorHeight >= ICON_HEIGHT) {
        source_bits = SOURCE_ICON_BITS;
        source_mask = SOURCE_ICON_MASK;
        state_bits = STATE_ICON_BITS;
        state_mask = STATE_ICON_MASK;
        width = ICON_WIDTH;
        height = ICON_HEIGHT;
    }
    else {
        /* If the server will handle a small icon, create
           one */
        source_bits = SMALL_SOURCE_ICON_BITS;
        source_mask = SMALL_SOURCE_ICON_MASK;
        state_bits = SMALL_STATE_ICON_BITS;
        state_mask = SMALL_STATE_ICON_MASK;
        width = SMALL_ICON_WIDTH;
        height = SMALL_ICON_HEIGHT;
    }
    /* Create the drag cursor icons */
    sourceIcon = GetDragIconFromBits(w, source_bits,
                   source_mask, width, height, background,
                   foreground);
    stateIcon = GetDragIconFromBits(w, state_bits,
                  state_mask, width, height, background,
                  foreground);
    /* Setup the arglist for the drag context that is
     * created at drag start */
    n = 0;
    .
    .
    .
    XtSetArg(args[n], XmNsourceCursorIcon, sourceIcon); n++;
    XtSetArg(args[n], XmNstateCursorIcon, stateIcon); n++;
    .
    .
    .
    /* start the drag.  This creates a drag context. */
    myDC = XmDragStart(w, event, args, n);
}

The GetDragIconFromBits function turns the bits into a bitmap.

static Widget GetDragIconFromBits(w, bits, mask, width,
                        height, background, foreground)
Widget w;
char *bits;
char *mask;
Dimension width;
Dimension height;
Pixel background;
Pixel foreground;
{
    Pixmap     icon, iconMask;
    Display    *display = XtDisplay(w);
    icon = XCreateBitmapFromData(display,
             DefaultRootWindow(display), bits, width,
             height);
    iconMask = XCreateBitmapFromData(display, 
                 DefaultRootWindow(display), mask, 
                 width, height);
    return(GetDragIcon(w, icon, iconMask, width, height, 
                       background, foreground));
}

The GetDragIcon function uses the bitmap created by the GetDragIconFromBits function to create a drag icon.

static Widget GetDragIcon(w, icon, iconMask, width, height, 
                          background, foreground)
Widget w;
Pixmap icon;
Pixmap iconMask;
Dimension width;
Dimension height;
Pixel background;
Pixel foreground;
{
    Widget  dragIcon;
    Arg     args[10];
    int     n = 0;
    XtSetArg(args[n], XmNhotX, ICON_X_HOT); n++;
    XtSetArg(args[n], XmNhotY, ICON_Y_HOT); n++;
    XtSetArg(args[n], XmNwidth, width); n++;
    XtSetArg(args[n], XmNheight, height); n++;
    XtSetArg(args[n], XmNmaxWidth, appInfo->maxCursorWidth);
      n++;
    XtSetArg(args[n], XmNmaxHeight, appInfo->maxCursorHeight);
      n++;
    XtSetArg(args[n], XmNbackground, background); n++;
    XtSetArg(args[n], XmNforeground, foreground); n++;
    XtSetArg(args[n], XmNpixmap, icon); n++;
    XtSetArg(args[n], XmNmask, iconMask); n++;
    dragIcon = XmCreateDragIcon(w, "dragIcon", args, n);
    return(dragIcon);
}

Drag Callbacks

Callbacks notify the initiator of how the drag is proceeding. The receiver's XmNdragProc (if any) is first notified of the action and given a chance to update the operation, operations, and dropSiteStatus fields in its callback structure. The new values are available to the initiator's drag callback in the appropriate callback structure.

These drag callbacks are all optional. They enable the initiator to monitor the progress of the drag and manage its visual effects accordingly. Otherwise, the toolkit on the initiator side handles the drag-over effects.

XmNdragMotionCallback  


Called when the drag icon is in motion

XmNoperationChangedCallback  


Called when the user requests a different operation be performed on the drop than was previously in effect

XmNdropSiteEnterCallback  


Called when the drag icon enters a drop site

XmNdropSiteLeaveCallback  


Called when the drag icon leaves a drop site

XmNtopLevelEnterCallback  


Called when the drag icon enters a top-level window or root window (when changing screens)

XmNtopLevelLeaveCallback  


Called when the drag icon leaves a top-level window or root window (when changing screens)

Callback structures for these routines contain information about the drag. The callback structures for XmNdragMotionCallback, XmNoperationChangedCallback, and XmNdropSiteEnterCallback contain the operations, operation, and dropSiteStatus fields (among others), which are initialized by the toolkit before the callback is called.

The operations field lists all operations possible for a drop on the current site, whether the site is registered as a DropSite or not. The toolkit initializes the operations field as follows:

  • If the receiver's XmNdragProc was called, the value of operations is the list of operations common to the value of the XmNdragProc's operations field at the end of XmNdragProc and the DropSite's XmNdropSiteOperations list.

  • If the XmNdragProc routine was not called but the user selected an operation, operations is set to that operation if it is in the XmNdragOperations list. If it is not in the list, operations is set to XmDROP_NOOP.

  • Otherwise, the operations field is initialized to the list in the DragContext's XmNdragOperations resource.

The operation field shows the operation that will occur if a drop happens at the current cursor location. It is initialized as follows:

  • If the receiver's XmNdragProc was called, operation is initialized to the value of operation at the end of the XmNdragProc.

  • If the XmNdragProc routine was not called but the pointer is in or entering an active drop site, the toolkit initializes operation by taking the following steps, in order of precedence from highest to lowest:

    1. If Move is in both the operations field and the DropSite's XmNdropSiteOperations list, operation is set to XmDROP_MOVE.

    2. If Copy is in both the operations field and the DropSite's XmNdropSiteOperations list, operation is set to XmDROP_COPY.

    3. If Link is in both the operations field and the DropSite's XmNdropSiteOperations list, operation is set to XmDROP_LINK.

    4. Otherwise, operation is set to XmDROP_NOOP.

  • Otherwise, the toolkit initializes operation by taking the following steps, in order of precedence from highest to lowest:

    1. If Move is in the operations field, operation is set to XmDROP_MOVE.

    2. If Copy is in the operations field, operation is set to XmDROP_COPY.

    3. If Link is in the operations field, operation is set to XmDROP_LINK.

    4. Otherwise, operation is set to XmDROP_NOOP.

The dropSiteStatus field in the callback structure indicates if the drag icon is over a valid drop site, an invalid drop site, or no drop site. The callback procedure can use this information to display the appropriate drag-over visuals. The toolkit initializes the dropSiteStatus field as follows:

  • If the pointer is over an active drop site:

    • If the receiver's XmNdragProc was called, dropSiteStatus is initialized to the value of dropSiteStatus at the end of the XmNdragProc procedure.

    • If the XmNdragProc routine was not called but the initiator and receiver have at least one target and one operation in common, dropSiteStatus is initialized to XmDROP_SITE_VALID.

    • Otherwise, dropSiteStatus is initialized to XmDROP_SITE_INVALID.

  • If the pointer is not over an active drop site, dropSiteStatus is initialized to XmNO_DROP_SITE.

  • If the operation field is XmDROP_NOOP, dropSiteStatus is initialized to XmDROP_SITE_INVALID.

If the application has not stored the DragContext ID in a global location, these callbacks can find the DragContext ID by passing the timeStamp field from the callback structure to the XmGetDragContext function.

This example shows a callback that is called when a new drop site is entered. It checks the validity of the drop site, and uses one of three custom source icons, depending on the status.

static void EnterCB(w, client_data, call_data)
Widget          w;
XtPointer       client_data, call_data;
{
   XmDragContext                   dc;
   XmDropSiteEnterCallback         EnterData;
   Cardinal                        n;
   Arg                             args[MAX_ARGS];
   dc = (XmDragContext)w;
   EnterData = (XmDropSiteEnterCallback )call_data;
   n = 0;
   if (EnterData->dropSiteStatus == XmVALID_DROP_SITE) {
      XtSetArg(args[n], XmNsourceCursorIcon,
                 GetValidIcon(w)); n++;
      XtSetValues(dc, args, n);
      }
   if (EnterData->dropSiteStatus == XmINVALID_DROP_SITE) {
      XtSetArg(args[n], XmNsourceCursorIcon,
                 GetInvalidIcon(w)); n++;
      XtSetValues(dc, args, n);
      }
   if (EnterData->dropSiteStatus == XmNO_DROP_SITE) {
      XtSetArg(args[n], XmNsourceCursorIcon,
                 GetNeutralIcon(w)); n++;
      XtSetValues(dc, args, n);
      }
}

If a drag callback is desired, it is added to the DragContext's callback resources.The following example adds a callback named EnterCB that is performed when the pointer enters an active drop site:

Widget       dc;
   dc = XmDragStart(w, event, args, n);
   XtAddCallback(dc, XmNdropSiteEnterCallback, EnterCB,
                   NULL);

Getting Data about the Current Drop Site

The initiator can find information about the current drop site with the XmDropSiteRetrieve function. It must pass in the DragContext, so that the toolkit knows what drop site the request is for. The initiator can find the value of any drop site resource except the callback routines

The following example gets the number and list of import targets for a drop site. The example shows a drop site enter callback, but it could be in any of the initiator's drag callbacks.

XmDropSiteEnterCallback      DragData;
     .
     .
     .
n = 0;
XtSetArg(args[n], XmNimportTargets, &importTargets); n++;
XtSetArg(args[n], XmNnumImportTargets,
           &numImportTargets); n++;
XmDropSiteRetrieve(DragData->DragContext, args, n);

Cancelling the Drag

The drag in progress can be cancelled in either of two ways. Both ways are treated the same by the toolkit.

  • The user can press KCancel.

  • The initiator can call the XmDragCancel function if it decides the drag should not continue for some reason.

The initiator is notified of the cancel by the XmNdropStartCallback with a dropAction field value of XmDROP_CANCEL.

The receiver is notified by a XmCR_DROP_SITE_LEAVE_MESSAGE message. This message is processed by the XmNdragProc in the dynamic protocol mode. This allows any drag-under effects to be undone.

Drop Receiver Responsibilities for Dropping

When the user releases the drag to start a drop, the toolkit sends a message to the receiver. The receiver's XmNdropProc routine processes it by checking that the proposed targets and actions are valid, and updates the status and operations fields accordingly. This information is sent back to the initiator's XmNdropStartCallback routine.

The application receiving a drop must

  • Have registered an XmNdropProc routine to be processed when a drop is made on the site. This is done as part of registering a widget as a drop site.

  • Make a list of transfer requests. If the drop is cancelled, the number of transfer requests is set to zero.

  • Register a DropTransfer XmNtransferProc to process transfers from the initiator if the number of transfers is not zero.

  • Call XmDropTransferStart at least once, to either cancel the drop or start the transfer process.

The receiving application may also do the following:

  • Provide drop site Help information

  • Cancel a drop

XmNdropProc

When a drop occurs (except for a Cancel), a message is sent from the toolkit on the initiator side to the receiver, and the receiver's XmNdropProc is called. Fields in its callback structure provide information about the drop to the receiver.

The operations, operation, and dropSiteStatus fields are initialized by the toolkit in a similar manner to that described for the receiver's XmNdragProc earlier in this chapter.

The XmNdropProc routine can update the operations, operation, and dropSiteStatus fields further. The final values are available to the initiator in its drop callback structures.

The dropAction field indicates if a normal drop is requested, or if the user requested help. For information about processing a help request, refer to Section 15.6.4, "Providing Help."

If the receiver takes too long before ending the XmNdropProc routine, the toolkit will cause the drag to time out. Therefore, if the receiver needs to do any processing before the transfer other than verifying that a transfer can take place, it should start a new process and end the XmNdropProc routine.

Either the XmNdropProc routine or one of its subprocedures must start a transfer by calling XmDropTransferStart. The initiator waits for a transfer request to finish its part in the drop. If a drop is not possible, the drop is cancelled as described below. If a drop is possible, the XmNdropProc routine provides the appropriate details to start the transfer.

The XmNdropProc procedure creates a list of DropTransfer entries containing target and client-specific information for each transfer desired. There is a separate entry for each data-target type combination. For example, if the data is desired in both TEXT and COMPOUND_TEXT forms, there would be two entries on the list. This list and the number of items in the list are used by XmDropTransferStart to start the transfer.

The receiver establishes the values of the DropTransfer resources before calling XmDropTransferStart. Following are the DropTransfer resources:

XmNdropTransfers  


The list of drag transfer entries.

XmNincremental  


Whether to use the incremental transfer mechanism.

XmNnumDropTransfers  


The number of transfer entries in the list. This number is decremented each time a transfer is made.

XmNtransferProc  


The procedure to process transferred information. This procedure is an XtSelectionCallbackProc procedure. For more information about XtSelectionCallbackProc, see X Toolkit Intrinsics—C Language Interface.

XmNtransferStatus  


Whether the transfer failed or not. The default value is XmTRANSFER_SUCCESS.

The following example from DNDlabel.c in "A Simple Drag Receiver" creates a transfer request list of one transfer entry, asking that the initiator send its data in compound text format. Copy is the only action it accepts; the rest result in a cancelled drop. The DropTransferCallback routine receives and processes the data from the initiator.

static void HandleDrop(w, client_data, call_data)
Widget          w;
XtPointer       client_data, call_data;
{
   XmDropProcCallback      DropData;
   XmDropTransferEntryRec  transferEntries[2];
   XmDropTransferEntry     transferList;
   Arg                     args[MAX_ARGS];
   int                     n;
   DropData = (XmDropProcCallback)call_data;
   /* set the transfer resources */
   n = 0; 
   /* if the action is Help, or the operation is not Copy, 
    *cancel the drop */
   if ((DropData->dropAction != XmDROP) ||
       (DropData->operation != XmDROP_COPY)) 
      XtSetArg(args[n], XmNtransferStatus,
                 XmTRANSFER_FAILURE); n++;
   else {
      /* the drop can continue.  Establish the transfer list
       * and start the transfer */
      transferEntries[0].target = COMPOUND_TEXT;
      transferEntries[0].client_data = (XtPointer)w;
      transferList = transferEntries;
      XtSetArg(args[n], XmNdropTransfers, transferList); n++;
      XtSetArg(args[n], XmNnumDropTransfers, 1); n++;
      XtSetArg(args[n], XmNtransferProc,
                 DropTransferCallback); n++;
      }
   /* start the transfer or cancel */ 
   XmDropTransferStart(DropData->dragContext, args, n);
}

If the program could accept transfers in more than one target type, for example, TEXT and COMPOUND_TEXT, then a separate transfer entry is needed for each request:

transferEntries[0].target = COMPOUND_TEXT;
transferEntries[1].target = TEXT;
     .
     .
     .
XtSetArg(args[n], XmNnumDropTransfers, 2); n++;

XmDropTransfer

The toolkit on the receiver side is in charge of the transfer procedure. Information about the transfer is stored in a DropTransfer widget, which is created by the XmDropTransferStart routine.

Before calling XmDropTransferStart, the receiver stores a list of DropTransfer transfer entries in the XmNdropTransfers resource. Each entry contains target and client-specific information for each transfer desired. It also registers a procedure to receive transfers from the initiator in the XmNtransferProc resource. These resources, along with the other DropTransfer resources, are used by the XmDropTransferStart function.

The toolkit processes the items on the list, one at a time, decrementing XmNnumDropTransfers each time. When the XmNnumDropTransfers value is zero, the drop is finished. The toolkit on the receiver side sends a message to the initiator, whose XmNdropFinishCallback is then called.

If XmNincremental is True, the Xt selection incremental transfer protocol is used between the toolkit and the receiver, regardless of what the initiator sent. Refer to the Xt documentation for details of how to use incremental transfer. If the value is False, the transfer between the toolkit and the receiver is made in one pass, regardless of how the initiator sent it.

The XmNtransferProc routine receives each transfer from the initiator. If more than one target type is acceptable to the receiver, this procedure needs to check which target type was used in this transfer, and process the transferred data accordingly.

The XmNtransferProc routine can examine and update the DropTransfer resources during the transfer with XtGetValues and XtSetValues.

The XmDropTransferAdd routine is used to add to the transfer list after the transfer has begun. For example, this routine is used when a Move operation is performed, to add a new transfer entry record telling the initiator to delete the data. It can be used in other situations where the entire transfer list is not known when XmDropTransferStart is called.

If there are problems with the drop, it can be cancelled as described later in the chapter.

The following example from DNDlabel.c in "A Simple Drag Receiver" receives compound string data from the initiator, and uses it to replace the label of the Label widget.

static void TransferProc(w, closure, seltype, type,
                           value, length, format) 
Widget           w;
XtPointer        closure;
Atom             *seltype;
Atom             *type;
XtPointer        value;
unsigned long    *length;
int              format;
{
   int         n;
   Arg         args[MAX_ARGS];
   /* information from the drag initiator is passed in
    * compound  text format.  Convert it to compound string
    * and replace the Label label. */
   if (*type = COMPOUND_TEXT) {
      n = 0;
      XtSetArg(args[n], XmNlabelString,
                 XmCvtCTToXmString(value)); 
      n++;
      XtSetValues(closure, args, n);
      } 
}

If the program is able to handle more than one target type, this routine needs to check for them all. For instance:

   if (*type = COMPOUND_TEXT) {
      /* code to change the label to the compound text
         passed */
      }
   else if (*type = TEXT) {
      /* code to change the label to the text passed */
      }

Processing Each Operation

The XmNtransferProc routine must be able to process the data from the initiator correctly for each operation listed in the DropSite XmNdropSiteOperations resource:

  • If the operation is Copy, the value field contains a pointer to the data from the initiator. It is used to assign the value to some element in the receiver's program. The example in the previous section shows a Copy in effect. When the transfer is finished, both the initiator and receiver have the data in each of their applications.

  • If the operation is Move, data is first copied to the receiver, then deleted from the initiator. It is important that the initiator not delete the data before the receiver has it. Therefore, a Move is a two-step process:

    • The first transfer is processed by the initiator like a Copy. It returns a pointer to the data in the value field.

    • When the XmNtransferProc routine has the data, it uses XmDropTransferAdd to make a new transfer entry for that data, setting the target to DELETE. The initiator will not delete the data until the receiver has issued this second transfer request.

    At the end of the transfer, the receiver has the only copy of the data.

  • If the operation is Link, the pointer is used to link an element in the receiver to the data. At the end of the operation, there is only one copy of the data, belonging to the initiator, but both applications have access to it.

Cancelling a Drop

A drop can be cancelled only by the receiver, from the XmNdropProc procedure or any subroutine it calls, such as XmNtransferProc. To cancel a drop:

  • Set the XmNnumDropTransfers DropTransfer resource to zero. This tells the toolkit that there are no more transfers to make and the drop is complete.

  • Set the XmNtransferStatus to XmTRANSFER_FAILURE. This information is passed to the initiator in the XmNdropFinishedCallback structure.

  • Call the XmDropTransferStart function from the XmNdropProc routine, or exit a subroutine called by the XmNdropProc procedure.

The transfer will be cancelled at the next transfer request. The drop is over, and the initiator's XmNdropFinishCallback and XmNdragDropFinishCallback routines are called.

The following example is from a program's XmNdropProc routine:

XtSetArg(args[n], XmNtransferStatus,
           XmTRANSFER_FAILURE); n++;
XtSetArg(args[n], XmNnumDropTransfers, 0); n++;
XmDropTransferStart(DropData->dragContext, args, n);

Providing Help

It might not always be obvious to the user what the result of dropping a particular source on a drop site might be. The user can request more information about the drop site by pressing KHelp while the drag icon is over the drop site.

When the user presses KHelp, the receiver's XmNdropProc routine is called with a value of XmDROP_HELP in the dropAction field of its callback structure. If the receiver supports help, it should post a dialog, providing information about the type of drop this site expects, and what it will do when a successful drop occurs.

The receiver should then exit the XmNdropProc routine without waiting for a response from the user. When the XmNdropProc routine has finished, the initiator's XmNdropStartCallback is called with a dropAction of XmDROP_HELP if the initiator has registered that callback. The initiator is not expected to do anything at this point, but it could provide special processing such as changing the drag icon.

Typically, the help dialog allows the user the opportunity to continue the drop or to cancel the drop. If more than one operation is possible, the dialog should explain the consequences of each operation and let the user select one. The dialog procedure may change the operation based on the user's selection:

  • If the user indicates that the drop should be cancelled, the receiver's help procedure should cancel the drop by requesting no transfers, as described in the previous section, Section 15.6.3, "Cancelling a Drop."

  • If the user indicates that the drop should continue, the help procedure should call XmDropTransferStart to begin the transfer of information from the initiator.

In either case, the help procedure must call XmDropTranferStart before it ends to either start the transfers or notify the initiator that no transfers will be requested.

The receiver may want to issue help information if a drop is considered invalid, even if the user has not requested it. If so, the receiver's XmNdropProc sets the dropAction field to XmDROP_HELP, and displays the help dialog as if help had been requested.

The following example taken from DNDDemo.c in Appendix B, "Drag and Drop Example Program," shows how the help dialog shown in the illustration was created.

Figure 15-14. Help Dialog

Figure 15-14 
Help Dialog

The XmDropProc DropProcCallback routine checks if the drop is normal or if there is a request for help.

static void DropProcCallback(w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
    XmDropProcCallbackStruct *cb = 
                 (XmDropProcCallbackStruct *)call;
    if (appInfo->highlightRect != NULL)
        RectUnhighlight(w);
    if (cb->dropAction != XmDROP_HELP)
        HandleDrop(w, call);
    else
        HandleHelp(w, call);
}

The HandleHelp routine displays the help dialog. The text presented in the dialog depends on the drop site and the operation. Callback routines are registered to be performed when either of the dialog pushbuttons is pressed.

static void HandleHelp(w, call)
Widget w;
XtPointer call;
{
    XmDropProcCallbackStruct *cb =
                     (XmDropProcCallbackStruct *)call;
    static XmDropProcCallbackStruct client;
    Boolean                 rectFound, bgFound, pixFound;
    XmString                helpStr;
    RectPtr                 rect;
    Arg                     args[5];
    XmString                tempStr, buttonArray[2];
    int                     n = 0;
    /* the drop is valid until it is determined invalid */
    cb->dropSiteStatus = XmVALID_DROP_SITE;
    /* if we haven't created a help dialog, create one now */
    if (helpDialog == NULL) {
        XtSetArg(args[n], XmNdialogStyle, 
             XmDIALOG_FULL_APPLICATION_MODAL); n++;
        XtSetArg(args[n], XmNtitle, "Drop Help"); n++;
        helpDialog = XmCreateMessageDialog(topLevel, "Help", 
             args, n);
        n = 0;
        buttonArray[0] = XmStringCreateSimple("Move");
        buttonArray[1] = XmStringCreateSimple("Copy");
        XtSetArg(args[n], XmNbuttons, buttonArray); n++;
        XtSetArg(args[n], XmNbuttonCount, 2); n++;
        XtSetArg(args[n], XmNbuttonSet, 0); n++;
        XtSetArg(args[n], XmNsimpleCallback,
                   ChangeOperation); n++;
        tempStr = XmStringCreateSimple("Operations:");
        XtSetArg(args[n], XmNoptionLabel, tempStr); n++;
        helpMenu = XmCreateSimpleOptionMenu(helpDialog,
                        "helpMenu", args, n);
        XmStringFree(tempStr);
        XmStringFree(buttonArray[0]);
        XmStringFree(buttonArray[1]);
        XtAddCallback(helpDialog, XmNokCallback,
             (XtCallbackProc) HandleOK, (XtPointer) &client);
        XtAddCallback(helpDialog, XmNcancelCallback,
             (XtCallbackProc) CancelDrop,
             (XtPointer) &client);
        XtUnmanageChild(XmMessageBoxGetChild(helpDialog, 
             XmDIALOG_HELP_BUTTON));
        XtRealizeWidget(helpDialog);
    }
    /* pass the necessary callback information along */
    client.dragContext = cb->dragContext;
    client.x = cb->x;
    client.y = cb->y;
    client.dropSiteStatus = cb->dropSiteStatus;
    client.operation = cb->operation;
    client.operations = cb->operations;
    /* find the valid targets */
    CheckTargets(cb->dragContext, XtDisplay(w),
                   &rectFound, &bgFound, &pixFound);
    /* determine the appropriate help message */
    if (rectFound) {
        if (cb->operations == XmDROP_MOVE | XmDROP_COPY) {
            XtManageChild(helpMenu);
            helpStr = XmStringCreateLtoR(HELP_MSG4, 
                 XmFONTLIST_DEFAULT_TAG);
            XtManageChild(XmMessageBoxGetChild(helpDialog, 
                 XmDIALOG_OK_BUTTON));
        }
        else if (cb->operation == XmDROP_MOVE) {
            XtUnmanageChild(helpMenu);
            helpStr = XmStringCreateLtoR(HELP_MSG2, 
                 XmFONTLIST_DEFAULT_TAG);
            XtManageChild(XmMessageBoxGetChild(helpDialog, 
                 XmDIALOG_OK_BUTTON));
        }
        else if (cb->operation == XmDROP_COPY) {
            XtUnmanageChild(helpMenu);
            helpStr = XmStringCreateLtoR(HELP_MSG3, 
                 XmFONTLIST_DEFAULT_TAG);
            XtManageChild(XmMessageBoxGetChild(helpDialog, 
                 XmDIALOG_OK_BUTTON));
        }
    }
    else if (bgFound ||
             pixFound && cb->operation == XmDROP_COPY) {
        XtUnmanageChild(helpMenu);
        rect = RectFind(cb->x, cb->y);
        if (rect) {
            helpStr = XmStringCreateLtoR(HELP_MSG1, 
                 XmFONTLIST_DEFAULT_TAG);
            XtManageChild(XmMessageBoxGetChild(helpDialog, 
                 XmDIALOG_OK_BUTTON));
        }
        else {
            helpStr = XmStringCreateLtoR(HELP_MSG5, 
                 XmFONTLIST_DEFAULT_TAG);
            XtUnmanageChild(XmMessageBoxGetChild(helpDialog, 
                 XmDIALOG_OK_BUTTON));
        }
    }
    else {
        XtUnmanageChild(helpMenu);
        helpStr = XmStringCreateLtoR(HELP_MSG5, 
             XmFONTLIST_DEFAULT_TAG);
        XtUnmanageChild(XmMessageBoxGetChild(helpDialog, 
             XmDIALOG_OK_BUTTON));
    }
    /* set the help message into the dialog */
    XtSetArg(args[0], XmNmessageString, helpStr);
    XtSetValues(helpDialog, args, 1);
    /* Free the XmString */
    XmStringFree(helpStr);
    /* map the help dialog */
    XtManageChild(helpDialog);
}

The HandleOK callback routine is performed when the user selects the OK button. It allows the drop to continue normally by calling the HandleDrop routine.

static void HandleOK(w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
    XmDropProcCallbackStruct *cb =
                (XmDropProcCallbackStruct *)client;
    cb->operation = appInfo->operation;
    HandleDrop(w, (XtPointer) cb);
}

The CancelDrop callback routine is performed when the user selects the Cancel button. It cancels the drop by calling XmDropTransferStart with indicators that the drop failed.

static void CancelDrop(w, client, call)
Widget w;
XtPointer client;
XtPointer call;
{
    XmDropProcCallbackStruct *cb =
                       (XmDropProcCallbackStruct *)client;
    Arg                args[2];
    /* On help, we need to cancel the drop transfer */
    XtSetArg(args[0], XmNtransferStatus,
               XmTRANSFER_FAILURE);
    XtSetArg(args[1], XmNnumDropTransfers, 0);
    /* we need to start the drop transfer to cancel
       the transfer */
    XmDropTransferStart(cb->dragContext, args, 2);
}

Drag Initiator Responsibilities for Dropping

The drag initiator

  • Registers an XmNconvertProc procedure to format data and send the formatted data to the receiver.

  • Optionally, registers an XmNdropStartCallback to be performed at the drop.

  • Optionally, registers an XmNdropFinishCallback to be performed after the drop and transfer have finished.

  • Optionally, registers an XmNdragDropFinishCallback to be performed after the entire drag and drop transaction has finished.

XmNdropStartCallback

The receiver's XmNdropProc routine receives the drop message first if the drop occurred over a widget that was registered as a drop site. It verifies that a drop is possible, and updates fields in its callback structure. These fields become available to the initiator in its XmNdropStartCallback callback structure. The initiator can perform any actions necessary before the information is transferred, for example, providing a new drag icon.

The toolkit initializes the operation, operations, and dropSiteStatus fields as described in Section 15.5.5, "Drag Callbacks," with one difference: the initialization for the drag callbacks uses the values at the end of the receiver's XmNdragProc, while the initialization for the drop callbacks uses the values at the end of the receiver's XmNdropProc.

The dropAction field indicates the action that the receiver has taken. XmDROP shows that a normal drop is in progress. XmDROP_CANCEL shows that the receiver has cancelled the drop. If the action is XmDROP_HELP, the initiator is not expected to do anything, although this callback provides the opportunity to do so if desired (for example, changing the drag icon to reflect the Help request).

This procedure will not know the resolution of the help dialog. However, if the user chooses to continue, the initiator's XmNconvertProc routine is called as part of the transfer process, and if the user chooses to cancel, the receiver's XmNdropFinishCallback is called with a dropAction of XmDROP_CANCEL.

Dealing with Requests for Transfer

The drag initiator must register a callback to process transfers in the XmNconvertProc DragContext resource. This routine is called when the receiver client invokes XmDropTransferStart. Before calling XmDropTransferStart, the receiver makes a list of the target formats it wants.

The initiator's XmNconvertProc callback routine processes transfer requests from the receiver. The routine should be able to return information about each object being dragged in each possible target format for that item.

If the DropTransfer XmNincremental resource is True, information is transferred between the initiator and the toolkit using the Xt selection incremental protocol. If the value is False, the information is transferred between the initiator and the toolkit in one pass. The initiator and receiver need not be using the same incremental or nonincremental protocol.

The XmNconvertProc routine is called for each target type desired by the receiver, a single target type for each request. The XmNconvertProc routine should be able to perform any of the operations listed in the DragContext's XmNdragOperations resource on data in any of the target types listed in the XmNexportTargets resource:

  • If the operation is Copy or Link, the XmNconvertProc returns a pointer to the data. The receiver will use this pointer to copy this data into its own storage, or establish a link using this pointer.

  • If the operation is Move, the first transfer request has a normal target type. The XmNconvertProc routine should return a pointer to the data, as it would for a Copy.

    A second transfer request for the data has a target type of DELETE. The receiver does not issue this request until it has received the data and handled it appropriately (such as storing it in a file). Only then should the initiator delete the data.

In the following example from DNDscroll.c in "A Simple Drag Source" the routine returns the value of the scrollbar slider in only one target type, compound text. This information is passed to the receiver's XmNtransferProc routine. This routine is called once for each item in the receiver's XmNdropTransfers list. Copy is the only operation allowed by the application, so this routine need not process any delete requests from the receiver.

static
Boolean DragConvertProc(w, selection, target, typeRtn, 
                        valueRtn, lengthRtn, formatRtn, 
                        max_lengthRtn, client_data, 
                        request_id)
Widget              w;
Atom                *selection;
Atom                *target;
Atom                *typeRtn;
XtPointer           *valueRtn;
unsigned long       *lengthRtn;
int                 *formatRtn;
unsigned long       *max_lengthRtn;
XtPointer           client_data;
XtRequestId         *request_id;
{
   XmString    cstring;
   static char tmpstring[100];
   int         *value;
   int         n;
   Arg         args[MAX_ARGS];
   char          *ctext;
   char        *passtext;
   /* this routine processes only compound text */
   if (*target != COMPOUND_TEXT)
      return(False);
   /* get the value of the scrollbar slider */
   n = 0;
   XtSetArg(args[n], XmNvalue, &value); n++;
   XtGetValues(scrollbar, args, n);
   /* convert the slider value to compound text */
   sprintf(tmpstring, "%d", value);
   cstring = XmStringCreateLocalized(tmpstring);
   ctext = XmCvtXmStringToCT(cstring);
   passtext = XtMalloc(strlen(ctext)+1);
   memcpy(passtext, ctext, strlen(ctext)+1);
   /* Format the value for transfer.  Convert the value
    * from compound string to compound text for the
    * transfer */
   *typeRtn = COMPOUND_TEXT;
   *valueRtn = (XtPointer) passtext;
   *lengthRtn = strlen(passtext);
   *formatRtn = 8;
   return(True);
}

If the DNDscroll.c program in "A Simple Drag Source" processed more than one target, such as text and compound text, then this routine would have to handle both types. For example:

if (*target = COMPOUND_TEXT) {
   /* processing to convert the slider to compound string
      format */
   }
else if (*target = TEXT) {
   /* processing to convert the slider to text format */
   }
else
   return(False);

XmNdropFinishCallback

The XmNdropFinishCallback is called when the receiver's XmNtransferProc routine has finished processing all the transfers desired by the receiver.

The completionStatus field indicates whether the entire drop was successful or not.

The operations, operation, dropSiteStatus, and dropAction fields are initialized as for the XmNdropStartCallback procedure.

XmNdragDropFinishCallback

The XmdragDropFinishCallback routine is performed when the complete drag and drop transaction has finished. This routine is called immediately after the initiator's XmNdropFinishCallback has finished. The initiator frees any remaining structures it has allocated during the drag.

The following sample code destroys any cursor icons that were created during the drag:

static void DnDFinishCallback(w, client_data, call_data)
Widget          w;
XtPointer       client_data, call_data;
{
   XmDragContext    dc;
   Widget           source_icon, state_icon, op_icon;
   Arg              args[MAX_ARGS];
   int              n;
   dc = (XmDragContext)w;
   source_icon = state_icon = op_icon = NULL;
   n = 0;
   XtSetArg(args[n], XmNsourceCursorIcon,
              &source_icon); n++;
   XtSetArg(args[n], XmNstateCursorIcon,
              &state_icon); n++;
   XtSetArg(args[n], XmNoperationCursorIcon,
              &op_icon); n++;
   XtGetValues(dc, args, n);
   if (source_icon != NULL)
      XtDestroyWidget(source_icon);
      if (state_icon != NULL)
         XtDestroyWidget(state_icon);
      if (op_icon != NULL)
         XtDestroyWidget(op_icon);
}