xclipboard.c revision 219ffec0
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 return newClip; 140 newClip->prev = old; 141 if (old) 142 { 143 newClip->next = old->next; 144 old->next = newClip; 145 } 146 return newClip; 147} 148 149/*ARGSUSED*/ 150static void 151DeleteClip(Widget w, ClipPtr clip) 152{ 153 if (clip->prev) 154 clip->prev->next = clip->next; 155 if (clip->next) 156 clip->next->prev = clip->prev; 157 if (clip->clip) 158 free (clip->clip); 159 free ((char *) clip); 160} 161 162static ClipPtr currentClip; 163static Widget top; 164static Widget text, nextButton, prevButton, indexLabel; 165static Widget fileDialog, fileDialogShell; 166static Widget failDialog, failDialogShell; 167 168static int 169IndexCurrentClip (void) 170{ 171 int i = 0; 172 ClipPtr clip; 173 174 for (clip = currentClip; clip; clip = clip->prev) 175 i++; 176 return i; 177} 178 179static void 180set_button_state (void) 181{ 182 Boolean prevvalid, nextvalid; 183 Arg arg; 184 char labelString[12]; 185 186 prevvalid = currentClip->prev != NULL; 187 nextvalid = currentClip->next != NULL; 188 XtSetArg (arg, XtNsensitive, prevvalid); 189 XtSetValues (prevButton, &arg, ONE); 190 XtSetArg (arg, XtNsensitive, nextvalid); 191 XtSetValues (nextButton, &arg, ONE); 192 snprintf (labelString, sizeof(labelString), "%d", IndexCurrentClip ()); 193 XtSetArg (arg, XtNlabel, labelString); 194 XtSetValues (indexLabel, &arg, ONE); 195} 196 197/* ARGSUSED */ 198static void 199NextCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 200{ 201 if (currentClip->next) 202 { 203 SaveClip (text, currentClip); 204 currentClip = currentClip->next; 205 RestoreClip (text, currentClip); 206 set_button_state (); 207 } 208} 209 210/* ARGSUSED */ 211static void 212PrevCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 213{ 214 if (currentClip->prev) 215 { 216 SaveClip (text, currentClip); 217 currentClip = currentClip->prev; 218 RestoreClip (text, currentClip); 219 set_button_state (); 220 } 221} 222 223/* ARGSUSED */ 224static void 225DeleteCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 226{ 227 ClipPtr newCurrent; 228 229 if (currentClip->prev) 230 newCurrent = currentClip->prev; 231 else 232 newCurrent = currentClip->next; 233 if (newCurrent) 234 { 235 DeleteClip (text, currentClip); 236 currentClip = newCurrent; 237 RestoreClip (text, currentClip); 238 } 239 else 240 EraseTextWidget (); 241 set_button_state (); 242} 243 244/* ARGSUSED */ 245static void _X_NORETURN 246Quit(Widget w, XEvent *ev, String *parms, Cardinal *np) 247{ 248 XtCloseDisplay (XtDisplay (text)); 249 exit (0); 250} 251 252static void 253CenterWidgetAtPoint(Widget w, int x, int y) 254{ 255 Arg args[2]; 256 Dimension width, height; 257 258 XtSetArg(args[0], XtNwidth, &width); 259 XtSetArg(args[1], XtNheight, &height); 260 XtGetValues (w, args, 2); 261 x = x - (int) width / 2; 262 y = y - (int) height / 2; 263 if (x < 0) 264 x = 0; 265 else { 266 int scr_width = WidthOfScreen (XtScreen(w)); 267 if (x + (int)width > scr_width) 268 x = scr_width - width; 269 } 270 if (y < 0) 271 y = 0; 272 else { 273 int scr_height = HeightOfScreen (XtScreen(w)); 274 if (y + (int)height > scr_height) 275 y = scr_height - height; 276 } 277 XtSetArg(args[0], XtNx, x); 278 XtSetArg(args[1], XtNy, y); 279 XtSetValues (w, args, 2); 280} 281 282static void 283CenterWidgetOnEvent(Widget w, XEvent *e) 284{ 285 CenterWidgetAtPoint (w, e->xbutton.x_root, e->xbutton.y_root); 286} 287 288static void 289CenterWidgetOnWidget(Widget w, Widget wT) 290{ 291 Position rootX, rootY; 292 Dimension width, height; 293 Arg args[2]; 294 295 XtSetArg (args[0], XtNwidth, &width); 296 XtSetArg (args[1], XtNheight, &height); 297 XtGetValues (wT, args, 2); 298 XtTranslateCoords (wT, (Position) width/2, (Position) height/2, &rootX, &rootY); 299 CenterWidgetAtPoint (w, (int) rootX, (int) rootY); 300} 301 302/*ARGSUSED*/ 303static void 304SaveToFile(Widget w, XEvent *e, String *argv, Cardinal *argc) 305{ 306 Arg args[1]; 307 const char *filename; 308 309 filename = "clipboard"; 310 if (currentClip->filename) 311 filename = currentClip->filename; 312 XtSetArg(args[0], XtNvalue, filename); 313 XtSetValues (fileDialog, args, 1); 314 CenterWidgetOnEvent (fileDialogShell, e); 315 XtPopup (fileDialogShell, XtGrabNone); 316} 317 318/*ARGSUSED*/ 319static void 320AcceptSaveFile(Widget w, XEvent *e, String *argv, Cardinal *argc) 321{ 322 char *filename; 323 Bool success; 324 Arg args[1]; 325 326 filename = XawDialogGetValueString (fileDialog); 327 success = XawAsciiSaveAsFile (XawTextGetSource (text), filename); 328 XtPopdown (fileDialogShell); 329 if (!success) 330 { 331 char *failMessage; 332 333 XtAsprintf (&failMessage, "Can't open file \"%s\"", filename); 334 XtSetArg (args[0], XtNlabel, failMessage); 335 XtSetValues (failDialog, args, 1); 336 CenterWidgetOnEvent (failDialogShell, e); 337 XtPopup (failDialogShell, XtGrabNone); 338 XtFree (failMessage); 339 } 340 else 341 { 342 if (currentClip->filename) 343 free (currentClip->filename); 344 currentClip->filename = strdup (filename); 345 } 346} 347 348/* ARGSUSED */ 349static void 350CancelSaveFile(Widget w, XEvent *ev, String *parms, Cardinal *np) 351{ 352 XtPopdown (fileDialogShell); 353} 354 355/* ARGSUSED */ 356static void 357FailContinue(Widget w, XEvent *ev, String *parms, Cardinal *np) 358{ 359 XtPopdown (failDialogShell); 360} 361 362/*ARGSUSED*/ 363static void 364WMProtocols(Widget w, XEvent *ev, String *params, Cardinal *n) 365{ 366 if (ev->type == ClientMessage && 367 ev->xclient.message_type == wm_protocols && 368 ev->xclient.data.l[0] == (long) wm_delete_window) { 369 while (w && !XtIsShell(w)) 370 w = XtParent(w); 371 if (w == top) 372 Quit(w, ev, params, n); 373 else if (w == fileDialogShell) 374 CancelSaveFile(w, ev, params, n); 375 else if (w == failDialogShell) 376 FailContinue(w, ev, params, n); 377 } 378} 379 380/* ARGUSED */ 381static void 382NewCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 383{ 384 NewCurrentClipContents ("", 0); 385} 386 387static void 388NewCurrentClipContents(char *data, int len) 389{ 390 XawTextBlock textBlock; 391 392 SaveClip (text, currentClip); 393 394 /* append new clips at the end */ 395 while (currentClip && currentClip->next) 396 currentClip = currentClip->next; 397 /* any trailing clips with no text get overwritten */ 398 if ((currentClip == NULL) || (strlen (currentClip->clip) != 0)) 399 currentClip = NewClip (text, currentClip); 400 401 textBlock.ptr = data; 402 textBlock.firstPos = 0; 403 textBlock.length = len; 404 textBlock.format = FMT8BIT; 405 if (XawTextReplace(text, 0, TextLength (text), &textBlock)) { 406#ifdef XKB 407 XkbStdBell(XtDisplay(text), XtWindow(text), 0, XkbBI_Info); 408#else 409 XBell( XtDisplay(text), 0); 410#endif 411 } 412 set_button_state (); 413} 414 415static void 416EraseTextWidget(void) 417{ 418 XawTextBlock block; 419 420 block.ptr = ""; 421 block.length = 0; 422 block.firstPos = 0; 423 block.format = FMT8BIT; 424 425 XawTextReplace(text, 0, INFINITY, &block); 426 /* If this fails, too bad. */ 427} 428 429 430static XtActionsRec xclipboard_actions[] = { 431 { "NewClip", NewCurrentClip }, 432 { "NextClip", NextCurrentClip }, 433 { "PrevClip", PrevCurrentClip }, 434 { "DeleteClip", DeleteCurrentClip }, 435 { "Save", SaveToFile }, 436 { "AcceptSave", AcceptSaveFile }, 437 { "CancelSave", CancelSaveFile }, 438 { "FailContinue", FailContinue }, 439 { "Quit", Quit }, 440 { "WMProtocols", WMProtocols } 441}; 442 443static XrmOptionDescRec table[] = { 444 {"-w", "wrap", XrmoptionNoArg, "on"}, 445/* {"-nw", "wrap", XrmoptionNoArg, "False"} */ 446}; 447 448static Boolean ConvertSelection ( Widget w, Atom *selection, Atom *target, 449 Atom *type, XtPointer *value, 450 unsigned long *length, int *format ); 451static void LoseSelection ( Widget w, Atom *selection ); 452 453static Atom ManagerAtom, ClipboardAtom; 454 455/*ARGSUSED*/ 456static void 457InsertClipboard(Widget w, XtPointer client_data, Atom *selection, 458 Atom *type, XtPointer value, unsigned long *length, 459 int *format) 460{ 461 Display *d = XtDisplay(w); 462 Atom target = (Atom)client_data; 463 Boolean convert_failed = (*type == XT_CONVERT_FAIL); 464 465 if (!convert_failed) 466 { 467 char **list; 468 int i, ret, count; 469 470 XTextProperty prop; 471 prop.value = value; 472 prop.nitems = *length; 473 prop.format = *format; 474 prop.encoding = *type; 475 ret = XmbTextPropertyToTextList(d, &prop, &list, &count); 476 if (ret >= Success) 477 { 478 /* manuals say something about multiple strings in a disjoint 479 text selection (?), it should be harmless to get them all */ 480 for (i = 0; i < count; i++) 481 NewCurrentClipContents(list[i], strlen(list[i])); 482 XFreeStringList(list); 483 } else 484 convert_failed = True; 485 XFree(value); 486 } 487 488 if (convert_failed) { 489 /* if UTF8_STRING failed try COMPOUND_TEXT */ 490 if (target == XA_UTF8_STRING(d)) 491 { 492 XtGetSelectionValue(w, *selection, XA_COMPOUND_TEXT(d), 493 InsertClipboard, 494 (XtPointer)(XA_COMPOUND_TEXT(d)), 495 CurrentTime); 496 return; 497 } 498 /* if COMPOUND_TEXT failed try STRING */ 499 else if (target == XA_COMPOUND_TEXT(d)) 500 { 501 XtGetSelectionValue(w, *selection, XA_STRING, 502 InsertClipboard, 503 NULL, 504 CurrentTime); 505 return; 506 } 507 /* all conversions failed */ 508 else 509 { 510 Arg arg; 511 XtSetArg (arg, XtNlabel, "CLIPBOARD selection conversion failed"); 512 XtSetValues (failDialog, &arg, 1); 513 CenterWidgetOnWidget (failDialogShell, text); 514 XtPopup (failDialogShell, XtGrabNone); 515#ifdef XKB 516 XkbStdBell (d, XtWindow(w), 0, XkbBI_MinorError); 517#else 518 XBell (d, 0); 519#endif 520 } 521 } 522 523 XtOwnSelection(top, ClipboardAtom, CurrentTime, 524 ConvertSelection, LoseSelection, NULL); 525} 526 527static Boolean 528ConvertSelection(Widget w, Atom *selection, Atom *target, 529 Atom *type, XtPointer *value, unsigned long *length, 530 int *format) 531{ 532 Display* d = XtDisplay(w); 533 XSelectionRequestEvent* req = 534 XtGetSelectionRequest(w, *selection, (XtRequestId)NULL); 535 536 if (*target == XA_TARGETS(d)) { 537 Atom* targetP; 538 Atom* std_targets; 539 unsigned long std_length; 540 XmuConvertStandardSelection(w, req->time, selection, target, type, 541 (XPointer*)&std_targets, &std_length, 542 format); 543 *value = XtMalloc(sizeof(Atom)*(std_length + 7)); 544 targetP = *(Atom**)value; 545 *targetP++ = XA_STRING; 546 *targetP++ = XA_TEXT(d); 547 *targetP++ = XA_UTF8_STRING(d); 548 *targetP++ = XA_COMPOUND_TEXT(d); 549 *targetP++ = XA_LENGTH(d); 550 *targetP++ = XA_LIST_LENGTH(d); 551 *targetP++ = XA_CHARACTER_POSITION(d); 552 *length = std_length + (targetP - (*(Atom **) value)); 553 memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length); 554 XtFree((char*)std_targets); 555 *type = XA_ATOM; 556 *format = 32; 557 return True; 558 } 559 560 if (*target == XA_LIST_LENGTH(d) || 561 *target == XA_LENGTH(d)) 562 { 563 long * temp; 564 565 temp = (long *) XtMalloc(sizeof(long)); 566 if (*target == XA_LIST_LENGTH(d)) 567 *temp = 1L; 568 else /* *target == XA_LENGTH(d) */ 569 *temp = (long) TextLength (text); 570 571 *value = (XPointer) temp; 572 *type = XA_INTEGER; 573 *length = 1L; 574 *format = 32; 575 return True; 576 } 577 578 if (*target == XA_CHARACTER_POSITION(d)) 579 { 580 long * temp; 581 582 temp = (long *) XtMalloc(2 * sizeof(long)); 583 temp[0] = (long) 0; 584 temp[1] = TextLength (text); 585 *value = (XPointer) temp; 586 *type = XA_SPAN(d); 587 *length = 2L; 588 *format = 32; 589 return True; 590 } 591 592 if (*target == XA_STRING || 593 *target == XA_TEXT(d) || 594 *target == XA_UTF8_STRING(d) || 595 *target == XA_COMPOUND_TEXT(d)) 596 { 597 Arg args[1]; 598 Widget source; 599 XTextProperty prop; 600 int ret, style = XStdICCTextStyle; /* a safe default for TEXT */ 601 char *data; 602 603 source = XawTextGetSource (text); 604 XtSetArg (args[0], XtNstring, &data); 605 XtGetValues (source, args, 1); 606 607 if (*target == XA_UTF8_STRING(d)) 608 style = XUTF8StringStyle; 609 else if (*target == XA_COMPOUND_TEXT(d)) 610 style = XCompoundTextStyle; 611 else if (*target == XA_STRING) 612 style = XStringStyle; 613 614 ret = XmbTextListToTextProperty (d, &data, 1, style, &prop); 615 if (ret >= Success) { 616 *length = prop.nitems; 617 *value = prop.value; 618 *type = prop.encoding; 619 *format = prop.format; 620 return True; 621 } else 622 return False; 623 } 624 625 if (XmuConvertStandardSelection(w, req->time, selection, target, type, 626 (XPointer *) value, length, format)) 627 return True; 628 629 return False; 630} 631 632static void 633LoseSelection(Widget w, Atom *selection) 634{ 635 Display *d = XtDisplay(w); 636 XtGetSelectionValue(w, *selection, XA_UTF8_STRING(d), InsertClipboard, 637 (XtPointer)(XA_UTF8_STRING(d)), CurrentTime); 638} 639 640/*ARGSUSED*/ 641static Boolean 642RefuseSelection(Widget w, Atom *selection, Atom *target, 643 Atom *type, XtPointer *value, unsigned long *length, 644 int *format) 645{ 646 return False; 647} 648 649/*ARGSUSED*/ 650static void 651LoseManager(Widget w, Atom *selection) 652{ 653 XtError("another clipboard has taken over control\n"); 654} 655 656typedef struct { 657 Boolean wrap; 658} ResourceData, *ResourceDataPtr; 659 660static ResourceData userOptions; 661 662#define Offset(field) XtOffsetOf(ResourceData, field) 663 664static XtResource resources[] = { 665 {"wrap", "Wrap", XtRBoolean, sizeof(Boolean), 666 Offset(wrap), XtRImmediate, (XtPointer)False} 667}; 668 669#undef Offset 670 671int 672main(int argc, char *argv[]) 673{ 674 Arg args[4]; 675 Cardinal n; 676 XtAppContext xtcontext; 677 Widget parent; 678 679 XtSetLanguageProc(NULL, NULL, NULL); 680 681 top = XtAppInitialize( &xtcontext, "XClipboard", table, XtNumber(table), 682 &argc, argv, fallback_resources, NULL, 0); 683 684 XtGetApplicationResources(top, (XtPointer)&userOptions, resources, 685 XtNumber(resources), NULL, 0); 686 687 XtAppAddActions (xtcontext, 688 xclipboard_actions, XtNumber (xclipboard_actions)); 689 /* CLIPBOARD_MANAGER is a non-standard mechanism */ 690 ManagerAtom = XInternAtom(XtDisplay(top), "CLIPBOARD_MANAGER", False); 691 ClipboardAtom = XA_CLIPBOARD(XtDisplay(top)); 692 if (XGetSelectionOwner(XtDisplay(top), ManagerAtom)) 693 XtError("another clipboard is already running\n"); 694 695 parent = XtCreateManagedWidget("form", formWidgetClass, top, NULL, ZERO); 696 (void) XtCreateManagedWidget("quit", Command, parent, NULL, ZERO); 697 (void) XtCreateManagedWidget("delete", Command, parent, NULL, ZERO); 698 (void) XtCreateManagedWidget("new", Command, parent, NULL, ZERO); 699 (void) XtCreateManagedWidget("save", Command, parent, NULL, ZERO); 700 nextButton = XtCreateManagedWidget("next", Command, parent, NULL, ZERO); 701 prevButton = XtCreateManagedWidget("prev", Command, parent, NULL, ZERO); 702 indexLabel = XtCreateManagedWidget("index", Label, parent, NULL, ZERO); 703 704 n=0; 705 XtSetArg(args[n], XtNtype, XawAsciiString); n++; 706 XtSetArg(args[n], XtNeditType, XawtextEdit); n++; 707 if (userOptions.wrap) { 708 XtSetArg(args[n], XtNwrap, XawtextWrapWord); n++; 709 XtSetArg(args[n], XtNscrollHorizontal, False); n++; 710 } 711 712 text = XtCreateManagedWidget( "text", Text, parent, args, n); 713 714 currentClip = NewClip (text, (ClipPtr) 0); 715 716 set_button_state (); 717 718 fileDialogShell = XtCreatePopupShell("fileDialogShell", 719 transientShellWidgetClass, 720 top, NULL, ZERO); 721 fileDialog = XtCreateManagedWidget ("fileDialog", dialogWidgetClass, 722 fileDialogShell, NULL, ZERO); 723 XawDialogAddButton(fileDialog, "accept", NULL, NULL); 724 XawDialogAddButton(fileDialog, "cancel", NULL, NULL); 725 726 failDialogShell = XtCreatePopupShell("failDialogShell", 727 transientShellWidgetClass, 728 top, NULL, ZERO); 729 failDialog = XtCreateManagedWidget ("failDialog", dialogWidgetClass, 730 failDialogShell, NULL, ZERO); 731 XawDialogAddButton (failDialog, "continue", NULL, NULL); 732 733 XtRealizeWidget(top); 734 XtRealizeWidget(fileDialogShell); 735 XtRealizeWidget(failDialogShell); 736 XtOwnSelection(top, ManagerAtom, CurrentTime, 737 RefuseSelection, LoseManager, NULL); 738 if (XGetSelectionOwner (XtDisplay(top), ClipboardAtom)) { 739 LoseSelection (top, &ClipboardAtom); 740 } else { 741 XtOwnSelection(top, ClipboardAtom, CurrentTime, 742 ConvertSelection, LoseSelection, NULL); 743 } 744 wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False); 745 wm_protocols = XInternAtom(XtDisplay(top), "WM_PROTOCOLS", False); 746 (void) XSetWMProtocols(XtDisplay(top), XtWindow(top), &wm_delete_window,1); 747 (void) XSetWMProtocols(XtDisplay(top), XtWindow(fileDialogShell), 748 &wm_delete_window,1); 749 (void) XSetWMProtocols(XtDisplay(top), XtWindow(failDialogShell), 750 &wm_delete_window,1); 751 XtAppMainLoop(xtcontext); 752 exit(0); 753} 754