This section illustrates how an application can use UTM to supplement the list of targets that an XmText widget can convert.
Here is the scenario. The application instantiates two XmText widgets. We will refer to one of these widgets as TextSource and the other as TextDestination. TextDestination has a rather unusual requirement; namely, it expects that any text transferred to it must not contain any lowercase letters. In other words, the source of a data transfer is responsible for converting any lowercase letters to uppercase before transferring the data.
The standard XmText widget does not provide any targets that can do this conversion. Therefore, the application must temporarily extend XmText to handle a new target named MYTEXT. The application must do the following:
The application will support three different forms of data transfer: primary, clipboard, and drag and drop.
The following subsections explains how the application accomplishes its goals.
The application must associate an XmNconvertCallback procedure with widget TextSource. The following code does just that:
XtAddCallback(TextSource, XmNconvertCallback, ConvertCallback, NULL);
The application must provide an XmNconvertCallback procedure named ConvertCallback. This procedure must handle the following requests:
Following are the relevant parts of the ConvertCallback routine:
void ConvertCallback(Widget w, XtPointer ignore, XtPointer call_data) { XmConvertCallbackStruct *ccs = (XmConvertCallbackStruct *)call_data; char *selected_text; char *copy_of_selected_text; Atom TARGETS = XInternAtom(XtDisplay(w), "TARGETS", False); Atom _MOTIF_CLIPBOARD_TARGETS = XInternAtom(XtDisplay(w), "_MOTIF_CLIPBOARD_TARGETS", False); Atom MYTEXT = XInternAtom(XtDisplay(w), "MYTEXT", False); int n=0; Atom *targs = (Atom *)XtMalloc(sizeof(Atom) * 2); if ((ccs->target == TARGETS) || (ccs->target == _MOTIF_CLIPBOARD_TARGETS)) { /* Use targs to hold a list of targets that my application can convert. This list will be merged with the targets that the XmText widget can convert. */ targs[n] = MYTEXT; n++; ccs->value = (XtPointer) targs; ccs->type = XA_ATOM; ccs->format = 32; ccs->length = n; ccs->status = XmCONVERT_MERGE; } else if (ccs->target == MYTEXT) { /* Get the selection. */ selected_text = XmTextGetSelection(w); copy_of_selected_text = selected_text; /* Convert any lowercase letters in the selection to uppercase. */ while (*selected_text++) { if (islower(*selected_text)) *selected_text = toupper(*selected_text); } /* Place the converted text into the XmConvertCallbackStruct. */ ccs->value = copy_of_selected_text; ccs->type = ccs->target; ccs->format = 8; ccs->length = strlen(copy_of_selected_text); ccs->status = XmCONVERT_DONE; } }
Notice how ConvertCallback sets the value of the status member of the XmConvertCallbackStruct. For example, when the destination requests TARGETS or _MOTIF_CLIPBOARD_TARGETS, ConvertCallback sets status to XmCONVERT_MERGE. This status tells UTM to call the convertProc trait method of XmText. This trait method will add MYTEXT to the list of supported targets. As another example, consider that ConvertCallback is taking full responsibility to convert the selection to MYTEXT. For this case, ConvertCallback sets status to XmCONVERT_DONE. This status tells UTM not to bother calling the convertProc trait method of XmText. Finally, consider what happens when ConvertCallback is asked to convert a target other than TARGETS, _MOTIF_CLIPBOARD_TARGETS, or MYTEXT. In this case, the status member will retain its default value of XmCONVERT_DEFAULT. This status tells UTM to call the convertProc trait method of XmText, and that the trait method will be reponsible for converting the target.
The application must associate an XmNdestinationCallback procedure with widget TextDestination. The following code does just that:
XtAddCallback(TextDestination, XmNdestinationCallback, DestinationCallback, NULL);
The application must provide an XmNdestinationCallback procedure named DestinationCallback. We have implemented this procedure in a very basic fashion. This procedure merely requests a list of the targets that the source widget supports:
void DestinationCallback(Widget w, XtPointer ignore, XtPointer call_data) { XmDestinationCallbackStruct *dcs = (XmDestinationCallbackStruct *)call_data; Atom TARGETS = XInternAtom(XtDisplay(w), "TARGETS", False); /* Ask the source to return a list of all the targets supported. */ XmTransferValue(dcs->transfer_id, TARGETS, (XtCallbackProc)TransferProc, NULL, XtLastTimestampProcessed() ); }
When the source returns the list of TARGETS, UTM calls the application's transfer procedure. The transfer procedure, TransferProc in this case, is named by the third argument to XmTransferValue.
Here is the algorithm for the transfer procedure. The transfer procedure must examine the returned list of targets to see if the list contains MYTEXT. If it does, ask the source to convert the selection to MYTEXT. When the source completes the conversion, the transfer procedure must paste the converted text into the TextDestination widget.
Following is the code for TransferProc:
void TransferProc(Widget w, XtPointer ignore, XtPointer call_data) { XmSelectionCallbackStruct *scs = (XmSelectionCallbackStruct *) call_data; Atom TARGETS = XInternAtom(XtDisplay(w), "TARGETS", False); Atom MYTEXT = XInternAtom(XtDisplay(w), "MYTEXT", False); Atom *targets = (Atom *)scs->value; int MYTEXT_is_supported = 0; unsigned long n; if ((scs->target == TARGETS) && (scs->type == XA_ATOM)) { for (n=0; n<=scs->length; n++) { /* Look through list of returned TARGETS to see if MYTEXT is there. */ if (targets[n] == MYTEXT) MYTEXT_is_supported = 1; } if (MYTEXT_is_supported) /* Now ask the source widget to convert the selection to MYTEXT. */ XmTransferValue(scs->transfer_id, MYTEXT, (XtCallbackProc)TransferProc, NULL, XtLastTimestampProcessed() ); } if ((scs->target == MYTEXT)) { XmTextPosition current_insertion_position; /* Source widget has converted MYTEXT, paste it into the destination widget. */ current_insertion_position = XmTextGetInsertionPosition(w); XmTextInsert(w, current_insertion_position, (char *)scs->value); XmTransferDone(scs->transfer_id, XmTRANSFER_DONE_SUCCEED); } }
Notice how TransferProc calls XmTransferDone. This call marks the end of the transfer. Therefore, UTM will not call the destinationProc trait method inside the TextDestination widget.
If the source does not know how to convert MYTEXT, UTM will call the destinationProc trait method inside the TextDestination widget.