1/*
2
3Copyright 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in 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@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
66typedef struct _Clip {
67    struct _Clip    *next, *prev;
68    char	    *clip;
69    char	    *filename;
70    size_t	    avail;
71} ClipRec, *ClipPtr;
72
73static Atom wm_delete_window;
74static Atom wm_protocols;
75
76static void EraseTextWidget ( void );
77static void NewCurrentClipContents ( char *data, int len );
78
79static String fallback_resources[] = {
80    "*international: true",
81    NULL
82};
83
84static long
85TextLength(Widget w)
86{
87    return XawTextSourceScan (XawTextGetSource (w),
88			      (XawTextPosition) 0,
89 			      XawstAll, XawsdRight, 1, TRUE);
90}
91
92static void
93SaveClip(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
120static void
121RestoreClip(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*/
132static ClipPtr
133NewClip(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*/
152static void
153DeleteClip(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
164static ClipPtr	currentClip;
165static Widget	top;
166static Widget	text, nextButton, prevButton, indexLabel;
167static Widget	fileDialog, fileDialogShell;
168static Widget	failDialog, failDialogShell;
169
170static int
171IndexCurrentClip (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
181static void
182set_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 */
200static void
201NextCurrentClip(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 */
213static void
214PrevCurrentClip(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 */
226static void
227DeleteCurrentClip(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 */
247static void _X_NORETURN
248Quit(Widget w, XEvent *ev, String *parms, Cardinal *np)
249{
250    XtCloseDisplay  (XtDisplay (text));
251    exit (0);
252}
253
254static void
255CenterWidgetAtPoint(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
284static void
285CenterWidgetOnEvent(Widget w, XEvent *e)
286{
287    CenterWidgetAtPoint (w, e->xbutton.x_root, e->xbutton.y_root);
288}
289
290static void
291CenterWidgetOnWidget(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*/
305static void
306SaveToFile(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*/
321static void
322AcceptSaveFile(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 */
351static void
352CancelSaveFile(Widget w, XEvent *ev, String *parms, Cardinal *np)
353{
354    XtPopdown (fileDialogShell);
355}
356
357/* ARGSUSED */
358static void
359FailContinue(Widget w, XEvent *ev, String *parms, Cardinal *np)
360{
361    XtPopdown (failDialogShell);
362}
363
364/*ARGSUSED*/
365static void
366WMProtocols(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
382static char empty_string[] = "";
383
384/* ARGUSED */
385static void
386NewCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
387{
388    NewCurrentClipContents (empty_string, 0);
389}
390
391static void
392NewCurrentClipContents(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
419static void
420EraseTextWidget(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
434static 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
447static XrmOptionDescRec table[] = {
448    {"-w",	    "wrap",		XrmoptionNoArg,  "on"},
449/*    {"-nw",	    "wrap",		XrmoptionNoArg,  "False"} */
450};
451
452static Boolean ConvertSelection ( Widget w, Atom *selection, Atom *target,
453				  Atom *type, XtPointer *value,
454				  unsigned long *length, int *format );
455static void LoseSelection ( Widget w, Atom *selection );
456
457static Atom	ManagerAtom, ClipboardAtom;
458
459/*ARGSUSED*/
460static void
461InsertClipboard(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
532static Boolean
533ConvertSelection(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
637static void
638LoseSelection(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*/
647static Boolean
648RefuseSelection(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*/
656static void
657LoseManager(Widget w, Atom *selection)
658{
659    XtError("another clipboard has taken over control\n");
660}
661
662typedef struct {
663  Boolean wrap;
664} ResourceData, *ResourceDataPtr;
665
666static ResourceData userOptions;
667
668#define Offset(field) XtOffsetOf(ResourceData, field)
669
670static XtResource resources[] = {
671  {"wrap", "Wrap", XtRBoolean, sizeof(Boolean),
672     Offset(wrap), XtRImmediate, (XtPointer)False}
673};
674
675#undef Offset
676
677int
678main(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