xclipboard.c revision 219ffec0
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	return newClip;
140    newClip->prev = old;
141    if (old)
142    {
143	newClip->next = old->next;
144	old->next = newClip;
145    }
146    return newClip;
147}
148
149/*ARGSUSED*/
150static void
151DeleteClip(Widget w, ClipPtr clip)
152{
153    if (clip->prev)
154	clip->prev->next = clip->next;
155    if (clip->next)
156	clip->next->prev = clip->prev;
157    if (clip->clip)
158	free (clip->clip);
159    free ((char *) clip);
160}
161
162static ClipPtr	currentClip;
163static Widget	top;
164static Widget	text, nextButton, prevButton, indexLabel;
165static Widget	fileDialog, fileDialogShell;
166static Widget	failDialog, failDialogShell;
167
168static int
169IndexCurrentClip (void)
170{
171    int	i = 0;
172    ClipPtr clip;
173
174    for (clip = currentClip; clip; clip = clip->prev)
175	i++;
176    return i;
177}
178
179static void
180set_button_state (void)
181{
182    Boolean prevvalid, nextvalid;
183    Arg arg;
184    char labelString[12];
185
186    prevvalid = currentClip->prev != NULL;
187    nextvalid = currentClip->next != NULL;
188    XtSetArg (arg, XtNsensitive, prevvalid);
189    XtSetValues (prevButton, &arg, ONE);
190    XtSetArg (arg, XtNsensitive, nextvalid);
191    XtSetValues (nextButton, &arg, ONE);
192    snprintf (labelString, sizeof(labelString), "%d", IndexCurrentClip ());
193    XtSetArg (arg, XtNlabel, labelString);
194    XtSetValues (indexLabel, &arg, ONE);
195}
196
197/* ARGSUSED */
198static void
199NextCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
200{
201    if (currentClip->next)
202    {
203	SaveClip (text, currentClip);
204	currentClip = currentClip->next;
205	RestoreClip (text, currentClip);
206	set_button_state ();
207    }
208}
209
210/* ARGSUSED */
211static void
212PrevCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
213{
214    if (currentClip->prev)
215    {
216	SaveClip (text, currentClip);
217	currentClip = currentClip->prev;
218	RestoreClip (text, currentClip);
219	set_button_state ();
220    }
221}
222
223/* ARGSUSED */
224static void
225DeleteCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
226{
227    ClipPtr newCurrent;
228
229    if (currentClip->prev)
230	newCurrent = currentClip->prev;
231    else
232	newCurrent = currentClip->next;
233    if (newCurrent)
234    {
235	DeleteClip (text, currentClip);
236	currentClip = newCurrent;
237	RestoreClip (text, currentClip);
238    }
239    else
240	EraseTextWidget ();
241    set_button_state ();
242}
243
244/* ARGSUSED */
245static void _X_NORETURN
246Quit(Widget w, XEvent *ev, String *parms, Cardinal *np)
247{
248    XtCloseDisplay  (XtDisplay (text));
249    exit (0);
250}
251
252static void
253CenterWidgetAtPoint(Widget w, int x, int y)
254{
255    Arg	args[2];
256    Dimension	width, height;
257
258    XtSetArg(args[0], XtNwidth, &width);
259    XtSetArg(args[1], XtNheight, &height);
260    XtGetValues (w, args, 2);
261    x = x - (int) width / 2;
262    y = y - (int) height / 2;
263    if (x < 0)
264	x = 0;
265    else {
266	int scr_width = WidthOfScreen (XtScreen(w));
267	if (x + (int)width > scr_width)
268	    x = scr_width - width;
269    }
270    if (y < 0)
271	y = 0;
272    else {
273	int scr_height = HeightOfScreen (XtScreen(w));
274	if (y + (int)height > scr_height)
275	    y = scr_height - height;
276    }
277    XtSetArg(args[0], XtNx, x);
278    XtSetArg(args[1], XtNy, y);
279    XtSetValues (w, args, 2);
280}
281
282static void
283CenterWidgetOnEvent(Widget w, XEvent *e)
284{
285    CenterWidgetAtPoint (w, e->xbutton.x_root, e->xbutton.y_root);
286}
287
288static void
289CenterWidgetOnWidget(Widget w, Widget wT)
290{
291    Position	rootX, rootY;
292    Dimension	width, height;
293    Arg		args[2];
294
295    XtSetArg (args[0], XtNwidth, &width);
296    XtSetArg (args[1], XtNheight, &height);
297    XtGetValues (wT, args, 2);
298    XtTranslateCoords (wT, (Position) width/2, (Position) height/2, &rootX, &rootY);
299    CenterWidgetAtPoint (w, (int) rootX, (int) rootY);
300}
301
302/*ARGSUSED*/
303static void
304SaveToFile(Widget w, XEvent *e, String *argv, Cardinal *argc)
305{
306    Arg	    args[1];
307    const char    *filename;
308
309    filename = "clipboard";
310    if (currentClip->filename)
311	filename = currentClip->filename;
312    XtSetArg(args[0], XtNvalue, filename);
313    XtSetValues (fileDialog, args, 1);
314    CenterWidgetOnEvent (fileDialogShell, e);
315    XtPopup (fileDialogShell, XtGrabNone);
316}
317
318/*ARGSUSED*/
319static void
320AcceptSaveFile(Widget w, XEvent *e, String *argv, Cardinal *argc)
321{
322    char    *filename;
323    Bool    success;
324    Arg	    args[1];
325
326    filename = XawDialogGetValueString (fileDialog);
327    success = XawAsciiSaveAsFile (XawTextGetSource (text), filename);
328    XtPopdown (fileDialogShell);
329    if (!success)
330    {
331	char	*failMessage;
332
333	XtAsprintf (&failMessage, "Can't open file \"%s\"", filename);
334	XtSetArg (args[0], XtNlabel, failMessage);
335	XtSetValues (failDialog, args, 1);
336	CenterWidgetOnEvent (failDialogShell, e);
337	XtPopup (failDialogShell, XtGrabNone);
338	XtFree (failMessage);
339    }
340    else
341    {
342	if (currentClip->filename)
343	    free (currentClip->filename);
344	currentClip->filename = strdup (filename);
345    }
346}
347
348/* ARGSUSED */
349static void
350CancelSaveFile(Widget w, XEvent *ev, String *parms, Cardinal *np)
351{
352    XtPopdown (fileDialogShell);
353}
354
355/* ARGSUSED */
356static void
357FailContinue(Widget w, XEvent *ev, String *parms, Cardinal *np)
358{
359    XtPopdown (failDialogShell);
360}
361
362/*ARGSUSED*/
363static void
364WMProtocols(Widget w, XEvent *ev, String *params, Cardinal *n)
365{
366    if (ev->type == ClientMessage &&
367	ev->xclient.message_type == wm_protocols &&
368	ev->xclient.data.l[0] == (long) wm_delete_window) {
369	while (w && !XtIsShell(w))
370	    w = XtParent(w);
371	if (w == top)
372	    Quit(w, ev, params, n);
373	else if (w == fileDialogShell)
374	    CancelSaveFile(w, ev, params, n);
375	else if (w == failDialogShell)
376	    FailContinue(w, ev, params, n);
377    }
378}
379
380/* ARGUSED */
381static void
382NewCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
383{
384    NewCurrentClipContents ("", 0);
385}
386
387static void
388NewCurrentClipContents(char *data, int len)
389{
390    XawTextBlock textBlock;
391
392    SaveClip (text, currentClip);
393
394    /* append new clips at the end */
395    while (currentClip && currentClip->next)
396	currentClip = currentClip->next;
397    /* any trailing clips with no text get overwritten */
398    if ((currentClip == NULL) || (strlen (currentClip->clip) != 0))
399	currentClip = NewClip (text, currentClip);
400
401    textBlock.ptr = data;
402    textBlock.firstPos = 0;
403    textBlock.length = len;
404    textBlock.format = FMT8BIT;
405    if (XawTextReplace(text, 0, TextLength (text), &textBlock)) {
406#ifdef XKB
407	XkbStdBell(XtDisplay(text), XtWindow(text), 0, XkbBI_Info);
408#else
409	XBell( XtDisplay(text), 0);
410#endif
411    }
412    set_button_state ();
413}
414
415static void
416EraseTextWidget(void)
417{
418    XawTextBlock block;
419
420    block.ptr = "";
421    block.length = 0;
422    block.firstPos = 0;
423    block.format = FMT8BIT;
424
425    XawTextReplace(text, 0, INFINITY, &block);
426    /* If this fails, too bad. */
427}
428
429
430static XtActionsRec xclipboard_actions[] = {
431    { "NewClip", 	NewCurrentClip },
432    { "NextClip",	NextCurrentClip },
433    { "PrevClip",	PrevCurrentClip },
434    { "DeleteClip",	DeleteCurrentClip },
435    { "Save",		SaveToFile },
436    { "AcceptSave",	AcceptSaveFile },
437    { "CancelSave",	CancelSaveFile },
438    { "FailContinue",	FailContinue },
439    { "Quit",		Quit },
440    { "WMProtocols",	WMProtocols }
441};
442
443static XrmOptionDescRec table[] = {
444    {"-w",	    "wrap",		XrmoptionNoArg,  "on"},
445/*    {"-nw",	    "wrap",		XrmoptionNoArg,  "False"} */
446};
447
448static Boolean ConvertSelection ( Widget w, Atom *selection, Atom *target,
449				  Atom *type, XtPointer *value,
450				  unsigned long *length, int *format );
451static void LoseSelection ( Widget w, Atom *selection );
452
453static Atom	ManagerAtom, ClipboardAtom;
454
455/*ARGSUSED*/
456static void
457InsertClipboard(Widget w, XtPointer client_data, Atom *selection,
458		Atom *type, XtPointer value, unsigned long *length,
459		int *format)
460{
461    Display *d = XtDisplay(w);
462    Atom target = (Atom)client_data;
463    Boolean convert_failed = (*type == XT_CONVERT_FAIL);
464
465    if (!convert_failed)
466    {
467	char **list;
468	int i, ret, count;
469
470	XTextProperty prop;
471	prop.value = value;
472	prop.nitems = *length;
473	prop.format = *format;
474	prop.encoding = *type;
475	ret = XmbTextPropertyToTextList(d, &prop, &list, &count);
476	if (ret >= Success)
477	{
478	    /* manuals say something about multiple strings in a disjoint
479	    text selection (?), it should be harmless to get them all */
480	    for (i = 0; i < count; i++)
481		NewCurrentClipContents(list[i], strlen(list[i]));
482	    XFreeStringList(list);
483	} else
484	    convert_failed = True;
485	XFree(value);
486    }
487
488    if (convert_failed) {
489	/* if UTF8_STRING failed try COMPOUND_TEXT */
490	if (target == XA_UTF8_STRING(d))
491	{
492	    XtGetSelectionValue(w, *selection, XA_COMPOUND_TEXT(d),
493				InsertClipboard,
494				(XtPointer)(XA_COMPOUND_TEXT(d)),
495				CurrentTime);
496	    return;
497	}
498	/* if COMPOUND_TEXT failed try STRING */
499	else if (target == XA_COMPOUND_TEXT(d))
500	{
501	    XtGetSelectionValue(w, *selection, XA_STRING,
502				InsertClipboard,
503				NULL,
504				CurrentTime);
505	    return;
506	}
507	/* all conversions failed */
508	else
509	{
510	    Arg arg;
511	    XtSetArg (arg, XtNlabel, "CLIPBOARD selection conversion failed");
512	    XtSetValues (failDialog, &arg, 1);
513	    CenterWidgetOnWidget (failDialogShell, text);
514	    XtPopup (failDialogShell, XtGrabNone);
515#ifdef XKB
516	    XkbStdBell (d, XtWindow(w), 0, XkbBI_MinorError);
517#else
518	    XBell (d, 0);
519#endif
520	}
521    }
522
523    XtOwnSelection(top, ClipboardAtom, CurrentTime,
524		   ConvertSelection, LoseSelection, NULL);
525}
526
527static Boolean
528ConvertSelection(Widget w, Atom *selection, Atom *target,
529		 Atom *type, XtPointer *value, unsigned long *length,
530		 int *format)
531{
532    Display* d = XtDisplay(w);
533    XSelectionRequestEvent* req =
534	XtGetSelectionRequest(w, *selection, (XtRequestId)NULL);
535
536    if (*target == XA_TARGETS(d)) {
537	Atom* targetP;
538	Atom* std_targets;
539	unsigned long std_length;
540	XmuConvertStandardSelection(w, req->time, selection, target, type,
541				    (XPointer*)&std_targets, &std_length,
542				    format);
543	*value = XtMalloc(sizeof(Atom)*(std_length + 7));
544	targetP = *(Atom**)value;
545	*targetP++ = XA_STRING;
546	*targetP++ = XA_TEXT(d);
547	*targetP++ = XA_UTF8_STRING(d);
548	*targetP++ = XA_COMPOUND_TEXT(d);
549	*targetP++ = XA_LENGTH(d);
550	*targetP++ = XA_LIST_LENGTH(d);
551	*targetP++ = XA_CHARACTER_POSITION(d);
552	*length = std_length + (targetP - (*(Atom **) value));
553	memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length);
554	XtFree((char*)std_targets);
555	*type = XA_ATOM;
556	*format = 32;
557	return True;
558    }
559
560    if (*target == XA_LIST_LENGTH(d) ||
561	*target == XA_LENGTH(d))
562    {
563    	long * temp;
564
565    	temp = (long *) XtMalloc(sizeof(long));
566    	if (*target == XA_LIST_LENGTH(d))
567      	  *temp = 1L;
568    	else			/* *target == XA_LENGTH(d) */
569      	  *temp = (long) TextLength (text);
570
571    	*value = (XPointer) temp;
572    	*type = XA_INTEGER;
573    	*length = 1L;
574    	*format = 32;
575    	return True;
576    }
577
578    if (*target == XA_CHARACTER_POSITION(d))
579    {
580    	long * temp;
581
582    	temp = (long *) XtMalloc(2 * sizeof(long));
583    	temp[0] = (long) 0;
584    	temp[1] = TextLength (text);
585    	*value = (XPointer) temp;
586    	*type = XA_SPAN(d);
587    	*length = 2L;
588    	*format = 32;
589    	return True;
590    }
591
592    if (*target == XA_STRING ||
593	*target == XA_TEXT(d) ||
594	*target == XA_UTF8_STRING(d) ||
595	*target == XA_COMPOUND_TEXT(d))
596    {
597	Arg args[1];
598	Widget source;
599	XTextProperty prop;
600	int ret, style = XStdICCTextStyle; /* a safe default for TEXT */
601	char *data;
602
603	source = XawTextGetSource (text);
604	XtSetArg (args[0], XtNstring, &data);
605	XtGetValues (source, args, 1);
606
607	if (*target == XA_UTF8_STRING(d))
608	    style = XUTF8StringStyle;
609	else if (*target == XA_COMPOUND_TEXT(d))
610	    style = XCompoundTextStyle;
611	else if (*target == XA_STRING)
612	    style = XStringStyle;
613
614	ret = XmbTextListToTextProperty (d, &data, 1, style, &prop);
615	if (ret >= Success) {
616	    *length = prop.nitems;
617	    *value = prop.value;
618	    *type = prop.encoding;
619	    *format = prop.format;
620	    return True;
621	} else
622	    return False;
623    }
624
625    if (XmuConvertStandardSelection(w, req->time, selection, target, type,
626				    (XPointer *) value, length, format))
627	return True;
628
629    return False;
630}
631
632static void
633LoseSelection(Widget w, Atom *selection)
634{
635    Display *d = XtDisplay(w);
636    XtGetSelectionValue(w, *selection, XA_UTF8_STRING(d), InsertClipboard,
637			(XtPointer)(XA_UTF8_STRING(d)), CurrentTime);
638}
639
640/*ARGSUSED*/
641static Boolean
642RefuseSelection(Widget w, Atom *selection, Atom *target,
643		Atom *type, XtPointer *value, unsigned long *length,
644		int *format)
645{
646    return False;
647}
648
649/*ARGSUSED*/
650static void
651LoseManager(Widget w, Atom *selection)
652{
653    XtError("another clipboard has taken over control\n");
654}
655
656typedef struct {
657  Boolean wrap;
658} ResourceData, *ResourceDataPtr;
659
660static ResourceData userOptions;
661
662#define Offset(field) XtOffsetOf(ResourceData, field)
663
664static XtResource resources[] = {
665  {"wrap", "Wrap", XtRBoolean, sizeof(Boolean),
666     Offset(wrap), XtRImmediate, (XtPointer)False}
667};
668
669#undef Offset
670
671int
672main(int argc, char *argv[])
673{
674    Arg args[4];
675    Cardinal n;
676    XtAppContext xtcontext;
677    Widget parent;
678
679    XtSetLanguageProc(NULL, NULL, NULL);
680
681    top = XtAppInitialize( &xtcontext, "XClipboard", table, XtNumber(table),
682			  &argc, argv, fallback_resources, NULL, 0);
683
684    XtGetApplicationResources(top, (XtPointer)&userOptions, resources,
685			      XtNumber(resources), NULL, 0);
686
687    XtAppAddActions (xtcontext,
688		     xclipboard_actions, XtNumber (xclipboard_actions));
689    /* CLIPBOARD_MANAGER is a non-standard mechanism */
690    ManagerAtom = XInternAtom(XtDisplay(top), "CLIPBOARD_MANAGER", False);
691    ClipboardAtom = XA_CLIPBOARD(XtDisplay(top));
692    if (XGetSelectionOwner(XtDisplay(top), ManagerAtom))
693	XtError("another clipboard is already running\n");
694
695    parent = XtCreateManagedWidget("form", formWidgetClass, top, NULL, ZERO);
696    (void) XtCreateManagedWidget("quit", Command, parent, NULL, ZERO);
697    (void) XtCreateManagedWidget("delete", Command, parent, NULL, ZERO);
698    (void) XtCreateManagedWidget("new", Command, parent, NULL, ZERO);
699    (void) XtCreateManagedWidget("save", Command, parent, NULL, ZERO);
700    nextButton = XtCreateManagedWidget("next", Command, parent, NULL, ZERO);
701    prevButton = XtCreateManagedWidget("prev", Command, parent, NULL, ZERO);
702    indexLabel = XtCreateManagedWidget("index", Label, parent, NULL, ZERO);
703
704    n=0;
705    XtSetArg(args[n], XtNtype, XawAsciiString); n++;
706    XtSetArg(args[n], XtNeditType, XawtextEdit); n++;
707    if (userOptions.wrap) {
708	XtSetArg(args[n], XtNwrap, XawtextWrapWord); n++;
709	XtSetArg(args[n], XtNscrollHorizontal, False); n++;
710    }
711
712    text = XtCreateManagedWidget( "text", Text, parent, args, n);
713
714    currentClip = NewClip (text, (ClipPtr) 0);
715
716    set_button_state ();
717
718    fileDialogShell = XtCreatePopupShell("fileDialogShell",
719					 transientShellWidgetClass,
720					 top, NULL, ZERO);
721    fileDialog = XtCreateManagedWidget ("fileDialog", dialogWidgetClass,
722					fileDialogShell, NULL, ZERO);
723    XawDialogAddButton(fileDialog, "accept", NULL, NULL);
724    XawDialogAddButton(fileDialog, "cancel", NULL, NULL);
725
726    failDialogShell = XtCreatePopupShell("failDialogShell",
727					 transientShellWidgetClass,
728					 top, NULL, ZERO);
729    failDialog = XtCreateManagedWidget ("failDialog", dialogWidgetClass,
730					failDialogShell, NULL, ZERO);
731    XawDialogAddButton (failDialog, "continue", NULL, NULL);
732
733    XtRealizeWidget(top);
734    XtRealizeWidget(fileDialogShell);
735    XtRealizeWidget(failDialogShell);
736    XtOwnSelection(top, ManagerAtom, CurrentTime,
737		   RefuseSelection, LoseManager, NULL);
738    if (XGetSelectionOwner (XtDisplay(top), ClipboardAtom)) {
739	LoseSelection (top, &ClipboardAtom);
740    } else {
741    	XtOwnSelection(top, ClipboardAtom, CurrentTime,
742		       ConvertSelection, LoseSelection, NULL);
743    }
744    wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False);
745    wm_protocols = XInternAtom(XtDisplay(top), "WM_PROTOCOLS", False);
746    (void) XSetWMProtocols(XtDisplay(top), XtWindow(top), &wm_delete_window,1);
747    (void) XSetWMProtocols(XtDisplay(top), XtWindow(fileDialogShell),
748			   &wm_delete_window,1);
749    (void) XSetWMProtocols(XtDisplay(top), XtWindow(failDialogShell),
750			   &wm_delete_window,1);
751    XtAppMainLoop(xtcontext);
752    exit(0);
753}
754