Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2 
      3 Copyright 1989, 1998  The Open Group
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included in
     12 all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20 
     21 Except as contained in this notice, the name of The Open Group shall not be
     22 used in advertising or otherwise to promote the sale, use or other dealings
     23 in this Software without prior written authorization from The Open Group.
     24  * *
     25  * Author:  Ralph Swick, DEC/Project Athena
     26  * Updated for R4:  Chris D. Peterson,  MIT X Consortium.
     27  * Reauthored by: Keith Packard, MIT X Consortium.
     28  * UTF-8 and CTEXT support: Stanislav Maslovski <stanislav.maslovski (at) gmail.com>
     29  */
     30 
     31 #ifdef HAVE_CONFIG_H
     32 #include "config.h"
     33 #endif
     34 
     35 #include <stdio.h>
     36 #include <X11/Intrinsic.h>
     37 #include <X11/StringDefs.h>
     38 #include <X11/Xatom.h>
     39 
     40 #include <X11/Xmu/Atoms.h>
     41 #include <X11/Xmu/StdSel.h>
     42 
     43 #include <X11/Shell.h>
     44 #include <X11/Xaw/Form.h>
     45 #include <X11/Xaw/Label.h>
     46 #include <X11/Xaw/Command.h>
     47 #include <X11/Xaw/AsciiText.h>
     48 #include <X11/Xaw/Dialog.h>
     49 #include <X11/Xaw/Cardinals.h>
     50 #include <X11/IntrinsicP.h>
     51 #include <X11/Xaw/TextP.h>
     52 #include <X11/Xfuncs.h>
     53 
     54 #ifdef XKB
     55 #include <X11/extensions/XKBbells.h>
     56 #endif
     57 
     58 #include <stdlib.h>
     59 
     60 #define Command commandWidgetClass
     61 #define Label	labelWidgetClass
     62 #define Text    asciiTextWidgetClass
     63 
     64 #define INFINITY 10000000	/* pretty big, huh? */
     65 
     66 typedef struct _Clip {
     67     struct _Clip    *next, *prev;
     68     char	    *clip;
     69     char	    *filename;
     70     size_t	    avail;
     71 } ClipRec, *ClipPtr;
     72 
     73 static Atom wm_delete_window;
     74 static Atom wm_protocols;
     75 
     76 static void EraseTextWidget ( void );
     77 static void NewCurrentClipContents ( char *data, int len );
     78 
     79 static String fallback_resources[] = {
     80     "*international: true",
     81     NULL
     82 };
     83 
     84 static long
     85 TextLength(Widget w)
     86 {
     87     return XawTextSourceScan (XawTextGetSource (w),
     88 			      (XawTextPosition) 0,
     89  			      XawstAll, XawsdRight, 1, TRUE);
     90 }
     91 
     92 static void
     93 SaveClip(Widget w, ClipPtr clip)
     94 {
     95     Arg	    args[1];
     96     char    *data;
     97     size_t  len;
     98     Widget  source;
     99 
    100     source = XawTextGetSource (w);
    101     XtSetArg (args[0], XtNstring, &data);
    102     XtGetValues (source, args, 1);
    103     len = strlen (data);
    104     if (len >= clip->avail)
    105     {
    106 	if (clip->clip)
    107 	    free (clip->clip);
    108 	clip->clip = malloc (len + 1);
    109 	if (!clip->clip)
    110 	    clip->avail = 0;
    111 	else
    112 	    clip->avail = len + 1;
    113     }
    114     if (clip->avail)
    115     {
    116 	strcpy (clip->clip, data);
    117     }
    118 }
    119 
    120 static void
    121 RestoreClip(Widget w, ClipPtr clip)
    122 {
    123     Arg	    args[1];
    124     Widget  source;
    125 
    126     source = XawTextGetSource (w);
    127     XtSetArg (args[0], XtNstring, clip->clip);
    128     XtSetValues (source, args, 1);
    129 }
    130 
    131 /*ARGSUSED*/
    132 static ClipPtr
    133 NewClip(Widget w, ClipPtr old)
    134 {
    135     ClipPtr newClip;
    136 
    137     newClip = calloc (1, sizeof (ClipRec));
    138     if (!newClip) {
    139 	XtError("Failed to allocate memory for ClipRec\n");
    140 	return newClip;
    141     }
    142     newClip->prev = old;
    143     if (old)
    144     {
    145 	newClip->next = old->next;
    146 	old->next = newClip;
    147     }
    148     return newClip;
    149 }
    150 
    151 /*ARGSUSED*/
    152 static void
    153 DeleteClip(Widget w, ClipPtr clip)
    154 {
    155     if (clip->prev)
    156 	clip->prev->next = clip->next;
    157     if (clip->next)
    158 	clip->next->prev = clip->prev;
    159     if (clip->clip)
    160 	free (clip->clip);
    161     free ((char *) clip);
    162 }
    163 
    164 static ClipPtr	currentClip;
    165 static Widget	top;
    166 static Widget	text, nextButton, prevButton, indexLabel;
    167 static Widget	fileDialog, fileDialogShell;
    168 static Widget	failDialog, failDialogShell;
    169 
    170 static int
    171 IndexCurrentClip (void)
    172 {
    173     int	i = 0;
    174     ClipPtr clip;
    175 
    176     for (clip = currentClip; clip; clip = clip->prev)
    177 	i++;
    178     return i;
    179 }
    180 
    181 static void
    182 set_button_state (void)
    183 {
    184     Boolean prevvalid, nextvalid;
    185     Arg arg;
    186     char labelString[12];
    187 
    188     prevvalid = currentClip->prev != NULL;
    189     nextvalid = currentClip->next != NULL;
    190     XtSetArg (arg, XtNsensitive, prevvalid);
    191     XtSetValues (prevButton, &arg, ONE);
    192     XtSetArg (arg, XtNsensitive, nextvalid);
    193     XtSetValues (nextButton, &arg, ONE);
    194     snprintf (labelString, sizeof(labelString), "%d", IndexCurrentClip ());
    195     XtSetArg (arg, XtNlabel, labelString);
    196     XtSetValues (indexLabel, &arg, ONE);
    197 }
    198 
    199 /* ARGSUSED */
    200 static void
    201 NextCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
    202 {
    203     if (currentClip->next)
    204     {
    205 	SaveClip (text, currentClip);
    206 	currentClip = currentClip->next;
    207 	RestoreClip (text, currentClip);
    208 	set_button_state ();
    209     }
    210 }
    211 
    212 /* ARGSUSED */
    213 static void
    214 PrevCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
    215 {
    216     if (currentClip->prev)
    217     {
    218 	SaveClip (text, currentClip);
    219 	currentClip = currentClip->prev;
    220 	RestoreClip (text, currentClip);
    221 	set_button_state ();
    222     }
    223 }
    224 
    225 /* ARGSUSED */
    226 static void
    227 DeleteCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
    228 {
    229     ClipPtr newCurrent;
    230 
    231     if (currentClip->prev)
    232 	newCurrent = currentClip->prev;
    233     else
    234 	newCurrent = currentClip->next;
    235     if (newCurrent)
    236     {
    237 	DeleteClip (text, currentClip);
    238 	currentClip = newCurrent;
    239 	RestoreClip (text, currentClip);
    240     }
    241     else
    242 	EraseTextWidget ();
    243     set_button_state ();
    244 }
    245 
    246 /* ARGSUSED */
    247 static void _X_NORETURN
    248 Quit(Widget w, XEvent *ev, String *parms, Cardinal *np)
    249 {
    250     XtCloseDisplay  (XtDisplay (text));
    251     exit (0);
    252 }
    253 
    254 static void
    255 CenterWidgetAtPoint(Widget w, int x, int y)
    256 {
    257     Arg	args[2];
    258     Dimension	width, height;
    259 
    260     XtSetArg(args[0], XtNwidth, &width);
    261     XtSetArg(args[1], XtNheight, &height);
    262     XtGetValues (w, args, 2);
    263     x = x - (int) width / 2;
    264     y = y - (int) height / 2;
    265     if (x < 0)
    266 	x = 0;
    267     else {
    268 	int scr_width = WidthOfScreen (XtScreen(w));
    269 	if (x + (int)width > scr_width)
    270 	    x = scr_width - width;
    271     }
    272     if (y < 0)
    273 	y = 0;
    274     else {
    275 	int scr_height = HeightOfScreen (XtScreen(w));
    276 	if (y + (int)height > scr_height)
    277 	    y = scr_height - height;
    278     }
    279     XtSetArg(args[0], XtNx, x);
    280     XtSetArg(args[1], XtNy, y);
    281     XtSetValues (w, args, 2);
    282 }
    283 
    284 static void
    285 CenterWidgetOnEvent(Widget w, XEvent *e)
    286 {
    287     CenterWidgetAtPoint (w, e->xbutton.x_root, e->xbutton.y_root);
    288 }
    289 
    290 static void
    291 CenterWidgetOnWidget(Widget w, Widget wT)
    292 {
    293     Position	rootX, rootY;
    294     Dimension	width, height;
    295     Arg		args[2];
    296 
    297     XtSetArg (args[0], XtNwidth, &width);
    298     XtSetArg (args[1], XtNheight, &height);
    299     XtGetValues (wT, args, 2);
    300     XtTranslateCoords (wT, (Position) width/2, (Position) height/2, &rootX, &rootY);
    301     CenterWidgetAtPoint (w, (int) rootX, (int) rootY);
    302 }
    303 
    304 /*ARGSUSED*/
    305 static void
    306 SaveToFile(Widget w, XEvent *e, String *argv, Cardinal *argc)
    307 {
    308     Arg	    args[1];
    309     const char    *filename;
    310 
    311     filename = "clipboard";
    312     if (currentClip->filename)
    313 	filename = currentClip->filename;
    314     XtSetArg(args[0], XtNvalue, filename);
    315     XtSetValues (fileDialog, args, 1);
    316     CenterWidgetOnEvent (fileDialogShell, e);
    317     XtPopup (fileDialogShell, XtGrabNone);
    318 }
    319 
    320 /*ARGSUSED*/
    321 static void
    322 AcceptSaveFile(Widget w, XEvent *e, String *argv, Cardinal *argc)
    323 {
    324     char    *filename;
    325     Bool    success;
    326     Arg	    args[1];
    327 
    328     filename = XawDialogGetValueString (fileDialog);
    329     success = XawAsciiSaveAsFile (XawTextGetSource (text), filename);
    330     XtPopdown (fileDialogShell);
    331     if (!success)
    332     {
    333 	char	*failMessage;
    334 
    335 	XtAsprintf (&failMessage, "Can't open file \"%s\"", filename);
    336 	XtSetArg (args[0], XtNlabel, failMessage);
    337 	XtSetValues (failDialog, args, 1);
    338 	CenterWidgetOnEvent (failDialogShell, e);
    339 	XtPopup (failDialogShell, XtGrabNone);
    340 	XtFree (failMessage);
    341     }
    342     else
    343     {
    344 	if (currentClip->filename)
    345 	    free (currentClip->filename);
    346 	currentClip->filename = strdup (filename);
    347     }
    348 }
    349 
    350 /* ARGSUSED */
    351 static void
    352 CancelSaveFile(Widget w, XEvent *ev, String *parms, Cardinal *np)
    353 {
    354     XtPopdown (fileDialogShell);
    355 }
    356 
    357 /* ARGSUSED */
    358 static void
    359 FailContinue(Widget w, XEvent *ev, String *parms, Cardinal *np)
    360 {
    361     XtPopdown (failDialogShell);
    362 }
    363 
    364 /*ARGSUSED*/
    365 static void
    366 WMProtocols(Widget w, XEvent *ev, String *params, Cardinal *n)
    367 {
    368     if (ev->type == ClientMessage &&
    369 	ev->xclient.message_type == wm_protocols &&
    370 	ev->xclient.data.l[0] == (long) wm_delete_window) {
    371 	while (w && !XtIsShell(w))
    372 	    w = XtParent(w);
    373 	if (w == top)
    374 	    Quit(w, ev, params, n);
    375 	else if (w == fileDialogShell)
    376 	    CancelSaveFile(w, ev, params, n);
    377 	else if (w == failDialogShell)
    378 	    FailContinue(w, ev, params, n);
    379     }
    380 }
    381 
    382 static char empty_string[] = "";
    383 
    384 /* ARGUSED */
    385 static void
    386 NewCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
    387 {
    388     NewCurrentClipContents (empty_string, 0);
    389 }
    390 
    391 static void
    392 NewCurrentClipContents(char *data, int len)
    393 {
    394     XawTextBlock textBlock;
    395 
    396     SaveClip (text, currentClip);
    397 
    398     /* append new clips at the end */
    399     while (currentClip && currentClip->next)
    400 	currentClip = currentClip->next;
    401     /* any trailing clips with no text get overwritten */
    402     if ((currentClip == NULL) || (strlen (currentClip->clip) != 0))
    403 	currentClip = NewClip (text, currentClip);
    404 
    405     textBlock.ptr = data;
    406     textBlock.firstPos = 0;
    407     textBlock.length = len;
    408     textBlock.format = FMT8BIT;
    409     if (XawTextReplace(text, 0, TextLength (text), &textBlock)) {
    410 #ifdef XKB
    411 	XkbStdBell(XtDisplay(text), XtWindow(text), 0, XkbBI_Info);
    412 #else
    413 	XBell( XtDisplay(text), 0);
    414 #endif
    415     }
    416     set_button_state ();
    417 }
    418 
    419 static void
    420 EraseTextWidget(void)
    421 {
    422     XawTextBlock block;
    423 
    424     block.ptr = empty_string;
    425     block.length = 0;
    426     block.firstPos = 0;
    427     block.format = FMT8BIT;
    428 
    429     XawTextReplace(text, 0, INFINITY, &block);
    430     /* If this fails, too bad. */
    431 }
    432 
    433 
    434 static XtActionsRec xclipboard_actions[] = {
    435     { "NewClip", 	NewCurrentClip },
    436     { "NextClip",	NextCurrentClip },
    437     { "PrevClip",	PrevCurrentClip },
    438     { "DeleteClip",	DeleteCurrentClip },
    439     { "Save",		SaveToFile },
    440     { "AcceptSave",	AcceptSaveFile },
    441     { "CancelSave",	CancelSaveFile },
    442     { "FailContinue",	FailContinue },
    443     { "Quit",		Quit },
    444     { "WMProtocols",	WMProtocols }
    445 };
    446 
    447 static XrmOptionDescRec table[] = {
    448     {"-w",	    "wrap",		XrmoptionNoArg,  "on"},
    449 /*    {"-nw",	    "wrap",		XrmoptionNoArg,  "False"} */
    450 };
    451 
    452 static Boolean ConvertSelection ( Widget w, Atom *selection, Atom *target,
    453 				  Atom *type, XtPointer *value,
    454 				  unsigned long *length, int *format );
    455 static void LoseSelection ( Widget w, Atom *selection );
    456 
    457 static Atom	ManagerAtom, ClipboardAtom;
    458 
    459 /*ARGSUSED*/
    460 static void
    461 InsertClipboard(Widget w, XtPointer client_data, Atom *selection,
    462 		Atom *type, XtPointer value, unsigned long *length,
    463 		int *format)
    464 {
    465     Display *d = XtDisplay(w);
    466     Atom target = *((Atom *)client_data);
    467     Boolean convert_failed = (*type == XT_CONVERT_FAIL);
    468 
    469     if (!convert_failed)
    470     {
    471 	char **list;
    472 	int i, ret, count;
    473 
    474 	XTextProperty prop;
    475 	prop.value = value;
    476 	prop.nitems = *length;
    477 	prop.format = *format;
    478 	prop.encoding = *type;
    479 	ret = XmbTextPropertyToTextList(d, &prop, &list, &count);
    480 	if (ret >= Success)
    481 	{
    482 	    /* manuals say something about multiple strings in a disjoint
    483 	    text selection (?), it should be harmless to get them all */
    484 	    for (i = 0; i < count; i++)
    485 		NewCurrentClipContents(list[i], strlen(list[i]));
    486 	    XFreeStringList(list);
    487 	} else
    488 	    convert_failed = True;
    489 	XFree(value);
    490     }
    491 
    492     if (convert_failed) {
    493 	/* if UTF8_STRING failed try COMPOUND_TEXT */
    494 	if (target == XA_UTF8_STRING(d))
    495 	{
    496 	    Atom compound_text = XA_COMPOUND_TEXT(d);
    497 	    XtGetSelectionValue(w, *selection, compound_text,
    498 				InsertClipboard,
    499 				&compound_text,
    500 				CurrentTime);
    501 	    return;
    502 	}
    503 	/* if COMPOUND_TEXT failed try STRING */
    504 	else if (target == XA_COMPOUND_TEXT(d))
    505 	{
    506 	    XtGetSelectionValue(w, *selection, XA_STRING,
    507 				InsertClipboard,
    508 				NULL,
    509 				CurrentTime);
    510 	    return;
    511 	}
    512 	/* all conversions failed */
    513 	else
    514 	{
    515 	    Arg arg;
    516 	    XtSetArg (arg, XtNlabel, "CLIPBOARD selection conversion failed");
    517 	    XtSetValues (failDialog, &arg, 1);
    518 	    CenterWidgetOnWidget (failDialogShell, text);
    519 	    XtPopup (failDialogShell, XtGrabNone);
    520 #ifdef XKB
    521 	    XkbStdBell (d, XtWindow(w), 0, XkbBI_MinorError);
    522 #else
    523 	    XBell (d, 0);
    524 #endif
    525 	}
    526     }
    527 
    528     XtOwnSelection(top, ClipboardAtom, CurrentTime,
    529 		   ConvertSelection, LoseSelection, NULL);
    530 }
    531 
    532 static Boolean
    533 ConvertSelection(Widget w, Atom *selection, Atom *target,
    534 		 Atom *type, XtPointer *value, unsigned long *length,
    535 		 int *format)
    536 {
    537     Display* d = XtDisplay(w);
    538     XSelectionRequestEvent* req =
    539 	XtGetSelectionRequest(w, *selection, (XtRequestId)NULL);
    540 
    541     if (*target == XA_TARGETS(d)) {
    542 	Atom* targetP;
    543 	Atom* std_targets;
    544 	unsigned long std_length;
    545 	XmuConvertStandardSelection(w, req->time, selection, target, type,
    546 				    (XPointer*)&std_targets, &std_length,
    547 				    format);
    548 	*value = XtMalloc(sizeof(Atom)*(std_length + 7));
    549 	targetP = *(Atom**)value;
    550 	*targetP++ = XA_STRING;
    551 	*targetP++ = XA_TEXT(d);
    552 	*targetP++ = XA_UTF8_STRING(d);
    553 	*targetP++ = XA_COMPOUND_TEXT(d);
    554 	*targetP++ = XA_LENGTH(d);
    555 	*targetP++ = XA_LIST_LENGTH(d);
    556 	*targetP++ = XA_CHARACTER_POSITION(d);
    557 	*length = std_length + (targetP - (*(Atom **) value));
    558 	memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length);
    559 	XtFree((char*)std_targets);
    560 	*type = XA_ATOM;
    561 	*format = 32;
    562 	return True;
    563     }
    564 
    565     if (*target == XA_LIST_LENGTH(d) ||
    566 	*target == XA_LENGTH(d))
    567     {
    568     	long * temp;
    569 
    570     	temp = (long *) XtMalloc(sizeof(long));
    571     	if (*target == XA_LIST_LENGTH(d))
    572       	  *temp = 1L;
    573     	else			/* *target == XA_LENGTH(d) */
    574       	  *temp = (long) TextLength (text);
    575 
    576     	*value = (XPointer) temp;
    577     	*type = XA_INTEGER;
    578     	*length = 1L;
    579     	*format = 32;
    580     	return True;
    581     }
    582 
    583     if (*target == XA_CHARACTER_POSITION(d))
    584     {
    585     	long * temp;
    586 
    587     	temp = (long *) XtMalloc(2 * sizeof(long));
    588     	temp[0] = (long) 0;
    589     	temp[1] = TextLength (text);
    590     	*value = (XPointer) temp;
    591     	*type = XA_SPAN(d);
    592     	*length = 2L;
    593     	*format = 32;
    594     	return True;
    595     }
    596 
    597     if (*target == XA_STRING ||
    598 	*target == XA_TEXT(d) ||
    599 	*target == XA_UTF8_STRING(d) ||
    600 	*target == XA_COMPOUND_TEXT(d))
    601     {
    602 	Arg args[1];
    603 	Widget source;
    604 	XTextProperty prop;
    605 	int ret, style = XStdICCTextStyle; /* a safe default for TEXT */
    606 	char *data;
    607 
    608 	source = XawTextGetSource (text);
    609 	XtSetArg (args[0], XtNstring, &data);
    610 	XtGetValues (source, args, 1);
    611 
    612 	if (*target == XA_UTF8_STRING(d))
    613 	    style = XUTF8StringStyle;
    614 	else if (*target == XA_COMPOUND_TEXT(d))
    615 	    style = XCompoundTextStyle;
    616 	else if (*target == XA_STRING)
    617 	    style = XStringStyle;
    618 
    619 	ret = XmbTextListToTextProperty (d, &data, 1, style, &prop);
    620 	if (ret >= Success) {
    621 	    *length = prop.nitems;
    622 	    *value = prop.value;
    623 	    *type = prop.encoding;
    624 	    *format = prop.format;
    625 	    return True;
    626 	} else
    627 	    return False;
    628     }
    629 
    630     if (XmuConvertStandardSelection(w, req->time, selection, target, type,
    631 				    (XPointer *) value, length, format))
    632 	return True;
    633 
    634     return False;
    635 }
    636 
    637 static void
    638 LoseSelection(Widget w, Atom *selection)
    639 {
    640     Display *d = XtDisplay(w);
    641     Atom utf8_string = XA_UTF8_STRING(d);
    642     XtGetSelectionValue(w, *selection, utf8_string, InsertClipboard,
    643 			&utf8_string, CurrentTime);
    644 }
    645 
    646 /*ARGSUSED*/
    647 static Boolean
    648 RefuseSelection(Widget w, Atom *selection, Atom *target,
    649 		Atom *type, XtPointer *value, unsigned long *length,
    650 		int *format)
    651 {
    652     return False;
    653 }
    654 
    655 /*ARGSUSED*/
    656 static void
    657 LoseManager(Widget w, Atom *selection)
    658 {
    659     XtError("another clipboard has taken over control\n");
    660 }
    661 
    662 typedef struct {
    663   Boolean wrap;
    664 } ResourceData, *ResourceDataPtr;
    665 
    666 static ResourceData userOptions;
    667 
    668 #define Offset(field) XtOffsetOf(ResourceData, field)
    669 
    670 static XtResource resources[] = {
    671   {"wrap", "Wrap", XtRBoolean, sizeof(Boolean),
    672      Offset(wrap), XtRImmediate, (XtPointer)False}
    673 };
    674 
    675 #undef Offset
    676 
    677 int
    678 main(int argc, char *argv[])
    679 {
    680     Arg args[4];
    681     Cardinal n;
    682     XtAppContext xtcontext;
    683     Widget parent;
    684 
    685     XtSetLanguageProc(NULL, NULL, NULL);
    686 
    687     top = XtAppInitialize( &xtcontext, "XClipboard", table, XtNumber(table),
    688 			  &argc, argv, fallback_resources, NULL, 0);
    689 
    690     XtGetApplicationResources(top, (XtPointer)&userOptions, resources,
    691 			      XtNumber(resources), NULL, 0);
    692 
    693     XtAppAddActions (xtcontext,
    694 		     xclipboard_actions, XtNumber (xclipboard_actions));
    695     /* CLIPBOARD_MANAGER is a non-standard mechanism */
    696     ManagerAtom = XInternAtom(XtDisplay(top), "CLIPBOARD_MANAGER", False);
    697     ClipboardAtom = XA_CLIPBOARD(XtDisplay(top));
    698     if (XGetSelectionOwner(XtDisplay(top), ManagerAtom))
    699 	XtError("another clipboard is already running\n");
    700 
    701     parent = XtCreateManagedWidget("form", formWidgetClass, top, NULL, ZERO);
    702     (void) XtCreateManagedWidget("quit", Command, parent, NULL, ZERO);
    703     (void) XtCreateManagedWidget("delete", Command, parent, NULL, ZERO);
    704     (void) XtCreateManagedWidget("new", Command, parent, NULL, ZERO);
    705     (void) XtCreateManagedWidget("save", Command, parent, NULL, ZERO);
    706     nextButton = XtCreateManagedWidget("next", Command, parent, NULL, ZERO);
    707     prevButton = XtCreateManagedWidget("prev", Command, parent, NULL, ZERO);
    708     indexLabel = XtCreateManagedWidget("index", Label, parent, NULL, ZERO);
    709 
    710     n=0;
    711     XtSetArg(args[n], XtNtype, XawAsciiString); n++;
    712     XtSetArg(args[n], XtNeditType, XawtextEdit); n++;
    713     if (userOptions.wrap) {
    714 	XtSetArg(args[n], XtNwrap, XawtextWrapWord); n++;
    715 	XtSetArg(args[n], XtNscrollHorizontal, False); n++;
    716     }
    717 
    718     text = XtCreateManagedWidget( "text", Text, parent, args, n);
    719 
    720     currentClip = NewClip (text, (ClipPtr) 0);
    721 
    722     set_button_state ();
    723 
    724     fileDialogShell = XtCreatePopupShell("fileDialogShell",
    725 					 transientShellWidgetClass,
    726 					 top, NULL, ZERO);
    727     fileDialog = XtCreateManagedWidget ("fileDialog", dialogWidgetClass,
    728 					fileDialogShell, NULL, ZERO);
    729     XawDialogAddButton(fileDialog, "accept", NULL, NULL);
    730     XawDialogAddButton(fileDialog, "cancel", NULL, NULL);
    731 
    732     failDialogShell = XtCreatePopupShell("failDialogShell",
    733 					 transientShellWidgetClass,
    734 					 top, NULL, ZERO);
    735     failDialog = XtCreateManagedWidget ("failDialog", dialogWidgetClass,
    736 					failDialogShell, NULL, ZERO);
    737     XawDialogAddButton (failDialog, "continue", NULL, NULL);
    738 
    739     XtRealizeWidget(top);
    740     XtRealizeWidget(fileDialogShell);
    741     XtRealizeWidget(failDialogShell);
    742     XtOwnSelection(top, ManagerAtom, CurrentTime,
    743 		   RefuseSelection, LoseManager, NULL);
    744     if (XGetSelectionOwner (XtDisplay(top), ClipboardAtom)) {
    745 	LoseSelection (top, &ClipboardAtom);
    746     } else {
    747     	XtOwnSelection(top, ClipboardAtom, CurrentTime,
    748 		       ConvertSelection, LoseSelection, NULL);
    749     }
    750     wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False);
    751     wm_protocols = XInternAtom(XtDisplay(top), "WM_PROTOCOLS", False);
    752     (void) XSetWMProtocols(XtDisplay(top), XtWindow(top), &wm_delete_window,1);
    753     (void) XSetWMProtocols(XtDisplay(top), XtWindow(fileDialogShell),
    754 			   &wm_delete_window,1);
    755     (void) XSetWMProtocols(XtDisplay(top), XtWindow(failDialogShell),
    756 			   &wm_delete_window,1);
    757     XtAppMainLoop(xtcontext);
    758     exit(0);
    759 }
    760