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 XtError("Failed to allocate memory for ClipRec\n"); 140 return newClip; 141 } 142 newClip->prev = old; 143 if (old) 144 { 145 newClip->next = old->next; 146 old->next = newClip; 147 } 148 return newClip; 149} 150 151/*ARGSUSED*/ 152static void 153DeleteClip(Widget w, ClipPtr clip) 154{ 155 if (clip->prev) 156 clip->prev->next = clip->next; 157 if (clip->next) 158 clip->next->prev = clip->prev; 159 if (clip->clip) 160 free (clip->clip); 161 free ((char *) clip); 162} 163 164static ClipPtr currentClip; 165static Widget top; 166static Widget text, nextButton, prevButton, indexLabel; 167static Widget fileDialog, fileDialogShell; 168static Widget failDialog, failDialogShell; 169 170static int 171IndexCurrentClip (void) 172{ 173 int i = 0; 174 ClipPtr clip; 175 176 for (clip = currentClip; clip; clip = clip->prev) 177 i++; 178 return i; 179} 180 181static void 182set_button_state (void) 183{ 184 Boolean prevvalid, nextvalid; 185 Arg arg; 186 char labelString[12]; 187 188 prevvalid = currentClip->prev != NULL; 189 nextvalid = currentClip->next != NULL; 190 XtSetArg (arg, XtNsensitive, prevvalid); 191 XtSetValues (prevButton, &arg, ONE); 192 XtSetArg (arg, XtNsensitive, nextvalid); 193 XtSetValues (nextButton, &arg, ONE); 194 snprintf (labelString, sizeof(labelString), "%d", IndexCurrentClip ()); 195 XtSetArg (arg, XtNlabel, labelString); 196 XtSetValues (indexLabel, &arg, ONE); 197} 198 199/* ARGSUSED */ 200static void 201NextCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 202{ 203 if (currentClip->next) 204 { 205 SaveClip (text, currentClip); 206 currentClip = currentClip->next; 207 RestoreClip (text, currentClip); 208 set_button_state (); 209 } 210} 211 212/* ARGSUSED */ 213static void 214PrevCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 215{ 216 if (currentClip->prev) 217 { 218 SaveClip (text, currentClip); 219 currentClip = currentClip->prev; 220 RestoreClip (text, currentClip); 221 set_button_state (); 222 } 223} 224 225/* ARGSUSED */ 226static void 227DeleteCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 228{ 229 ClipPtr newCurrent; 230 231 if (currentClip->prev) 232 newCurrent = currentClip->prev; 233 else 234 newCurrent = currentClip->next; 235 if (newCurrent) 236 { 237 DeleteClip (text, currentClip); 238 currentClip = newCurrent; 239 RestoreClip (text, currentClip); 240 } 241 else 242 EraseTextWidget (); 243 set_button_state (); 244} 245 246/* ARGSUSED */ 247static void _X_NORETURN 248Quit(Widget w, XEvent *ev, String *parms, Cardinal *np) 249{ 250 XtCloseDisplay (XtDisplay (text)); 251 exit (0); 252} 253 254static void 255CenterWidgetAtPoint(Widget w, int x, int y) 256{ 257 Arg args[2]; 258 Dimension width, height; 259 260 XtSetArg(args[0], XtNwidth, &width); 261 XtSetArg(args[1], XtNheight, &height); 262 XtGetValues (w, args, 2); 263 x = x - (int) width / 2; 264 y = y - (int) height / 2; 265 if (x < 0) 266 x = 0; 267 else { 268 int scr_width = WidthOfScreen (XtScreen(w)); 269 if (x + (int)width > scr_width) 270 x = scr_width - width; 271 } 272 if (y < 0) 273 y = 0; 274 else { 275 int scr_height = HeightOfScreen (XtScreen(w)); 276 if (y + (int)height > scr_height) 277 y = scr_height - height; 278 } 279 XtSetArg(args[0], XtNx, x); 280 XtSetArg(args[1], XtNy, y); 281 XtSetValues (w, args, 2); 282} 283 284static void 285CenterWidgetOnEvent(Widget w, XEvent *e) 286{ 287 CenterWidgetAtPoint (w, e->xbutton.x_root, e->xbutton.y_root); 288} 289 290static void 291CenterWidgetOnWidget(Widget w, Widget wT) 292{ 293 Position rootX, rootY; 294 Dimension width, height; 295 Arg args[2]; 296 297 XtSetArg (args[0], XtNwidth, &width); 298 XtSetArg (args[1], XtNheight, &height); 299 XtGetValues (wT, args, 2); 300 XtTranslateCoords (wT, (Position) width/2, (Position) height/2, &rootX, &rootY); 301 CenterWidgetAtPoint (w, (int) rootX, (int) rootY); 302} 303 304/*ARGSUSED*/ 305static void 306SaveToFile(Widget w, XEvent *e, String *argv, Cardinal *argc) 307{ 308 Arg args[1]; 309 const char *filename; 310 311 filename = "clipboard"; 312 if (currentClip->filename) 313 filename = currentClip->filename; 314 XtSetArg(args[0], XtNvalue, filename); 315 XtSetValues (fileDialog, args, 1); 316 CenterWidgetOnEvent (fileDialogShell, e); 317 XtPopup (fileDialogShell, XtGrabNone); 318} 319 320/*ARGSUSED*/ 321static void 322AcceptSaveFile(Widget w, XEvent *e, String *argv, Cardinal *argc) 323{ 324 char *filename; 325 Bool success; 326 Arg args[1]; 327 328 filename = XawDialogGetValueString (fileDialog); 329 success = XawAsciiSaveAsFile (XawTextGetSource (text), filename); 330 XtPopdown (fileDialogShell); 331 if (!success) 332 { 333 char *failMessage; 334 335 XtAsprintf (&failMessage, "Can't open file \"%s\"", filename); 336 XtSetArg (args[0], XtNlabel, failMessage); 337 XtSetValues (failDialog, args, 1); 338 CenterWidgetOnEvent (failDialogShell, e); 339 XtPopup (failDialogShell, XtGrabNone); 340 XtFree (failMessage); 341 } 342 else 343 { 344 if (currentClip->filename) 345 free (currentClip->filename); 346 currentClip->filename = strdup (filename); 347 } 348} 349 350/* ARGSUSED */ 351static void 352CancelSaveFile(Widget w, XEvent *ev, String *parms, Cardinal *np) 353{ 354 XtPopdown (fileDialogShell); 355} 356 357/* ARGSUSED */ 358static void 359FailContinue(Widget w, XEvent *ev, String *parms, Cardinal *np) 360{ 361 XtPopdown (failDialogShell); 362} 363 364/*ARGSUSED*/ 365static void 366WMProtocols(Widget w, XEvent *ev, String *params, Cardinal *n) 367{ 368 if (ev->type == ClientMessage && 369 ev->xclient.message_type == wm_protocols && 370 ev->xclient.data.l[0] == (long) wm_delete_window) { 371 while (w && !XtIsShell(w)) 372 w = XtParent(w); 373 if (w == top) 374 Quit(w, ev, params, n); 375 else if (w == fileDialogShell) 376 CancelSaveFile(w, ev, params, n); 377 else if (w == failDialogShell) 378 FailContinue(w, ev, params, n); 379 } 380} 381 382static char empty_string[] = ""; 383 384/* ARGUSED */ 385static void 386NewCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 387{ 388 NewCurrentClipContents (empty_string, 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 ((currentClip == NULL) || (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 = empty_string; 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 Atom compound_text = XA_COMPOUND_TEXT(d); 497 XtGetSelectionValue(w, *selection, compound_text, 498 InsertClipboard, 499 &compound_text, 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 Atom utf8_string = XA_UTF8_STRING(d); 642 XtGetSelectionValue(w, *selection, utf8_string, InsertClipboard, 643 &utf8_string, 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