xclipboard.c revision c3f70f0a
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    int		    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    int	    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 = (ClipPtr) malloc (sizeof (ClipRec));
138    if (!newClip)
139	return newClip;
140    newClip->clip = NULL;
141    newClip->avail = 0;
142    newClip->prev = old;
143    newClip->next = NULL;
144    newClip->filename = NULL;
145    if (old)
146    {
147	newClip->next = old->next;
148	old->next = newClip;
149    }
150    return newClip;
151}
152
153/*ARGSUSED*/
154static void
155DeleteClip(Widget w, ClipPtr clip)
156{
157    if (clip->prev)
158	clip->prev->next = clip->next;
159    if (clip->next)
160	clip->next->prev = clip->prev;
161    if (clip->clip)
162	free (clip->clip);
163    free ((char *) clip);
164}
165
166static ClipPtr	currentClip;
167static Widget	top;
168static Widget	text, nextButton, prevButton, indexLabel;
169static Widget	fileDialog, fileDialogShell;
170static Widget	failDialog, failDialogShell;
171
172static int
173IndexCurrentClip (void)
174{
175    int	i = 0;
176    ClipPtr clip;
177
178    for (clip = currentClip; clip; clip = clip->prev)
179	i++;
180    return i;
181}
182
183static void
184set_button_state (void)
185{
186    Boolean prevvalid, nextvalid;
187    Arg arg;
188    char labelString[10];
189
190    prevvalid = currentClip->prev != NULL;
191    nextvalid = currentClip->next != NULL;
192    XtSetArg (arg, XtNsensitive, prevvalid);
193    XtSetValues (prevButton, &arg, ONE);
194    XtSetArg (arg, XtNsensitive, nextvalid);
195    XtSetValues (nextButton, &arg, ONE);
196    sprintf (labelString, "%d", IndexCurrentClip ());
197    XtSetArg (arg, XtNlabel, labelString);
198    XtSetValues (indexLabel, &arg, ONE);
199}
200
201/* ARGSUSED */
202static void
203NextCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
204{
205    if (currentClip->next)
206    {
207	SaveClip (text, currentClip);
208	currentClip = currentClip->next;
209	RestoreClip (text, currentClip);
210	set_button_state ();
211    }
212}
213
214/* ARGSUSED */
215static void
216PrevCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
217{
218    if (currentClip->prev)
219    {
220	SaveClip (text, currentClip);
221	currentClip = currentClip->prev;
222	RestoreClip (text, currentClip);
223	set_button_state ();
224    }
225}
226
227/* ARGSUSED */
228static void
229DeleteCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
230{
231    ClipPtr newCurrent;
232
233    if (currentClip->prev)
234	newCurrent = currentClip->prev;
235    else
236	newCurrent = currentClip->next;
237    if (newCurrent)
238    {
239	DeleteClip (text, currentClip);
240	currentClip = newCurrent;
241	RestoreClip (text, currentClip);
242    }
243    else
244	EraseTextWidget ();
245    set_button_state ();
246}
247
248/* ARGSUSED */
249static void
250Quit(Widget w, XEvent *ev, String *parms, Cardinal *np)
251{
252    XtCloseDisplay  (XtDisplay (text));
253    exit (0);
254}
255
256static void
257CenterWidgetAtPoint(Widget w, int x, int y)
258{
259    Arg	args[2];
260    Dimension	width, height;
261
262    XtSetArg(args[0], XtNwidth, &width);
263    XtSetArg(args[1], XtNheight, &height);
264    XtGetValues (w, args, 2);
265    x = x - (int) width / 2;
266    y = y - (int) height / 2;
267    if (x < 0)
268	x = 0;
269    else {
270	int scr_width = WidthOfScreen (XtScreen(w));
271	if (x + (int)width > scr_width)
272	    x = scr_width - width;
273    }
274    if (y < 0)
275	y = 0;
276    else {
277	int scr_height = HeightOfScreen (XtScreen(w));
278	if (y + (int)height > scr_height)
279	    y = scr_height - height;
280    }
281    XtSetArg(args[0], XtNx, x);
282    XtSetArg(args[1], XtNy, y);
283    XtSetValues (w, args, 2);
284}
285
286static void
287CenterWidgetOnEvent(Widget w, XEvent *e)
288{
289    CenterWidgetAtPoint (w, e->xbutton.x_root, e->xbutton.y_root);
290}
291
292static void
293CenterWidgetOnWidget(Widget w, Widget wT)
294{
295    Position	rootX, rootY;
296    Dimension	width, height;
297    Arg		args[2];
298
299    XtSetArg (args[0], XtNwidth, &width);
300    XtSetArg (args[1], XtNheight, &height);
301    XtGetValues (wT, args, 2);
302    XtTranslateCoords (wT, (Position) width/2, (Position) height/2, &rootX, &rootY);
303    CenterWidgetAtPoint (w, (int) rootX, (int) rootY);
304}
305
306/*ARGSUSED*/
307static void
308SaveToFile(Widget w, XEvent *e, String *argv, Cardinal *argc)
309{
310    Arg	    args[1];
311    char    *filename;
312
313    filename = "clipboard";
314    if (currentClip->filename)
315	filename = currentClip->filename;
316    XtSetArg(args[0], XtNvalue, filename);
317    XtSetValues (fileDialog, args, 1);
318    CenterWidgetOnEvent (fileDialogShell, e);
319    XtPopup (fileDialogShell, XtGrabNone);
320}
321
322/*ARGSUSED*/
323static void
324AcceptSaveFile(Widget w, XEvent *e, String *argv, Cardinal *argc)
325{
326    char    *filename;
327    Boolean success;
328    Arg	    args[1];
329
330    filename = XawDialogGetValueString (fileDialog);
331    success = XawAsciiSaveAsFile (XawTextGetSource (text), filename);
332    XtPopdown (fileDialogShell);
333    if (!success)
334    {
335	char	failMessage[1024];
336
337	sprintf (failMessage, "Can't open file \"%s\"", filename);
338	XtSetArg (args[0], XtNlabel, failMessage);
339	XtSetValues (failDialog, args, 1);
340	CenterWidgetOnEvent (failDialogShell, e);
341	XtPopup (failDialogShell, XtGrabNone);
342    }
343    else
344    {
345	if (currentClip->filename)
346	    free (currentClip->filename);
347	currentClip->filename = malloc (strlen (filename) + 1);
348	if (currentClip->filename)
349	    strcpy (currentClip->filename, filename);
350    }
351}
352
353/* ARGSUSED */
354static void
355CancelSaveFile(Widget w, XEvent *ev, String *parms, Cardinal *np)
356{
357    XtPopdown (fileDialogShell);
358}
359
360/* ARGSUSED */
361static void
362FailContinue(Widget w, XEvent *ev, String *parms, Cardinal *np)
363{
364    XtPopdown (failDialogShell);
365}
366
367/*ARGSUSED*/
368static void
369WMProtocols(Widget w, XEvent *ev, String *params, Cardinal *n)
370{
371    if (ev->type == ClientMessage &&
372	ev->xclient.message_type == wm_protocols &&
373	ev->xclient.data.l[0] == (long) wm_delete_window) {
374	while (w && !XtIsShell(w))
375	    w = XtParent(w);
376	if (w == top)
377	    Quit(w, ev, params, n);
378	else if (w == fileDialogShell)
379	    CancelSaveFile(w, ev, params, n);
380	else if (w == failDialogShell)
381	    FailContinue(w, ev, params, n);
382    }
383}
384
385/* ARGUSED */
386static void
387NewCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
388{
389    NewCurrentClipContents ("", 0);
390}
391
392static void
393NewCurrentClipContents(char *data, int len)
394{
395    XawTextBlock textBlock;
396
397    SaveClip (text, currentClip);
398
399    /* append new clips at the end */
400    while (currentClip && currentClip->next)
401	currentClip = currentClip->next;
402    /* any trailing clips with no text get overwritten */
403    if (strlen (currentClip->clip) != 0)
404	currentClip = NewClip (text, currentClip);
405
406    textBlock.ptr = data;
407    textBlock.firstPos = 0;
408    textBlock.length = len;
409    textBlock.format = FMT8BIT;
410    if (XawTextReplace(text, 0, TextLength (text), &textBlock)) {
411#ifdef XKB
412	XkbStdBell(XtDisplay(text), XtWindow(text), 0, XkbBI_Info);
413#else
414	XBell( XtDisplay(text), 0);
415#endif
416    }
417    set_button_state ();
418}
419
420static void
421EraseTextWidget(void)
422{
423    XawTextBlock block;
424
425    block.ptr = "";
426    block.length = 0;
427    block.firstPos = 0;
428    block.format = FMT8BIT;
429
430    XawTextReplace(text, 0, INFINITY, &block);
431    /* If this fails, too bad. */
432}
433
434
435static XtActionsRec xclipboard_actions[] = {
436    { "NewClip", 	NewCurrentClip },
437    { "NextClip",	NextCurrentClip },
438    { "PrevClip",	PrevCurrentClip },
439    { "DeleteClip",	DeleteCurrentClip },
440    { "Save",		SaveToFile },
441    { "AcceptSave",	AcceptSaveFile },
442    { "CancelSave",	CancelSaveFile },
443    { "FailContinue",	FailContinue },
444    { "Quit",		Quit },
445    { "WMProtocols",	WMProtocols }
446};
447
448static XrmOptionDescRec table[] = {
449    {"-w",	    "wrap",		XrmoptionNoArg,  "on"},
450/*    {"-nw",	    "wrap",		XrmoptionNoArg,  "False"} */
451};
452
453static Boolean ConvertSelection ( Widget w, Atom *selection, Atom *target,
454				  Atom *type, XtPointer *value,
455				  unsigned long *length, int *format );
456static void LoseSelection ( Widget w, Atom *selection );
457
458static Atom	ManagerAtom, ClipboardAtom;
459
460/*ARGSUSED*/
461static void
462InsertClipboard(Widget w, XtPointer client_data, Atom *selection,
463		Atom *type, XtPointer value, unsigned long *length,
464		int *format)
465{
466    Display *d = XtDisplay(w);
467    Atom target = (Atom)client_data;
468    Boolean convert_failed = (*type == XT_CONVERT_FAIL);
469
470    if (!convert_failed)
471    {
472	char **list;
473	int i, ret, count;
474
475	XTextProperty prop;
476	prop.value = value;
477	prop.nitems = *length;
478	prop.format = *format;
479	prop.encoding = *type;
480	ret = XmbTextPropertyToTextList(d, &prop, &list, &count);
481	if (ret >= Success)
482	{
483	    /* manuals say something about multiple strings in a disjoint
484	    text selection (?), it should be harmless to get them all */
485	    for (i = 0; i < count; i++)
486		NewCurrentClipContents(list[i], strlen(list[i]));
487	    XFreeStringList(list);
488	} else
489	    convert_failed = True;
490	XFree(value);
491    }
492
493    if (convert_failed) {
494	/* if UTF8_STRING failed try COMPOUND_TEXT */
495	if (target == XA_UTF8_STRING(d))
496	{
497	    XtGetSelectionValue(w, *selection, XA_COMPOUND_TEXT(d),
498				InsertClipboard,
499				(XtPointer)(XA_COMPOUND_TEXT(d)),
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    XtGetSelectionValue(w, *selection, XA_UTF8_STRING(d), InsertClipboard,
642			(XtPointer)(XA_UTF8_STRING(d)), CurrentTime);
643}
644
645/*ARGSUSED*/
646static Boolean
647RefuseSelection(Widget w, Atom *selection, Atom *target,
648		Atom *type, XtPointer *value, unsigned long *length,
649		int *format)
650{
651    return False;
652}
653
654/*ARGSUSED*/
655static void
656LoseManager(Widget w, Atom *selection)
657{
658    XtError("another clipboard has taken over control\n");
659}
660
661typedef struct {
662  Boolean wrap;
663} ResourceData, *ResourceDataPtr;
664
665static ResourceData userOptions;
666
667#define Offset(field) XtOffsetOf(ResourceData, field)
668
669static XtResource resources[] = {
670  {"wrap", "Wrap", XtRBoolean, sizeof(Boolean),
671     Offset(wrap), XtRImmediate, (XtPointer)False}
672};
673
674#undef Offset
675
676int
677main(int argc, char *argv[])
678{
679    Arg args[4];
680    Cardinal n;
681    XtAppContext xtcontext;
682    Widget parent;
683
684    XtSetLanguageProc(NULL, NULL, NULL);
685
686    top = XtAppInitialize( &xtcontext, "XClipboard", table, XtNumber(table),
687			  &argc, argv, fallback_resources, NULL, 0);
688
689    XtGetApplicationResources(top, (XtPointer)&userOptions, resources,
690			      XtNumber(resources), NULL, 0);
691
692    XtAppAddActions (xtcontext,
693		     xclipboard_actions, XtNumber (xclipboard_actions));
694    /* CLIPBOARD_MANAGER is a non-standard mechanism */
695    ManagerAtom = XInternAtom(XtDisplay(top), "CLIPBOARD_MANAGER", False);
696    ClipboardAtom = XA_CLIPBOARD(XtDisplay(top));
697    if (XGetSelectionOwner(XtDisplay(top), ManagerAtom))
698	XtError("another clipboard is already running\n");
699
700    parent = XtCreateManagedWidget("form", formWidgetClass, top, NULL, ZERO);
701    (void) XtCreateManagedWidget("quit", Command, parent, NULL, ZERO);
702    (void) XtCreateManagedWidget("delete", Command, parent, NULL, ZERO);
703    (void) XtCreateManagedWidget("new", Command, parent, NULL, ZERO);
704    (void) XtCreateManagedWidget("save", Command, parent, NULL, ZERO);
705    nextButton = XtCreateManagedWidget("next", Command, parent, NULL, ZERO);
706    prevButton = XtCreateManagedWidget("prev", Command, parent, NULL, ZERO);
707    indexLabel = XtCreateManagedWidget("index", Label, parent, NULL, ZERO);
708
709    n=0;
710    XtSetArg(args[n], XtNtype, XawAsciiString); n++;
711    XtSetArg(args[n], XtNeditType, XawtextEdit); n++;
712    if (userOptions.wrap) {
713	XtSetArg(args[n], XtNwrap, XawtextWrapWord); n++;
714	XtSetArg(args[n], XtNscrollHorizontal, False); n++;
715    }
716
717    text = XtCreateManagedWidget( "text", Text, parent, args, n);
718
719    currentClip = NewClip (text, (ClipPtr) 0);
720
721    set_button_state ();
722
723    fileDialogShell = XtCreatePopupShell("fileDialogShell",
724					 transientShellWidgetClass,
725					 top, NULL, ZERO);
726    fileDialog = XtCreateManagedWidget ("fileDialog", dialogWidgetClass,
727					fileDialogShell, NULL, ZERO);
728    XawDialogAddButton(fileDialog, "accept", NULL, NULL);
729    XawDialogAddButton(fileDialog, "cancel", NULL, NULL);
730
731    failDialogShell = XtCreatePopupShell("failDialogShell",
732					 transientShellWidgetClass,
733					 top, NULL, ZERO);
734    failDialog = XtCreateManagedWidget ("failDialog", dialogWidgetClass,
735					failDialogShell, NULL, ZERO);
736    XawDialogAddButton (failDialog, "continue", NULL, NULL);
737
738    XtRealizeWidget(top);
739    XtRealizeWidget(fileDialogShell);
740    XtRealizeWidget(failDialogShell);
741    XtOwnSelection(top, ManagerAtom, CurrentTime,
742		   RefuseSelection, LoseManager, NULL);
743    if (XGetSelectionOwner (XtDisplay(top), ClipboardAtom)) {
744	LoseSelection (top, &ClipboardAtom);
745    } else {
746    	XtOwnSelection(top, ClipboardAtom, CurrentTime,
747		       ConvertSelection, LoseSelection, NULL);
748    }
749    wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False);
750    wm_protocols = XInternAtom(XtDisplay(top), "WM_PROTOCOLS", False);
751    (void) XSetWMProtocols(XtDisplay(top), XtWindow(top), &wm_delete_window,1);
752    (void) XSetWMProtocols(XtDisplay(top), XtWindow(fileDialogShell),
753			   &wm_delete_window,1);
754    (void) XSetWMProtocols(XtDisplay(top), XtWindow(failDialogShell),
755			   &wm_delete_window,1);
756    XtAppMainLoop(xtcontext);
757    exit(0);
758}
759