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