xclipboard.c revision c3f70f0a
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[1024]; 336 337 sprintf (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 } 343 else 344 { 345 if (currentClip->filename) 346 free (currentClip->filename); 347 currentClip->filename = malloc (strlen (filename) + 1); 348 if (currentClip->filename) 349 strcpy (currentClip->filename, filename); 350 } 351} 352 353/* ARGSUSED */ 354static void 355CancelSaveFile(Widget w, XEvent *ev, String *parms, Cardinal *np) 356{ 357 XtPopdown (fileDialogShell); 358} 359 360/* ARGSUSED */ 361static void 362FailContinue(Widget w, XEvent *ev, String *parms, Cardinal *np) 363{ 364 XtPopdown (failDialogShell); 365} 366 367/*ARGSUSED*/ 368static void 369WMProtocols(Widget w, XEvent *ev, String *params, Cardinal *n) 370{ 371 if (ev->type == ClientMessage && 372 ev->xclient.message_type == wm_protocols && 373 ev->xclient.data.l[0] == (long) wm_delete_window) { 374 while (w && !XtIsShell(w)) 375 w = XtParent(w); 376 if (w == top) 377 Quit(w, ev, params, n); 378 else if (w == fileDialogShell) 379 CancelSaveFile(w, ev, params, n); 380 else if (w == failDialogShell) 381 FailContinue(w, ev, params, n); 382 } 383} 384 385/* ARGUSED */ 386static void 387NewCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 388{ 389 NewCurrentClipContents ("", 0); 390} 391 392static void 393NewCurrentClipContents(char *data, int len) 394{ 395 XawTextBlock textBlock; 396 397 SaveClip (text, currentClip); 398 399 /* append new clips at the end */ 400 while (currentClip && currentClip->next) 401 currentClip = currentClip->next; 402 /* any trailing clips with no text get overwritten */ 403 if (strlen (currentClip->clip) != 0) 404 currentClip = NewClip (text, currentClip); 405 406 textBlock.ptr = data; 407 textBlock.firstPos = 0; 408 textBlock.length = len; 409 textBlock.format = FMT8BIT; 410 if (XawTextReplace(text, 0, TextLength (text), &textBlock)) { 411#ifdef XKB 412 XkbStdBell(XtDisplay(text), XtWindow(text), 0, XkbBI_Info); 413#else 414 XBell( XtDisplay(text), 0); 415#endif 416 } 417 set_button_state (); 418} 419 420static void 421EraseTextWidget(void) 422{ 423 XawTextBlock block; 424 425 block.ptr = ""; 426 block.length = 0; 427 block.firstPos = 0; 428 block.format = FMT8BIT; 429 430 XawTextReplace(text, 0, INFINITY, &block); 431 /* If this fails, too bad. */ 432} 433 434 435static XtActionsRec xclipboard_actions[] = { 436 { "NewClip", NewCurrentClip }, 437 { "NextClip", NextCurrentClip }, 438 { "PrevClip", PrevCurrentClip }, 439 { "DeleteClip", DeleteCurrentClip }, 440 { "Save", SaveToFile }, 441 { "AcceptSave", AcceptSaveFile }, 442 { "CancelSave", CancelSaveFile }, 443 { "FailContinue", FailContinue }, 444 { "Quit", Quit }, 445 { "WMProtocols", WMProtocols } 446}; 447 448static XrmOptionDescRec table[] = { 449 {"-w", "wrap", XrmoptionNoArg, "on"}, 450/* {"-nw", "wrap", XrmoptionNoArg, "False"} */ 451}; 452 453static Boolean ConvertSelection ( Widget w, Atom *selection, Atom *target, 454 Atom *type, XtPointer *value, 455 unsigned long *length, int *format ); 456static void LoseSelection ( Widget w, Atom *selection ); 457 458static Atom ManagerAtom, ClipboardAtom; 459 460/*ARGSUSED*/ 461static void 462InsertClipboard(Widget w, XtPointer client_data, Atom *selection, 463 Atom *type, XtPointer value, unsigned long *length, 464 int *format) 465{ 466 Display *d = XtDisplay(w); 467 Atom target = (Atom)client_data; 468 Boolean convert_failed = (*type == XT_CONVERT_FAIL); 469 470 if (!convert_failed) 471 { 472 char **list; 473 int i, ret, count; 474 475 XTextProperty prop; 476 prop.value = value; 477 prop.nitems = *length; 478 prop.format = *format; 479 prop.encoding = *type; 480 ret = XmbTextPropertyToTextList(d, &prop, &list, &count); 481 if (ret >= Success) 482 { 483 /* manuals say something about multiple strings in a disjoint 484 text selection (?), it should be harmless to get them all */ 485 for (i = 0; i < count; i++) 486 NewCurrentClipContents(list[i], strlen(list[i])); 487 XFreeStringList(list); 488 } else 489 convert_failed = True; 490 XFree(value); 491 } 492 493 if (convert_failed) { 494 /* if UTF8_STRING failed try COMPOUND_TEXT */ 495 if (target == XA_UTF8_STRING(d)) 496 { 497 XtGetSelectionValue(w, *selection, XA_COMPOUND_TEXT(d), 498 InsertClipboard, 499 (XtPointer)(XA_COMPOUND_TEXT(d)), 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 XtGetSelectionValue(w, *selection, XA_UTF8_STRING(d), InsertClipboard, 642 (XtPointer)(XA_UTF8_STRING(d)), CurrentTime); 643} 644 645/*ARGSUSED*/ 646static Boolean 647RefuseSelection(Widget w, Atom *selection, Atom *target, 648 Atom *type, XtPointer *value, unsigned long *length, 649 int *format) 650{ 651 return False; 652} 653 654/*ARGSUSED*/ 655static void 656LoseManager(Widget w, Atom *selection) 657{ 658 XtError("another clipboard has taken over control\n"); 659} 660 661typedef struct { 662 Boolean wrap; 663} ResourceData, *ResourceDataPtr; 664 665static ResourceData userOptions; 666 667#define Offset(field) XtOffsetOf(ResourceData, field) 668 669static XtResource resources[] = { 670 {"wrap", "Wrap", XtRBoolean, sizeof(Boolean), 671 Offset(wrap), XtRImmediate, (XtPointer)False} 672}; 673 674#undef Offset 675 676int 677main(int argc, char *argv[]) 678{ 679 Arg args[4]; 680 Cardinal n; 681 XtAppContext xtcontext; 682 Widget parent; 683 684 XtSetLanguageProc(NULL, NULL, NULL); 685 686 top = XtAppInitialize( &xtcontext, "XClipboard", table, XtNumber(table), 687 &argc, argv, fallback_resources, NULL, 0); 688 689 XtGetApplicationResources(top, (XtPointer)&userOptions, resources, 690 XtNumber(resources), NULL, 0); 691 692 XtAppAddActions (xtcontext, 693 xclipboard_actions, XtNumber (xclipboard_actions)); 694 /* CLIPBOARD_MANAGER is a non-standard mechanism */ 695 ManagerAtom = XInternAtom(XtDisplay(top), "CLIPBOARD_MANAGER", False); 696 ClipboardAtom = XA_CLIPBOARD(XtDisplay(top)); 697 if (XGetSelectionOwner(XtDisplay(top), ManagerAtom)) 698 XtError("another clipboard is already running\n"); 699 700 parent = XtCreateManagedWidget("form", formWidgetClass, top, NULL, ZERO); 701 (void) XtCreateManagedWidget("quit", Command, parent, NULL, ZERO); 702 (void) XtCreateManagedWidget("delete", Command, parent, NULL, ZERO); 703 (void) XtCreateManagedWidget("new", Command, parent, NULL, ZERO); 704 (void) XtCreateManagedWidget("save", Command, parent, NULL, ZERO); 705 nextButton = XtCreateManagedWidget("next", Command, parent, NULL, ZERO); 706 prevButton = XtCreateManagedWidget("prev", Command, parent, NULL, ZERO); 707 indexLabel = XtCreateManagedWidget("index", Label, parent, NULL, ZERO); 708 709 n=0; 710 XtSetArg(args[n], XtNtype, XawAsciiString); n++; 711 XtSetArg(args[n], XtNeditType, XawtextEdit); n++; 712 if (userOptions.wrap) { 713 XtSetArg(args[n], XtNwrap, XawtextWrapWord); n++; 714 XtSetArg(args[n], XtNscrollHorizontal, False); n++; 715 } 716 717 text = XtCreateManagedWidget( "text", Text, parent, args, n); 718 719 currentClip = NewClip (text, (ClipPtr) 0); 720 721 set_button_state (); 722 723 fileDialogShell = XtCreatePopupShell("fileDialogShell", 724 transientShellWidgetClass, 725 top, NULL, ZERO); 726 fileDialog = XtCreateManagedWidget ("fileDialog", dialogWidgetClass, 727 fileDialogShell, NULL, ZERO); 728 XawDialogAddButton(fileDialog, "accept", NULL, NULL); 729 XawDialogAddButton(fileDialog, "cancel", NULL, NULL); 730 731 failDialogShell = XtCreatePopupShell("failDialogShell", 732 transientShellWidgetClass, 733 top, NULL, ZERO); 734 failDialog = XtCreateManagedWidget ("failDialog", dialogWidgetClass, 735 failDialogShell, NULL, ZERO); 736 XawDialogAddButton (failDialog, "continue", NULL, NULL); 737 738 XtRealizeWidget(top); 739 XtRealizeWidget(fileDialogShell); 740 XtRealizeWidget(failDialogShell); 741 XtOwnSelection(top, ManagerAtom, CurrentTime, 742 RefuseSelection, LoseManager, NULL); 743 if (XGetSelectionOwner (XtDisplay(top), ClipboardAtom)) { 744 LoseSelection (top, &ClipboardAtom); 745 } else { 746 XtOwnSelection(top, ClipboardAtom, CurrentTime, 747 ConvertSelection, LoseSelection, NULL); 748 } 749 wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False); 750 wm_protocols = XInternAtom(XtDisplay(top), "WM_PROTOCOLS", False); 751 (void) XSetWMProtocols(XtDisplay(top), XtWindow(top), &wm_delete_window,1); 752 (void) XSetWMProtocols(XtDisplay(top), XtWindow(fileDialogShell), 753 &wm_delete_window,1); 754 (void) XSetWMProtocols(XtDisplay(top), XtWindow(failDialogShell), 755 &wm_delete_window,1); 756 XtAppMainLoop(xtcontext); 757 exit(0); 758} 759