"STRING", data, length+1, &recvd, NULL);
while (status == ClipboardLocked);
if (status != ClipboardSuccess || recvd != length) {
printf ("Failed to receive all clipboard data0);
XtFree (data);
}
else
printf ("Retrieved
}
18.2 Copy by Name
As discussed earlier, there are cases where data should not be c-opied to the clipboard until it is requested. It is
possible to copy data by name, so that the owner of the data is notified through a callback function when the data is
needed by the clipboard. Since copying large amounts of data may be expensive, time−consuming, or even impossible
due to other constraints in an application, copying data by name may be the only option available. The technique is
especially advantageous if the data is never requested, since time and resources are saved.
The procedure for copying data by name is quite similar to the procedure for normal copying. The application first
calls XmClipboardStartCopy(), but unlike a normal copy operation, the callback and widget parameters
are specified. These values indicate that the data is to be copied by name. The callback parameter specifies the
routine that is called when the data is requested by another client. The widget parameter specifies the widget that
receives the messages requesting the data. Since the toolkit handles the messages, any valid widget ID can be used.
XmClipboardCopy() is then called with a buffer value of NULL. XmClipboardEndCopy() is called as
usual. When a client requests the data from the clipboard, the callback routine provided to
XmClipboardStartCopy() is called and the application provides the actual data using
XmClipboardCopyByName().
You can use the convenience function XmClipboardBeginCopy() instead of XmClipboardStartCopy().
The only difference between the two routines is that the convenience function does not take a timestamp parameter;
it simply uses CurrentTime as the timestamp value.
The program shown in the source code demonstrates copying data to the clipboard by name.
/* copy_by_name.c −− demonstrate clipboard copies "by−name".
* Copying by name requires that the copy *to* clipboard
* functions use the same window as the copy *from* clipboard
* functions. This is a restriction placed on the API by the
* toolkit, not by the ICCCM.
*/
#include <Xm/CutPaste.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
static void to_clipbd(), from_clipbd();
Widget toplevel;
main(argc, argv)
int argc;
char *argv[];
{
Widget rowcol, button;
XtAppContext app;
18 The Clipboard 18.2 Copy by Name
502
XtSetLanguageProc (NULL, NULL, NULL);
/* Initialize toolkit, application context and toplevel shell */
toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0,
&argc, argv, NULL, NULL);
/* manage two buttons in a RowColumn widget */
rowcol = XtVaCreateWidget ("rowcol",
xmRowColumnWidgetClass, toplevel,
NULL);
/* button1 copies to the clipboard */
button = XtVaCreateManagedWidget ("button1",
xmPushButtonWidgetClass, rowcol,
XtVaTypedArg, XmNlabelString, XmRString,
"Copy To Clipboard", sizeof (char *),
NULL);
XtAddCallback (button, XmNactivateCallback, to_clipbd, NULL);
/* button2 retrieves text stored in the clipboard */
button = XtVaCreateManagedWidget ("button2",
xmPushButtonWidgetClass, rowcol,
XtVaTypedArg, XmNlabelString, XmRString,
"Retrieve From Clipboard", sizeof (char *),
NULL);
XtAddCallback (button, XmNactivateCallback, from_clipbd, NULL);
/* manage RowColumn, realize toplevel shell and start main loop */
XtManageChild (rowcol);
XtRealizeWidget (toplevel);
XtAppMainLoop (app);
}
static void
copy_by_name(widget, data_id, private_id, reason)
Widget widget;
int *data_id;
int *private_id;
int *reason;
{
Display *dpy = XtDisplay (toplevel);
Window window = XtWindow (toplevel);
static int cnt;
int status;
char buf[32];
printf ("Copy by name called0reason: %s, private_id: %d, data_id: %d0,
*reason == XmCR_CLIPBOARD_DATA_REQUEST? "request" : "delete",
*private_id, *data_id);
if (*reason == XmCR_CLIPBOARD_DATA_REQUEST) {
sprintf (buf, "stuff−%d", ++cnt); /* make each copy unique */
do
status = XmClipboardCopyByName (dpy, window, *data_id, buf,
strlen (buf)+1, *private_id = cnt);
while (status != ClipboardSuccess);
}
}
18 The Clipboard 18.2 Copy by Name
503
/* copy data to clipboard */
static void
to_clipbd(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
unsigned long item_id = 0; /* clipboard item id */
int status;
XmString clip_label;
Display *dpy = XtDisplay (toplevel);
Window window = XtWindow (toplevel);
clip_label = XmStringCreateLocalized ("to_clipbd");
/* start a copy. retry till unlocked */
do
status = XmClipboardBeginCopy (dpy, window,
clip_label, widget, copy_by_name, &item_id);
while (status == ClipboardLocked);
/* copy by name by passing NULL as the "data", copy_by_name() as
* the callback and "widget" as the widget.
*/
do
status = XmClipboardCopy (dpy, window, item_id, "STRING",
NULL, 8L, 0, NULL);
while (status == ClipboardLocked);
/* end the copy */
do
status = XmClipboardEndCopy (dpy, window, item_id);
while (status == ClipboardLocked);
}
static void
from_clipbd(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
int status;
unsigned total_bytes;
unsigned long received;
char *data = NULL, buf[32];
Display *dpy = XtDisplay (toplevel);
Window window = XtWindow (toplevel);
do
status = XmClipboardStartRetrieve (dpy, window, CurrentTime);
while (status == ClipboardLocked);
/* initialize data to contain at least one byte. */
data = XtMalloc (1);
total_bytes = 1;
do {
buf[0] = 0;
/* retrieve data from clipboard −− if locked, try again */
status = XmClipboardRetrieve (dpy, window, "STRING",
buf, sizeof (buf), &received, NULL);
if (status == ClipboardNoData) {
18 The Clipboard 18.2 Copy by Name
504
puts ("No data on the clipboard");
break;
}
/* reallocate data to contain enough space for everything */
if (!(data = XtRealloc (data, total_bytes + received))) {
XtError ("Can't allocate space for data");
break; /* XtError may or may not return */
}
/* copy buf into data. strncpy() does not NULL terminate */
strncpy (&data[total_bytes−1], buf, received);
total_bytes += received;
} while (status == ClipboardTruncate);
data[total_bytes−1] = 0; /* NULL terminate */
if (status == ClipboardSuccess)
printf ("Retrieved
data, total_bytes);
status = XmClipboardEndRetrieve(dpy, window);
}
Just as in Example 17−1, the function to_clipbd() is used to initiate copying data to the clipboard. However,
rather than passing actual data, we use:
status = XmClipboardBeginCopy (dpy, window,
clip_label, widget, copy_by_name, &item_id);
Passing a valid widget and a callback routine indicates that the copy−by−name method is being used. Here, the data is
provided through the given callback routine when it is requested, rather than being provided immediately. The
item_id parameter is filled in by the clipboard function to identify the particular data element. The parameter is
then used in the call to copy data:
status = XmClipboardCopy (dpy, window, item_id, "STRING",
NULL, 8L, 0, NULL);
Passing NULL as the data also indicates that the data is passed by name. The value 8L is passed as the size
parameter to indicate how much data will be sent if the data is requested. This value is important in case other clients
query the clipboard to find out how much data is available to copy.
The callback function copy_by_name() is called either when someone requests the data from the clipboard or
when another client copies new data (by name or with actual data) to the clipboard. In the first case, the data must be
copied to the clipboard; in the second case, the clipboard is telling the client that it can now free its data. The callback
function is an XmCutPasteProc, which takes the following form:
typedef void
(*XmCutPasteProc) (Widget, * int, * int, * int)
Widget widget;
int *data_id;
int *private_id;
int *reason;
The widget parameter is the same as that passed to XmClipboardStartCopy(). The data_id arguemnt is the
ID of the data item that is returned by XmClipboardCopy(), and private_id is the private data passed to
XmClipboardCopy(). The reason parameter takes the value XmCR_CLIPBOARD_DATA_REQUEST, which
indicates that the data must be copied to the clipboard, or XmCR_CLIPBOARD_DATA_DELETE, which indicates that
the client can delete the data from the clipboard. Although the last three parameters are pointers to integer values, the
18 The Clipboard 18.2 Copy by Name
505
values are read−only and changing them has no effect.
The purpose of the function is either to send the appropriate data to the clipboard or to free the data. The value of
reason determines which action is taken. Since no data is passed to the clipboard until this callback function is
called, either the data must be stored locally (in the application) or the function must be able to generate it
dynamically. The example makes no assumptions or suggestions about how to create the data, since it is entirely
subject to the nature of the data and/or the application.
Once the data is obtained, it is sent to the clipboard using XmClipboardCopyByName(). This function does not
need to lock the clipboard since the clipboard is already being locked by the window that called
XmClipboardRetrieve(). At this point in time, both routines are accessing the clipboard. If the same
application is both retrieving the data and copying the data, the XmClipboardRetrieve() and
XmClipboardCopyByName() routines must use the same window for their respective window parameters
because otherwise deadlock will occur and the application will hang. There may be cases where you should copy data
to the clipboard incrementally. The data may be large enough that allocating one large data space to handle the entire
copy is unreasonable; its size may warrant sending it in smaller chunks. Moreover, data may be generated by a slow
mechanism such as a database library. If the database only returns data in specific block sizes, then you need not
buffer them all up and send to the clipboard with one call; you can send each block as it comes through.
Incremental copying requires multiple calls to XmClipboardCopyByName(). Since
XmClipboardCopyByName() does not lock the clipboard, you need to do that yourself by calling
XmClipboardLock(). However, you only need to call it once no matter how much data is transferred. When you
are through copying the data, you need to call XmClipboardUnlock(). In some cases, you may need to stop
sending data before the copy is complete. For example, if the database is not responding to your application or there
are other extenuating circumstances, you may want to terminate the copy operation using
XmClipboardCancelCopy(), which has the following form:
int
XmClipboardCancelCopy(display, window, item_id)
Display *display;
Window window;
long item_id;
When using XmClipboardCancelCopy, you should not unlock the clipboard using XmClipboardUnlock().
If you have copied data by name to the clipboard under a specific data format, you may withdraw it by calling
XmClipboardWithdrawFormat(). The function takes the following form:
int
XmClipboardWithdrawFormat(display, window, data_id)
Display *display;
Window window;
int data_id;
Despite the name of the procedure, its main purpose is not to remove a format specification, but to remove a data
element in that format from the clipboard. The data_id parameter is the same value that is returned by
XmClipboardCopy() when the data is initially copied by name. If the specified window holds the clipboard data
but it is in a different format than that specified by data_id, then the data is not removed from the clipboard.
18 The Clipboard 18.2 Copy by Name
506
Get Volume 6A: Motif Programming Manual now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.