xclipboard.c revision 68af480f
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 = (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    snprintf (labelString, sizeof(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 _X_NORETURN
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    Bool    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;
336
337	XtAsprintf (&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	XtFree (failMessage);
343    }
344    else
345    {
346	if (currentClip->filename)
347	    free (currentClip->filename);
348	currentClip->filename = strdup (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