xclipboard.c revision bbf6ba0a
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;
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 = malloc (strlen (filename) + 1);
349	if (currentClip->filename)
350	    strcpy (currentClip->filename, filename);
351    }
352}
353
354/* ARGSUSED */
355static void
356CancelSaveFile(Widget w, XEvent *ev, String *parms, Cardinal *np)
357{
358    XtPopdown (fileDialogShell);
359}
360
361/* ARGSUSED */
362static void
363FailContinue(Widget w, XEvent *ev, String *parms, Cardinal *np)
364{
365    XtPopdown (failDialogShell);
366}
367
368/*ARGSUSED*/
369static void
370WMProtocols(Widget w, XEvent *ev, String *params, Cardinal *n)
371{
372    if (ev->type == ClientMessage &&
373	ev->xclient.message_type == wm_protocols &&
374	ev->xclient.data.l[0] == (long) wm_delete_window) {
375	while (w && !XtIsShell(w))
376	    w = XtParent(w);
377	if (w == top)
378	    Quit(w, ev, params, n);
379	else if (w == fileDialogShell)
380	    CancelSaveFile(w, ev, params, n);
381	else if (w == failDialogShell)
382	    FailContinue(w, ev, params, n);
383    }
384}
385
386/* ARGUSED */
387static void
388NewCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np)
389{
390    NewCurrentClipContents ("", 0);
391}
392
393static void
394NewCurrentClipContents(char *data, int len)
395{
396    XawTextBlock textBlock;
397
398    SaveClip (text, currentClip);
399
400    /* append new clips at the end */
401    while (currentClip && currentClip->next)
402	currentClip = currentClip->next;
403    /* any trailing clips with no text get overwritten */
404    if (strlen (currentClip->clip) != 0)
405	currentClip = NewClip (text, currentClip);
406
407    textBlock.ptr = data;
408    textBlock.firstPos = 0;
409    textBlock.length = len;
410    textBlock.format = FMT8BIT;
411    if (XawTextReplace(text, 0, TextLength (text), &textBlock)) {
412#ifdef XKB
413	XkbStdBell(XtDisplay(text), XtWindow(text), 0, XkbBI_Info);
414#else
415	XBell( XtDisplay(text), 0);
416#endif
417    }
418    set_button_state ();
419}
420
421static void
422EraseTextWidget(void)
423{
424    XawTextBlock block;
425
426    block.ptr = "";
427    block.length = 0;
428    block.firstPos = 0;
429    block.format = FMT8BIT;
430
431    XawTextReplace(text, 0, INFINITY, &block);
432    /* If this fails, too bad. */
433}
434
435
436static XtActionsRec xclipboard_actions[] = {
437    { "NewClip", 	NewCurrentClip },
438    { "NextClip",	NextCurrentClip },
439    { "PrevClip",	PrevCurrentClip },
440    { "DeleteClip",	DeleteCurrentClip },
441    { "Save",		SaveToFile },
442    { "AcceptSave",	AcceptSaveFile },
443    { "CancelSave",	CancelSaveFile },
444    { "FailContinue",	FailContinue },
445    { "Quit",		Quit },
446    { "WMProtocols",	WMProtocols }
447};
448
449static XrmOptionDescRec table[] = {
450    {"-w",	    "wrap",		XrmoptionNoArg,  "on"},
451/*    {"-nw",	    "wrap",		XrmoptionNoArg,  "False"} */
452};
453
454static Boolean ConvertSelection ( Widget w, Atom *selection, Atom *target,
455				  Atom *type, XtPointer *value,
456				  unsigned long *length, int *format );
457static void LoseSelection ( Widget w, Atom *selection );
458
459static Atom	ManagerAtom, ClipboardAtom;
460
461/*ARGSUSED*/
462static void
463InsertClipboard(Widget w, XtPointer client_data, Atom *selection,
464		Atom *type, XtPointer value, unsigned long *length,
465		int *format)
466{
467    Display *d = XtDisplay(w);
468    Atom target = (Atom)client_data;
469    Boolean convert_failed = (*type == XT_CONVERT_FAIL);
470
471    if (!convert_failed)
472    {
473	char **list;
474	int i, ret, count;
475
476	XTextProperty prop;
477	prop.value = value;
478	prop.nitems = *length;
479	prop.format = *format;
480	prop.encoding = *type;
481	ret = XmbTextPropertyToTextList(d, &prop, &list, &count);
482	if (ret >= Success)
483	{
484	    /* manuals say something about multiple strings in a disjoint
485	    text selection (?), it should be harmless to get them all */
486	    for (i = 0; i < count; i++)
487		NewCurrentClipContents(list[i], strlen(list[i]));
488	    XFreeStringList(list);
489	} else
490	    convert_failed = True;
491	XFree(value);
492    }
493
494    if (convert_failed) {
495	/* if UTF8_STRING failed try COMPOUND_TEXT */
496	if (target == XA_UTF8_STRING(d))
497	{
498	    XtGetSelectionValue(w, *selection, XA_COMPOUND_TEXT(d),
499				InsertClipboard,
500				(XtPointer)(XA_COMPOUND_TEXT(d)),
501				CurrentTime);
502	    return;
503	}
504	/* if COMPOUND_TEXT failed try STRING */
505	else if (target == XA_COMPOUND_TEXT(d))
506	{
507	    XtGetSelectionValue(w, *selection, XA_STRING,
508				InsertClipboard,
509				NULL,
510				CurrentTime);
511	    return;
512	}
513	/* all conversions failed */
514	else
515	{
516	    Arg arg;
517	    XtSetArg (arg, XtNlabel, "CLIPBOARD selection conversion failed");
518	    XtSetValues (failDialog, &arg, 1);
519	    CenterWidgetOnWidget (failDialogShell, text);
520	    XtPopup (failDialogShell, XtGrabNone);
521#ifdef XKB
522	    XkbStdBell (d, XtWindow(w), 0, XkbBI_MinorError);
523#else
524	    XBell (d, 0);
525#endif
526	}
527    }
528
529    XtOwnSelection(top, ClipboardAtom, CurrentTime,
530		   ConvertSelection, LoseSelection, NULL);
531}
532
533static Boolean
534ConvertSelection(Widget w, Atom *selection, Atom *target,
535		 Atom *type, XtPointer *value, unsigned long *length,
536		 int *format)
537{
538    Display* d = XtDisplay(w);
539    XSelectionRequestEvent* req =
540	XtGetSelectionRequest(w, *selection, (XtRequestId)NULL);
541
542    if (*target == XA_TARGETS(d)) {
543	Atom* targetP;
544	Atom* std_targets;
545	unsigned long std_length;
546	XmuConvertStandardSelection(w, req->time, selection, target, type,
547				    (XPointer*)&std_targets, &std_length,
548				    format);
549	*value = XtMalloc(sizeof(Atom)*(std_length + 7));
550	targetP = *(Atom**)value;
551	*targetP++ = XA_STRING;
552	*targetP++ = XA_TEXT(d);
553	*targetP++ = XA_UTF8_STRING(d);
554	*targetP++ = XA_COMPOUND_TEXT(d);
555	*targetP++ = XA_LENGTH(d);
556	*targetP++ = XA_LIST_LENGTH(d);
557	*targetP++ = XA_CHARACTER_POSITION(d);
558	*length = std_length + (targetP - (*(Atom **) value));
559	memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length);
560	XtFree((char*)std_targets);
561	*type = XA_ATOM;
562	*format = 32;
563	return True;
564    }
565
566    if (*target == XA_LIST_LENGTH(d) ||
567	*target == XA_LENGTH(d))
568    {
569    	long * temp;
570
571    	temp = (long *) XtMalloc(sizeof(long));
572    	if (*target == XA_LIST_LENGTH(d))
573      	  *temp = 1L;
574    	else			/* *target == XA_LENGTH(d) */
575      	  *temp = (long) TextLength (text);
576
577    	*value = (XPointer) temp;
578    	*type = XA_INTEGER;
579    	*length = 1L;
580    	*format = 32;
581    	return True;
582    }
583
584    if (*target == XA_CHARACTER_POSITION(d))
585    {
586    	long * temp;
587
588    	temp = (long *) XtMalloc(2 * sizeof(long));
589    	temp[0] = (long) 0;
590    	temp[1] = TextLength (text);
591    	*value = (XPointer) temp;
592    	*type = XA_SPAN(d);
593    	*length = 2L;
594    	*format = 32;
595    	return True;
596    }
597
598    if (*target == XA_STRING ||
599	*target == XA_TEXT(d) ||
600	*target == XA_UTF8_STRING(d) ||
601	*target == XA_COMPOUND_TEXT(d))
602    {
603	Arg args[1];
604	Widget source;
605	XTextProperty prop;
606	int ret, style = XStdICCTextStyle; /* a safe default for TEXT */
607	char *data;
608
609	source = XawTextGetSource (text);
610	XtSetArg (args[0], XtNstring, &data);
611	XtGetValues (source, args, 1);
612
613	if (*target == XA_UTF8_STRING(d))
614	    style = XUTF8StringStyle;
615	else if (*target == XA_COMPOUND_TEXT(d))
616	    style = XCompoundTextStyle;
617	else if (*target == XA_STRING)
618	    style = XStringStyle;
619
620	ret = XmbTextListToTextProperty (d, &data, 1, style, &prop);
621	if (ret >= Success) {
622	    *length = prop.nitems;
623	    *value = prop.value;
624	    *type = prop.encoding;
625	    *format = prop.format;
626	    return True;
627	} else
628	    return False;
629    }
630
631    if (XmuConvertStandardSelection(w, req->time, selection, target, type,
632				    (XPointer *) value, length, format))
633	return True;
634
635    return False;
636}
637
638static void
639LoseSelection(Widget w, Atom *selection)
640{
641    Display *d = XtDisplay(w);
642    XtGetSelectionValue(w, *selection, XA_UTF8_STRING(d), InsertClipboard,
643			(XtPointer)(XA_UTF8_STRING(d)), 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