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