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