Chapter 10. ViewKit Cut and Paste

This chapter describes the VkCutPaste class, which provides copy, paste, drag, and drop capabilities. Figure 10-1 shows the inheritance graph for VkCutPaste.

Figure 10-1. Inheritance Graph for VkCutPaste

Figure 10-1 Inheritance Graph for VkCutPaste

Overview of ViewKit Cut and Paste

The VkCutPaste class provides a simple C++ API that helps developers add inter-application Copy, Paste, Drag, and Drop capabilities to their applications easily, and with little or no worrying about the complex protocols of Motif and the X Window System. However, developers should be familiar with the style guidelines covered in the Inter-Client Communication Conventions Manual, IRIX Interactive Desktop User Interface Guidelines, and IRIX Interactive Desktop Integration Guide in order to provide a consistent look and feel in their applications.

Although it is called VkCutPaste, this class does not provide a specific cut function. However, a cut can be implemented in virtually all programs by calling the copy functions, followed by calling a delete function specific to your program.

The VkCutPaste class uses the Xt Intrinsics and Motif to implement a standard X Selection ICCCM compliant communication, so your application must link with those libraries to use this API. VkCutPaste does not require other parts of ViewKit, and can be used in a standard Motif application.

If you wish to examine a sample program using VkCutPaste, you can find one at /usr/share/src/ViewKit/CutPasteDragDrop/cutpaste1.c++.

Primary and Clipboard Transfer Models

VkCutPaste supports both the Primary Transfer Model and the Clipboard Transfer Model (see Chapter 7, “Interapplication Data Exchange,” in the IRIX Interactive Desktop Integration Guide for more information on these transfer models). PRIMARY refers to copying data by highlighting it using the mouse. When the middle mouse button is clicked, the data is pasted to the current location of the mouse pointer. CLIPBOARD refers to copying data by using a menu selection (for instance, Copy in an Edit menu) or a keyboard accelerator (Ctrl+c). The user must use another menu selection (for example, Paste in an Edit menu) or keyboard accelerator (Ctrl+v) to paste the data at the desired location.

The behavior of the PRIMARY and CLIPBOARD selections depend entirely on how you choose to implement them. VkCutPaste does not dictate any particular behavior. However, by custom, data on the CLIPBOARD selection tends to be more permanent. It normally remains even if the original data is cut or no longer selected, and even if new data has since been selected. It generally is cleared only if the user makes another selection, and then uses a menu selection or keyboard accelerator to place the new selection on the clipboard.

Also by custom, data on the PRIMARY selection tends to be more transient. It normally is replaced when different data is highlighted. Depending on how you implement the PRIMARY selection, it may also be cleared when the selected data is cut or no longer highlighted.

Consult the Inter-Client Communication Conventions Manual, IRIX Interactive Desktop User Interface Guidelines, and IRIX Interactive Desktop Integration Guide for style guidelines.

VkCutPaste Constructor and Destructor

The following are the VkCutPaste constructor and destructor.

VkCutPaste() 

VkCutPaste(Widget w)

Instantiates a VkCutPaste object. The widget, w, must not be destroyed for the life of the VkCutPaste class, since it is used during the execution of most of the VkCutPaste functions. Do not pass the same widget into more than one concurrent instance of the VkCutPaste class.

~VkCutPaste() 


void ~VkCutPaste(void)

This deletes any remaining memory allocated by the VkCutPaste class. Because the class sometimes creates temporary files, it is important always to delete a VkCutPaste object when you are finished with it.

In order to use the copy and paste or drag and drop capabilities, you must instantiate the VkCutPaste class in your program (probably at program start-up). You can do this with the following two lines of code, where someWidget is any widget in your Motif or ViewKit program that will be valid during the lifetime of the VkCutPaste class:

#include <Vk/VkCutPaste.h>
VkCutPaste *cnp = new VkCutPaste(someWidget);

Copying Data

For most purposes, VkCutPaste::clear(), VkCutPaste::putCopy(), and VkCutPaste export() are the only functions necessary to implement a copy capability. VkCutPaste::registerLoseSelection() handles the occasions when your application loses ownership of one of the selections.

clear() 

void clear (Atom selection, Time time = CurrentTime)

Clears the indicated selection (either “CLIPBOARD” or “PRIMARY”), and frees any memory that was allocated as part of an earlier putCopy(). After the clear() function is called, no data is being offered to any other ICCCM clients on the indicated selection.

putCopy() 

Boolean putCopy(Atom selection,

                Atom target,
                XtPointer data,
                unsigned long numBytes);
 

Creates a copy of the data, which is made available to other clients by export(). The data passed into putCopy() can be freed immediately after this call. target identifies the kind of data you are trying to exchange.

If your application does not call clear() before calling putCopy(), putCopy() appends the current data to any data already on the selection.

export() 

Boolean export(Atom selection, Time time = CurrentTime);

Makes the data on the indicated selection available to other ICCCM clients.

registerLoseSelection() 


void registerLoseSelection(Atom selection,

                           LoseSelectionProc loseSelProc,
                           void *clientData)
 

Registers a callback procedure to be invoked when the application loses ownership of the selection. This is especially useful in the case of the PRIMARY selection, since in the loseSelProc callback the application should unhighlight what was previously highlighted as the PRIMARY selection. You typically call registerLoseSelection() once, right after you instantiate the VkCutPaste class.

The prototype for the LoseSelectionProc is as follows:

typedef void (*LoseSelectionProc)(Widget w,
                                  Atom selection,
                                  void *clientData);
 

The following code fragment, which would appear in a menu callback, demonstrates the use of these copying functions. In this case, the user has highlighted an XPM image and selected Copy from the Edit menu:

extern VkCutPaste *cnp;
extern XtPointer image;                          // pointer to XPM image buffer
extern unsigned long numBytes;                   // number of bytes of image data
 
Atom xaClipboard = XmInternAtom(dpy, “CLIPBOARD”, False);   // get clipboard atom
Atom xaXPM = XmInternAtom(dpy, “XPM”, False);               // get XPM atom
 
cnp->clear(xaCLIPBOARD);                             // clear clipboard
cnp->putCopy(xaCLIPBOARD, xaXPM, image, numBytes);   // make copy of XPM image
cnp->export(xaCLIPBOARD);                            // make data available

Once this code has run, any application on your desktop that has implemented the standard X Window Copy and Paste protocol can paste the XPM image.

To make this image available on the PRIMARY selection, you simply substitute XA_PRIMARY for xaCLIPBOARD.

Pasting Data

In most cases, the recommended function for pasting data into an application is VkCutPaste::importImmediate(). In some rare instances, however, you may need to use VkCutPaste::import().

importImmediate() 


XtPointer importImmediate(Atom selection,

                          Atom *interestList,
                          int interestListLen,
                          Atom *targetRet,
                          unsigned long *numBytesRet,
                          Time theTime = CurrentTime);
 

Imports Copy and Paste data from any ICCCM client. This function blocks until the data is retrieved.

The selection is either “CLIPBOARD” or “PRIMARY”. The interestList is an array of targets this application accepts, in order of preference. For example, interestList[0] might be XPM, interestList[1] might be GIF_89, and interestList[2] might be STRING. interestListLen would then be 3. For a list of registered targets, see the VkCutPaste(3x) reference page.

importImmediate() accepts any target on the interestList or any target that can be converted to one of the targets on the list (see “Using Data Type Converters” for more information on data type conversion). The function returns an XtPointer to the data or a NULL pointer (meaning that none of the requested targets were available). In the above example, if an acceptable target were found, targetRet would be XPM, GIF_89, or STRING.

importImmediate() makes a copy of the interestList, so you can free the memory immediately after this call completes.

Since this function uses its own secondary event loop, some clients might want to avoid this call.

import() 

void import(Atom selection,

            Atom *interestList,
            int interestListLen,
            ImportCallbackProc importProc,
            void *clientData = NULL,
            Time theTime = CurrentTime);
 

Acts like importImmediate() except that it is non-blocking and requires a callback. For instance, your application could call importimmediate() with an interestList containing XPM, GIF_89, and STRING. If at least one of those targets is available, then importProc is called with data set to a valid XtPointer. If none of the acceptable targets are available, importProc is called with data set to NULL.

Since import() makes a copy of the interestList, you can free the memory immediately after this call completes, even if the importProc has not yet been called.

The prototype for the ImportCallbackProc is as follows:

typedef void (*ImportCallbackProc)(Widget w,
                                   Atom target,
                                   XtPointer data,
                                   unsigned long numBytes,
                                   void *clientData);
 

The following code sample, which would appear in a menu callback, illustrates the paste capability. In this example, the application accepts a GIF image or an XPM image (your list of acceptable formats can be as long as you like; this example just happens to use two). The user has selected Paste from the Edit menu.

XtPointer image;
unsigned long numBytes;
 
Atom xaClipboard = XmInternAtom(dpy, “CLIPBOARD”, False);
Atom xaGIF_89 = XmInternAtom(dpy, “GIF_89”, False);
Atom xaXPM = XmInternAtom(dpy,”XPM”, False);
 
Atom interestList[2];
interestList[0] = xaGIF_89;
interestList[1] = xaXPM;
int numItemsInList = 2;
image = cnp->importImmediate(xaCLIPBOARD, interestList, numItemsInList,
                             &targetRet, &numBytes);
 
if (image == NULL)
    printf(“No images available”);
else if (targetRet ==xaGIF_89)
    printf(“GIF image received”);
else if (targetRet == xaXPM)
    printf(“XPM image received”);
 

The image that is returned should eventually be freed as follows:

XtFree(image);

Dragging Data

The VkCutPaste class provides two dragging functions. VkCutPaste::dragAwayCopy() are sufficient for most needs. The advanced programmer may want more control, however, and so may wish to use VkCutPaste::dragAwayCopyExtended().

dragAwayCopy() 


Widget dragAwayCopy(Widget w,

                  XEvent *xev,
                  Atom target,
                  XtPointer data,
                  unsigned long numBytes,
                  DragAwayCallbackProc dragAwayProc = NULL,
                  void *clientData = NULL);
 

Drags away data from the current application. You can free data immediately after calling this function, because dragAwayCopy() makes a copy of the data for its own use. The XEvent should be the ButtonPress event that initiated the drag. The optional dragAwayProc is called after the drag completes. One of the parameters to dragAwayProc indicates if the drag was successful or not.

dragAwayCopy() returns the DragContext created for this drag and drop transaction (see the XmDragStart(3X) reference page).

The prototype for the DragAwayCallbackProc is as follows:

typedef void (*DragAwayCallbackProc)(Widget w,
                                     Boolean result,
                                     void *clientData);
 

dragAwayCopyExtended() 


Widget dragAwayCopyExtended(Widget w,

                    XEvent *xev,
                    Atom *targetList,
                    XtPointer *dataList,
                    unsigned long *lenList,
                    int numDragItems,
                    DragAwayCallbackProc dragAwayProc = NULL,
                    void *clientData = NULL,
                    ArgList args = NULL,
                    int numArgs = 0);
 

Does exactly what dragAwayCopy() does, but provides more options for the advanced programmer. For example, you could specify the drag away data as more than one target (it should be the same conceptual object, just in different formats, like GIF and XPM format data of the same image). You could also use this function to create various drag icons by specifying those parameters in the args argument. args and numArgs are passed off to XmDragStart(). Several args are not allowed: XmNconvertProc, XmNdragOperations, XmNexportTargets, XmNnumExportTargets, and XmNclientData. See the XmDragContext(3X) reference page for more information on other, valid arguments. The Motif Drop Copy protocol (XmDROP_COPY) is the only drag-and-drop operation that the VkCutPaste class supports.

dragAwayCopyExtended() returns the DragContext created for this drag-and-drop transaction (see the XmDragStart(3X) reference page).

The following is the prototype for the DragAwayCallbackProc:

typedef void (*DragAwayCallbackProc)(Widget w,
                                     Boolean result,
                                     void *clientData);
 

The following code sample illustrates dragging. The user has just clicked a mouse button on an XPM image and has begun dragging it out of your application:

extern VkCutPaste *cnp;
extern XtPointer image;                    // pointer to the XPM image data
extern unsigned long numBytes              // number of bytes of image data
extern Widget theWidget;                   // widget where drag began
extern XEvent *xev;                        // ButtonPress Event that
                                           //     triggered the drag
 
Atom xaXPM = XmInternAtom(dpy, “XPM”, False);
 
cnp->dragAwayCopy(theWidget, xev, xaXPM, image, numBytes);
 
// at this point, you can free the data

The two optional parameters, dragAwayProc and clientData, are not usually specified, but if you want to be notified when the drag is finished, or if it was successful, you can pass in a callback procedure and any data you want. For example:

// dragAwayCB is invoked after completion of the drag
 
void dragAwayCB(Widget w, Boolean dragSuccess, XtPointer clientData)
{
    printf(“Drag success = %d”, dragSuccess);
    printf(“ClientData = 0x%x”, clientData);
} 

Your call to dragAwayCopy() would then be like this:

cnp->dragAwayCopy(theWidget, xev, xaXPM, image, numBytes,
                  dragAwayCB, (XtPointer) 123);

Accepting Drops

ViewKit provides two functions for creating a drop site. VkCutPaste::registerDropSite() is sufficient for most needs. The advanced programmer may want more control, however, and so may wish to use VkCutPaste::registerDropSiteExtended(). If you are interacting with the Silicon Graphics IRIX Interactive Desktop, see “Accepting Drops From the IRIX Interactive Desktop”.

registerDropSite() 


Boolean registerDropSite(Widget w,

                         Atom *interestList,
                         int interestListLen,
                         DropSiteCallbackProc dropProc,
                         void *clientData = NULL);
 

Registers a widget as a drop site. The interestList argument is a list of what data targets the drop site accepts, in order of preference. When another client drops data on this widget, the dropProc is called. The target passed to dropProc is the first target in the interestList that the other client can supply, or for which a converter exists (see “Using Data Type Converters” for more information about converting data types).

The following is the prototype for the DropSiteCallbackProc:

typedef void (*DropSiteCallbackProc)(Widget w,

                                    Atom target,
                                    XtPointer data,
                                    unsigned long numBytes,
                                    int x,
                                    int y,
                                    void *clientData);
 

registerDropSiteExtended() 


Boolean registerDropSiteExtended(Widget w,

                     Atom *interestList,
                     int interestListLen,
                     DropSiteCallbackProc dropSiteCallbackProc,
                     DragCallbackProc dragCallbackProc = NULL,
                     void *clientData = NULL,
                     Arg *args = NULL,
                     int numArgs = 0);
 

Does exactly what registerDropSite() does, but provides more options for the advanced programmer. For example, you could create various drop icons by specifying those parameters in the args argument. args and numArgs are passed off to XmDropSiteRegister(). Some args are not allowed: XmNdragProc, XmNdropProc, XmNdropSiteOperations, XmNimportTargets, and XmNnumImportTargets. See the XmDropSite(3X) reference page for information on other, valid arguments. The Motif Drop Copy protocol (XmDROP_COPY) is the only drag-and-drop operation the VkCutPaste class supports.

The DropSiteCallbackProc is called when the user drops some data on the specified widget. The target passed to the DropSiteCallbackProc is the first target found in interestList that the other client is offering, or for which a converter exists (see “Using Data Type Converters”).

The prototype for the DropSiteCallbackProc is as follows:

typedef void (*DropSiteCallbackProc)(Widget w,
                                    Atom target,
                                    XtPointer data,
                                    unsigned long numBytes,
                                    int x,
                                    int y,
                                    void *clientData);
 

The DragCallbackProc is called when the user drags some data over the specified widget. The target passed to the DragCallbackProc is the first target found in interestList that the other client is offering, or for which a converter exists (see “Using Data Type Converters”).

The following is the prototype of the DragCallbackProc:

typedef void (*DragCallbackProc)(Widget w,
                                 Atom target,
                                 int reason,
                                 int x,
                                 int y,
                                 void *clientData);
 

These are the reasons that can be passed to the dragCallbackProc:

  • XmCR_DROP_SITE_LEAVE_MESSAGE

  • XmCR_DROP_SITE_ENTER_MESSAGE

  • XmCR_DROP_SITE_MOTION_MESSAGE

unregisterDropSite() 


Boolean unregisterDropSite(Widget w);

Unregisters a drop site, making it stop accepting drops of any kind.

The following code sample illustrates a drop. In this example, the application's widget accepts an XPM image or a GIF image (your list of acceptable formats can be as long as you like; this example just happens to use two). After creating the widget, you might include something like this:

Atom xaXPM = XmInternAtom(dpy, “XPM”, False);
Atom xaGIF_89 = XmInternAtom(dpy, “GIF_89”, False);
 
// dropProcCB wil be invoked whenever an image is dropped on the widget
 
void dropProcCB(Widget w, Atom target, XtPointer data,
                unsigned long numBytes, int x, int y,
                XtPointer clientData)
{
    if (target == xaXPM)
        printf(“XPM Image dropped at x=%d, y=%d.”, x, y);
    else if (target == xaGIF_89)
        printf(“GIF Image dropped at x=%d, y=%d.”, x, y);
 
    // at some point, you should “XtFree(data)” to free the memory
}
// this code is done once, and makes the indicated widget a drop site
//
extern Widget theWidget;        // widget that is to become a drop site
 
Atom interestList[2];
 
interestList[0] = xaXPM;        // we first prefer XPM
interestList[1] = xaGIF_89;     // we also accept GIF
int numItemsInList = 2;
 
cnp->registerDropSite(theWidget, interestList,
                      numItemsInList, dropProcCB, (XtPointer) 123);

Accepting Drops From the IRIX Interactive Desktop

When a user drags a file icon from the IRIX Interactive Desktop, the Desktop transfers the file information via an _SGI_ICON target. The VkCutPaste class provides some convenience routines for parsing this data:

getFilenamesFromSGI_ICON() 


Boolean getFilenamesFromSGI_ICON(char *sgiIconData,

                                 unsigned long numBytes,
                                 char ***fileNameArrayRet,
                                 int *numFilesRet)
 

Parses the data of target _SGI_ICON that the IRIX Interactive Desktop uses to drag one or more files around. This is a convenience function that facilitates your application's acceptance of files from the desktop. You can call this function from a DropSiteCallbackProc when your application receives a target of _SGI_ICON. After your application is finished with the filename list, you should free the memory allocated inside getFilenamesFromSGI_ICON() by calling freeFilenamesFromSGI_ICON().

freeFilenamesFromSGI_ICON() 


void freeFilenamesFromSGI_ICON(char **fileNameArray,

                               int numFiles)

This frees the data returned from getFilenamesFromSGI_ICON().

The following code fragment illustrates the use of getFilenamesFromSGI_ICON() and freeFilenamesFromSGI_ICON() within a DropSiteCallbackProc:

Atom xaSGI_ICON = XmInternAtom(dpy, “_SGI_ICON”, False);
 
void dropProcCB(Widget w, Atom target, XtPointer data,
                unsigned long numBytes,
                int x, int y, XtPointer clientData);
{
    if (target == xaSGI_ICON)
    {
        int numFilenames = 0;
        char **filenames = NULL;
 
        printf(“drop of _SGI_ICON at %d, %d\n”, x, y);
        if (cnp->getFilenamesFromSGI_ICON((char *)data, numBytes,
            &filenames, &numFilenames))
        {
            int i;
 
            for (i = 0; i < numFilenames; i++)
            {
                printf(“\tdropped file %d: %s\n”, i, filenames[i]);
            }
 
            cnp->freeFilenamesFromSGI_ICON(filenames, numFilenames);
        }
    }
    if (data != NULL)
    {
        XtFree(data);
    }
}
 
// this code is done once, and makes the indicated widget a drop site
 
extern Widget theWidget;        // widget that is to become a drop site
 
Atom interestList[1];
interestList[0] = xaSGI_ICON;    // only interested in _SGI_ICON
 
int numItemsInList = 1;
 
cnp->registerDropSite(theWidget, interestList, numItemsInList,
                      dropProcCB, (XtPointer)123);

Registering New Data Types

Before you can pass a particular target to a VKCutPaste method, the VkCutPaste class must be aware of the target and its properties. The VkCutPaste class has a long list of known targets (see the VkCutPaste(3x) reference page). However, if your application creates a new, custom target, you must describe it by calling VkCutPaste::registerDataType().

registerDataType() 


void registerDataType(Atom target,

                      Atom type,
                      int format,
                      unsigned long flags =
                                     CUTPASTE_NORMAL_TYPE,
                      DestroyProc destroyProc = NULL,
                      void *clientData = NULL)
 

Registers a data target with the VkCutPaste class, and at the same time tells the class what the type, format, and flags are (for more information on targets, types, and formats, see the Inter-Client Communication Conventions Manual). This is commonly done only once per data type, immediately after the creation of the instance of the VkCutPaste class. You must register a data type only for types not already in VkCutPaste's list of registered targets. See the VkCutPaste(3x) reference page for a list of registered targets.

For the purposes of this chapter, the term “filename type” refers to a string type that is the name of a file being copied and pasted or dragged and dropped. The term “normal type” refers to a large block of malloc'ed memory that is being copied and pasted or dragged and dropped.

The flags argument is a bit mask of the following flags:

  • CUTPASTE_NORMAL_TYPE

  • CUTPASTE_HIDDEN_TYPE

  • CUTPASTE_FILENAME_TYPE

A CUTPASTE_HIDDEN_TYPE means that this target will never be published to other clients. This is unusual, but can be useful if you are using an internal representation you do not wish to expose.

You must flag any new “filename” target as a CUTPASTE_FILENAME_TYPE so that the VkCutPaste class can deal with it properly. When registering a new filename target, you must also pass in a destroyProc that can be called to remove the file when necessary.

The prototype for the DestroyProc is as follows:

typedef void (*destroyProc)(Widget w,
                            Atom selection,
                            Atom target,
                            XtPointer data,
                            unsigned long numBytes,
                            void *clientData);
 

The DestroyProc cleans up any auxiliary data that is no longer needed. The VkCutPaste class calls the DestroyProc at various times, immediately after deleting a target from an internal buffer.

The most common use of a DestroyProc is when the target is a filename target. The filename is the actual target, and the cloned file on disk is the auxiliary data. The DestroyProc removes the cloned file when the VkCutPaste class no longer needs it. The DestroyProc should NOT free the filename memory.

The following DestroyProc can be used verbatim for any filename type:

static void destroyProc(Widget w, Atom selection,
                        Atom target, XtPointer data,
                        unsigned long numBytes,
                        void *clientData)
{
    if (data != NULL)            // protect against errors
        unlink((char *) data);   // remove the file
}

The following code fragment illustrates the use of registerDataType(). You do not need to use this code, since the XPM_FILE target is already a registered data type. However, this code does serve as a valid example:

Atom xaXPM_FILE = XmInternAtom(dpy, “XPM_FILE”, False);
cnp->registerDataType(xaXPM_FILE,  // “target” is XPM_FILE
                      xaXPM_FILE,  // “type” is XPM_FILE
                      8,           // “format” is 8
                      CUTPASTE_FILENAME_TYPE,
                      destroyProc,
                      NULL);
 

getDataTypeInfo() 


Boolean getDataTypeInfo(Atom target,

                        Atom *type,
                        int *format,
                        unsigned long *flags)
 

Receives a target, and passes back the target's type and format (as specified in the Inter-Client Communication Conventions Manual), and any associated flags. getDataTypeInfo() returns True if it finds the target, and False otherwise. The VkCutPaste class already has an extensive list of registered targets. You can add types to this list by calling registerDataType().

Using Data Type Converters

The VkCutPaste class provides integral support for converters from one data type to another. Converters are used to increase the number of targets your application can offer (copying and dragging), or accept (pasting and dropping). For example, if you call dragAwayCopy() with XPM data, and a drop site in another ICCCM application only accepts images of type GIF_89, then the drop would normally fail. However, if you have registered a converter from XPM to GIF_89, the VkCutPaste class automatically calls your converter and successfully drops the GIF_89 on the destination client.

registerConverter() 


void registerConverter(Atom from,

                       Atom to,
                       ConvertProc converter,
                       CanConvertProc canConvertProc = NULL,
                       void *clientData = NULL)
 

Registers a converter between two data targets. For example, if you have registered a converter from XPM to GIF_89, and you dragAwayCopy() an XPM, the VkCutPaste class automatically calls your converter when the drop site accepts only GIF_89.

The prototype for the converter is as follows:

typedef Boolean (*ConvertProc)(Widget w,
                                Atom selection,
                                void *clientData, 
                                Atom srcTarget, 
                                XtPointer src,
                                unsigned long numSrcBytes,
                                Atom dstTarget,
                                XtPointer *dst,
                                unsigned long *numDstBytes)
 

The VkCutPaste class provides the ability to use an optional canConvertProc so that your application can conditionally offer conversion support for a given target. The canConvertProc returns True if the converter can perform the requested conversion, and False otherwise. The VkCutPaste class calls the canConvertProc to ensure that VkCutPaste does not offer targets that it cannot actually produce. If your conversions always work under all circumstances, do not register a CanConvertProc.

This is the prototype for the CanConvertProc:

typedef Boolean (*CanConvertProc)(Widget w,
                                  Atom selection,
                                  void *clientData,
                                  Atom srcTarget,
                                  XtPointer src,
                                  unsigned long numSrcBytes,
                                  Atom dstTarget);
 

Example 10-1 demonstrates registering a XPM to GIF_89 converter:

Example 10-1. Registering an XPM to GIF89 Converter

extern Display *dpy;
Atom xaXPM = XmInternAtom(dpy, “XPM”, False);
Atom xaGIF_89 = XmInternAtom(dpy, “GIF_89”, False);
  
Boolean xpmToGifConverter(Widget w, Atom selection, 
                          void *clientData,
                          Atom srcTarget, XtPointer src, 
                          unsigned long numSrcBytes,
                          Atom dstTarget, XtPointer *dst, 
                          unsigned long *numDstBytes)
{
    if (srcTarget != xaXPM || dstTarget != xaGIF_89)
        return(False);   // this should never happen
 
    *numDstBytes = /* calculate enough memory for Gif */
    *dst = (XtPointer) malloc(*numDstBytes);
 
    // insert code here to convert from the source XPM image 
    // into a GIF, and place it in the newly malloced memory.
 
    return(True);        // return True if the conversion is successful
}
Boolean xpmToGifConversionIsPossible(Widget w, Atom selection, 
                                     void *clientData,
                                     Atom srcTarget,
                                     XtPointer src, 
                                     unsigned long numSrcBytes, 
                                     Atom dstTarget)
{
    if (srcTarget != xaXPM || dstTarget != xaGIF_89)
        return(False);   // this should never happen
 
    numberOfColorsInXPM = discoverNumberOfColorsInXPM(src);
    if (numberOfColorsInXPM < 256)    // GIF only has 256 colors
        return(True);
    else
        return(False);                // we cannot convert this GIF
}
 
cnp->registerConverter(xaXPM, xaGIF_89, xpmToGifConverter, 
                       xpmToGifConversionIsPossible, NULL); 

After this single registerConverter() call, your converter is automatically called in each of four possible scenarios:

  1. If you export an XPM, but the importing client accepts only GIF_89.

  2. If you dragAway XPM, but the client you drop on accepts only GIF_89.

  3. If you want to import a GIF_89, but the sending client doesn't offer GIF_89, but does offer XPM.

  4. If a drop on you only offers XPM, but you accept only GIF_89.

File and Data Ownership

Changes in the ownership of files and data during copy, paste, drag, and drop operations can be difficult to trace. The pseudocode examples in this section detail the ownership changes during different stages of data transfer. The examples show code implementing copy, paste, drag, and drop of both normal data and filename data.

Filename data simply means the data being transferred is a filename. For example, when transferring an XPM_FILE target, the data transferred is actually the filename, not the data contained in the file. The receiving application needs to retrieve the filename, then access the file. Normal data means that the data being transferred is a block of malloc'ed memory.

The VkCutPaste class recognizes targets as being filename targets only if they have been registered with the CUTPASTE_FILENAME_TYPE flag. See the VkCutPaste(3x) reference page for a list of registered targets.


Note: Applications should seldom need to use filename targets for cut, paste, drag, and drop operations. In fact, exchanging filenames with other applications does not work when the sender and receiver applications are running on different computers. When given a choice between filename targets and the corresponding normal targets, applications should always exchange the normal targets.

In these pseudocode samples (Example 10-2 to Example 10-10), cnp is an instance of the VkCutPaste class, and the filename target is any target that has been registered with the CUTPASTE_FILENAME_TYPE flag.

Example 10-2. Data and File Ownership Changes While Copying Filenames

// Create a file on disk.  Ownership of the file will be transferred
// to the VkCutPaste class at putCopy() time.  
 
char *filename = create a file, return the malloced filename;
 
// Clear any prior putCopy() or export().  
 
cnp->clear();
 
// Copy the filename `filename', and transfer the ownership of the 
// file on disk to the VkCutPaste class. Note that the file itself 
// is NOT copied, only the filename.  The client can now free the 
// memory used by filename, but must NOT reference the disk file again.
// NOTE: If the client has registered a destroyProc for this 
// filename type, the VkCutPaste class might call that destroyProc to
// remove the original disk file. In that case, the client should honor
// the request and remove the file.
 
cnp->putCopy(filename);
 
// The VkCutPaste class has made a copy of the filename, so the 
// original data should be freed by the client.  
 
free(filename);
 
// Make the data available to other clients.  
 
cnp->export();
...
 
// Some time later, if the VkCutPaste class gets a request for this 
// filename, the VkCutPaste class will clone the file, and hand off to
// the requesting client the cloned filename.  Ownership of this 
// cloned file is transferred to the requesting client.
 
...
// The next time clear() is called, any data that has been copied 
// during prior putCopy() calls is freed, and any files that the 
// VkCutPaste class has obtained ownership of during prior putCopy() 
// calls are removed. After this clear(), this VkCutPaste instance 
// no longer has any data available for export. 
 
cnp->clear();

 

Example 10-3. Data and File Ownership Changes While Pasting Filenames

// Client requests a filename target. The filename returned and the
// corresponding file on disk are now owned by this client. It is 
// therefore the responsibility of the requesting client to remove 
// (unlink()) the file when the client is finished with it, and
// free the filename.  
 
filename = cnp->importImmediate();
 
// After this client is done with the file, the file must be 
// removed, and the filename freed.  
 
unlink(filename);
XtFree(filename);

 

Example 10-4. Data and File Ownership Changes While Copying Normal Data

// Create some data in memory.  
 
char *data = create some data;
 
// Clear any prior putCopy() or export().  
 
cnp->clear();
 
// The VkCutPaste class makes a copy of the data.  This copy of 
// the data will be freed during the next “clear()” operation.  
 
cnp->putCopy(data);
 
// The VkCutPaste class has made a copy of the data, so the original 
// data should be freed by the client.  
 
free(data);
 
// Make the data available to other clients.  
 
cnp->export();
...
// The next time clear() is called, any data that has been copied 
// during prior putCopy() calls is freed, and any files that the 
// VkCutPaste class has obtained ownership of during prior putCopy()
// calls are removed. After this clear(), this VkCutPaste instance no
// longer has any data available for export. 
cnp->clear();

 

Example 10-5. Data and File Ownership Changes While Pasting Normal Data

// Client requests the data.  This data is now owned by this 
// client.  The client will use the data, and should free it when 
// it is done processing the data.  
 
data = cnp->importImmediate();
 
// Free the imported data when you are done processing it.  
 
XtFree(data);

 

Example 10-6. Data and File Ownership Changes While Dragging Filename Data

// Create a file on disk.  Ownership of the file will be 
// transferred to the VkCutPaste class at dragAwayCopy() time.  
 
char *filename = create a file, return the malloced filename;
 
// Copy the filename `filename', and transfer the ownership of the 
// file on disk to the VkCutPaste class. Note that the file itself 
// is NOT copied, only the filename.  The client can now free the 
// memory used by filename, but must NOT reference the disk file 
// again. The VkCutPaste class will free this copy of the data, and 
// remove the file when the drag and drop operation is complete.  
//
// NOTE: If the client has registered a destroyProc for this filename 
// type, the VkCutPaste class might call that destroyProc to remove 
// the original disk file. In that case, the client should honor the
// request and remove the file.
 
cnp->dragAwayCopy(filename);
 
// The VkCutPaste class has made a copy of the filename, so the 
// original data should be freed by the client.  
 
free(filename);
...
// Some time later, if the VkCutPaste class gets a request for this 
// filename, the VkCutPaste class will clone the file, and hand off
// the cloned filename to the requesting client.  Ownership of this 
// cloned file is transferred to the requesting client.   The 
// original file is removed when the drag and drop operation is 
// complete.

 

Example 10-7. Data and File Ownership Changes While Accepting Filename Data

// The “drop” client registers a drop site.  
 
cnp->registerDropSite(dropFilenameCallback);
...
 
// Some time later, the dropFilenameCallback() routine is called.  
// Inside of the dropFilenameCallback, the filename passed in and 
// the corresponding file on disk are now owned by this client.  It 
// is therefore the responsibility of the requesting client to remove 
// (unlink()) the file when the client is finished with it, and free 
// the filename.  
 
dropFilenameCallback(filename) 
{
    // Do some processing on the file.
 
    unlink(filename);     
 
    // After this client is done with
    // the file, the file must be removed,
    // and the filename freed.
  
    XtFree(filename);
}

 

Example 10-8. Data and File Ownership Changes While Dragging Normal Data

// Create some data in memory.
 
char *data = create some data;
 
// The VkCutPaste class makes a copy of the data.  The VkCutPaste
// class will free this copy of the data when the drag and drop 
// operation is complete.   
 
cnp->dragAwayCopy(data);
 
// The client can now free the original data.  
 
free(data);

 

Example 10-9. Data and File Ownership Changes While Accepting Normal Data

// The “drop” client registers a drop site.  
 
cnp->registerDropSite(dropDataCallback);
...
// 
// Some time later, the dropDataCallback() routine is called.  
 
dropDataCallback(data) 
{
 
    // Do some processing with this data.
 
    ...
 
    // Free the data when you are done processing it.
 
    XtFree(data);
}

 

Example 10-10. Data and File Ownership Changes While Accepting _SGI_ICON Data

// The drop client registers a drop site that accepts _SGI_ICON data
 
cnp->registerDropSite(dropSGI_ICONcallback);
...
// Some time later, the dropSGI_ICONcallback() routine is called.
// Inside the dropSGI_ICONcallback, this client reads the filenames 
// out of the _SGI_ICON data, but DOES NOT own the files and must not
// modify them or remove them. This client does need to free the 
// _SGI_ICONdata.
 
dropSGI_ICONcallback(_SGI_ICONdata)
{
    // Get the filenames, read some data from the files, etc.
 
    XtFree(_SGI_ICONdata);
}


Miscellaneous Functions

The VkCutPaste class also provides several utility functions that you may find useful.

primaryAtom()  


Atom primaryAtom(void)

Returns the PRIMARY atom. This is a convenience function, and returns exactly the same thing as XmInternAtom(dpy, “PRIMARY”, False).

clipboardAtom() 


Atom clipboardAtom(void)

Returns the CLIPBOARD atom. This is a convenience function, and returns exactly the same thing as XmInternAtom(dpy, “CLIPBOARD”, False).

getVersion() 

unsigned long getVersion(void)

Returns the version number of this implementation of the VkCutPaste class. For example, version 1.0 would be 0x010000, version 2.01 would be 0x020100, and so on.

getWidget() 

Widget getWidget(void)

Returns the widget that was originally passed to the VkCutPaste constructor.

getXServerTime() 


Time getXServerTime(void)

Invokes a round trip to the X-Server, and returns a server time stamp. You should try to avoid this call. Instead, you should pass the CurrentTime flag to VkCutPaste functions, or (even better) use the time stamp of a recent X-Event. However, there may be some rare situations where there are no X-Events available and CurrentTime does not work, so this convenience function provides a fail-safe way to get a valid server time stamp.

setTransactionsTimeout() 


void setTransactionsTimeout(unsigned long numSeconds)

Sets the transaction time-out. The default Motif time-out is 5 seconds, which means that if the remote client does not respond to a request for data within 5 seconds, the transaction is cancelled and no data is transferred. For some large data targets, or those that require long conversions, 5 seconds may not be adequate.

isOwnedByMe() 


Boolean isOwnedByMe(Atom selection)

Returns True if the selection is currently owned by the calling client. This can be used to optimize the speed of Paste() when the client would have exchanged data with itself anyway.

isOwnedbyLocalHost() 


Boolean isOwnedByLocalHost(Atom selection)

Returns True if the indicated selection is currently owned by a client running on the same machine as this client. This call is useful if you are planning to copy and paste filenames with other clients, since a file that is on a different machine is not accessible to your client. Some examples of filename targets are XPM_FILE and GIF_89_FILE. Normal types like XPM and GIF_89 are always safe and always work, and should be used instead, unless there is some overwhelming reason to exchange filenames.

getLocalReference() 


Boolean getLocalReference(Atom selection,

                          Atom target,
                          XtPointer *dataRet,
                          unsigned long *numBytesRet)
 

Allows you to retrieve the contents of the local export selection. This is not based on X Selections. This only gives you a pointer, so the data must not be freed or modified. Returns a pointer to the data at index (if multiple putCopy() were called, the first one is at index 0, the second is at index 1, and so on).

getLocalTypeReference() 


Boolean getLocalTypeReference(Atom selection,

                              Atom target,
                              XtPointer *dataRet,
                              unsigned long *numBytesRet)
 

Does exactly the same thing as getLocalReference(), but instead of specifying an index, you specify the target you wish to retrieve from the exported selection.

putReference() 


Boolean putReference(Atom selection,

                     Atom target,
                     XtPointer data, 
                     unsigned long numBytes)
 

Can be used instead of putCopy() when the data is so large that an extra copy would be impossible or impractical. This function does not make a copy of the data, so you should never free data until after you call remove() to remove the currently exported data from the selection. If, for some reason, the data becomes invalid, you must call remove().

remove() 

Boolean remove(Atom selection, Atom target);

Removes the indicated target from the currently exported selection. For example, if you had called putReference(XPM) earlier, and now for some reason, the XPM data is no longer valid, you must call remove(XPM).