| Documentation: Clipboard and Primary Selection [Remove Frame]

FOX supports three different ways of moving or copying data between applications:

- Drag and drop. Drag and drop is used when you grab some object on the screen and drag it to another location, and then drop it there. It is the most visually intuitive way to move information from one place to another. The drag and drop mechanism is described in the section on drag and drop.

- Clipboard. The clipboard is used by typically selecting some object or text and copying it to the clipboard using Ctrl-C, or cutting it to the clipboard using Ctrl-X. Next you can move to another application and pasted the object or text into it by using Ctrl-V.
  Conceptually, the clipboard is a memory region which holds on to a copy of the object or text that was copied or cut. This memory region will continue to exist until it is replaced by another clipboard copy or cut operation, either in the same application or in another application; there is conceptually only one single memory region serving all applications on the desktop.

- Primary Selection. The primary selection is silently established by selecting some text in a text field. It can be pasted into another text field simply by using the middle mouse button. Unlike the clipboard, there is no memory region to remember the primary selection other than the selection on the screen itself. Thus, changing the selection immediately overwrites the old one.
  Analoguous to the clipboard, there is only one primary selection active among all applications on the desktop; selecting text in another application will deselect the text in the current one.

All three methods can exchange essentially arbitrary amounts of data, or arbitrary types. Exchanges may take place between different controls in the same application, or different controls in different applications. Under X-Windows, the different applications may even be running on different machines of different types.

Whether using the clipboard, drag and drop, or primary selection, applications must first agree on the types of data being exchanged. A single piece of data, for example, an image, may be exchanged using different representations. For an image, for instance, the transfer may take place as GIF or BMP formats.

To increase the odds that both the originating application and the receiving one support the same data representations, the originating application can furnish its list of supported representations for the data. The receiving application, after inspecting this list, can then ask for the one format of the supplied list it also supports.

Registering Clipboard Types

In order to communicate a particular data structure across applications, both partners need to first register a Drag Type. Registering a drag type yields a unique number which is guaranteed to be the same for all applications on the desktop.
The Drag Type is created by calling the function:

    FXDragType FXApp::registerDragType(const FXString& name) const;

The registerDragType() function registers a new Drag Type name on the application's desktop, and returns an abstract handle to the Drag Type. The returned handle is used in all subsequent clipboard, drag and drop, or primary selection operations to signify the data type being exchanged.

Chosing one of the standard MIME types. for commonly available data streams will ensure that data can be exchanged between FOX programs and programs written using other toolkits.

The function:

    FXString FXApp::getDragTypeName(FXDragType type) const;

will return the Drag Type Name, given the Drag Type type. You may need to use this in case your application receives a drop of an unknown type, and you need to decide what to do with it.

Clipboard Exchange

The clipboard is a single facility on the desktop which can be interrogated about the types of objects currently retained, and the contents of these objects.
The clipboard is said to be owned by a window, meaning that whenever such a query must be asnwered, that particular window is the one which will be asked.
The advantage of this model is that it permits an easy-going decentralized scheme where 3rd-party widgets can implement any number of clipboard data types without any need for central coordination.
Moreover, the originator can furnish a large number of potential formats for the clipboard selection, yet will only be called upon to actually supply one of these formats during the transaction.

Upon performing a Cut or Copy to clipboard operation, the originating widget will claim ownership of the clipboard by calling:

    FXbool FXWindow::acquireClipboard(const FXDragType *types,FXuint numtypes);

where types is a list of Drag Types previously registered (see above), and numtypes is the number of such types.

When one widget acquires the clipboard, the previous widget which owned the clipboard will receive a message of type SEL_CLIPBOARD_LOST, which should cause that widget to release all data previously retained regarding the clipboard operation.

If the caller has successfully invoked acquireClipboard(), it can allocate storage to remember the clipboard selection in. As it may be requested to furnish the selection in any of the previously stated data types, it may have to remember multiple representations of the clipboard selection.

The widget calling acquireClipboard() will receive a message of type SEL_CLIPBOARD_GAINED.

To voluntarily release the clipboard, a widget may call:

    virtual void FXWindow::releaseClipboard();

This will cause the calling widget to receive a message of type SEL_CLIPBOARD_LOST, just as if some other widget had called acquireClipboard().

The proper response to receiving a SEL_CLIPBOARD_LOST message is to release the clipboard selection data:

    long MyWidget::onClipboardLost(FXObject* sender,FXSelector sel,void* ptr){
      BaseClassOfMyWidget::onClipboardLost(sender,sel,ptr);
      ...
      free the data
      ...
      return 1;
      }

A widget which wants to obtain the clipboard selection can ask for the entire list of clipboard types by calling:

    FXbool inquireDNDTypes(FXDNDOrigin origin,FXDragType*& types,FXuint& numtypes) const;

Where the parameter origin should be set to FROM_CLIPBOARD. The result will be placed in an array of Drag Types types of length numtypes.

Often, the requesting widget can deal with only one single type of data; for such cases, its more convenient to be able to ask if one particular Drag Type is supported by the originator:

    FXbool offeredDNDType(FXDNDOrigin origin,FXDragType type) const;

Where again, origin must be set to FROM_CLIPBOARD, and type is the type which we want to know about.

In even simpler scenarios, the requesting widget may simply ask for the clipboard data of a certain datatype. This is appropriate if there is no special action needed in preparation to receiving the data.

A widget can obtain the data using:

    FXbool getDNDData(FXDNDOrigin origin,FXDragType type,FXuchar*& data,FXuint& size) const;

Once more, origin is set to FROM_CLIPBOARD. The requested type is given in type. Upon successful return, data will refer to a byte array containing the clipboard selection data, and size will contain the length of this data array. If the originating widget is unable to furnish the requested data type, getDNDData() returns FALSE and the data and size will be set to NULL and 0, respectively.

The requesting widget is now resposible for deleting the memory, which must be done using FXFREE().

When a widget requests data using getDNDData(), a message of type SEL_CLIPBOARD_REQUEST is issued to the widget owning the clipboard. The originating widget must call:

    FXbool setDNDData(FXDNDOrigin origin,FXDragType type,FXuchar* data,FXuint size) const;

With origin set to FROM_CLIPBOARD, type to the requested data type. The originating widget must allocate (using FXMALLOC) and fill a data array with the clipboard selection and pass a reference to data, and the size of the array in size.

After handing the array to setDNDData(), the originating widget no longer owns the data array and should make NO attempts to free it!

To figure out which datatype was requested by the requesting widget, the originating widget can inspect the target member of the FXEvent structure passed in the SEL_CLIPBOARD_REQUEST message pointer.

A typical widget implementation first lets its base class inspect the Drag Type, and if it was not handled yet, inspects it itself:

    long MyWidget::onClipboardRequest(FXObject* sender,FXSelector sel,void* ptr){

      // See if base class knows how to deal with the requested clipboard type
      if(BaseClassOfMyWidget::onClipboardRequest(sender,sel,ptr)) return 1;

      // See if we can deal with this type ourselves
      if(((FXEvent*)ptr)->target==mydataType){
        FXuchar *data;
        FXuint len;
        ...
        FXMALLOC(&data,FXuchar,len);
        ...
        fill data
        ...

        // Give the array to the system!
        setDNDData(FROM_CLIPBOARD,event->target,data,len);

        // Return 1 because it was handled here
        return 1;
        }

      // Return 0 to signify we haven't dealt with it yet; a derived
      // class from MyWidget may yet give it another try ...
      return 0;
      }

Thus, MyWidget itself can also be subclassed so as to add even more data types!

Clipboard Originator

The originator's responsibilities during a clipboard transaction are as follows:

- Register the desired clipboard data types; in most widgets, this is done once only in the widget's create() routine. Usually, all widgets of the same type support the same set of clipboard types so it is appropriate to remember the clipboard type in a static member variable.

- When performing a Cut or Copy to clipboard operation, call acquireClipboard(), and if successfully obtaining the clipboard, allocate the necessary memory for keeping a copy of the clipboard selection data.

- Alternatively, allocate the memory and remember the clipboard selection when the SEL_CLIPBOARD_GAINED is received.

- Sit there and wait for a SEL_CLIPBOARD_REQUEST message. If a request is received, try handle the request in the base class, and if still not handled, try handle it in the widget.
  Return 1 only if it was handled.

- If a SEL_CLIPBOARD_LOST is received, release the memory used to keep the clipboard selection and forget anything has happened.

- Typically, just in case the widget still owns the clipboard when it is destroyed, release the memory for the clipboard selection in the destructor also.

Clipboard Requestor

The requestor's responsibilities in a clipboard transaction are as follows:

- Upon performing a Paste command, determine the type of data using inquireDNDTypes() or offeredDNDType(). Then ask for the data using getDNDData(), passing in the requested data type, which is typically one of the data types furnished by the originator.

- If getDNDData() returned successfully, insert the data into the widget, and release the data array obtained from getDNDData().

Primary Selection Exchange

The primary selection data exchange mechanism is pretty much the same as that for the clipboard.
The key difference is that typically, no copy is made of the selection. As soon as some text is selected, the originating window acquires the primary selection. If the selected text is changed, the widget typically releases and reacquires the primary selection.

Also, upon receiving a SEL_SELECTION_LOST, typically all that happens is that the text is deselected, instead of deleted.

When exchanging via primary selection, the origin parameter is normally set to FROM_SELECTION.

Copyright © 1997-2022 Jeroen van der Zijp