xclipboard.c revision 97e8a2e8
1/* 2 * $Xorg: xclipboard.c,v 1.4 2001/02/09 02:05:38 xorgcvs Exp $ 3 * 4 * 5Copyright 1989, 1998 The Open Group 6 7Permission to use, copy, modify, distribute, and sell this software and its 8documentation for any purpose is hereby granted without fee, provided that 9the above copyright notice appear in all copies and that both that 10copyright notice and this permission notice appear in supporting 11documentation. 12 13The above copyright notice and this permission notice shall be included in 14all copies or substantial portions of the Software. 15 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 23Except as contained in this notice, the name of The Open Group shall not be 24used in advertising or otherwise to promote the sale, use or other dealings 25in this Software without prior written authorization from The Open Group. 26 * * 27 * Author: Ralph Swick, DEC/Project Athena 28 * Updated for R4: Chris D. Peterson, MIT X Consortium. 29 * Reauthored by: Keith Packard, MIT X Consortium. 30 */ 31/* $XFree86: xc/programs/xclipboard/xclipboard.c,v 1.8tsi Exp $ */ 32 33#include <stdio.h> 34#include <X11/Intrinsic.h> 35#include <X11/StringDefs.h> 36#include <X11/Xatom.h> 37 38#include <X11/Xmu/Atoms.h> 39#include <X11/Xmu/StdSel.h> 40 41#include <X11/Shell.h> 42#include <X11/Xaw/Form.h> 43#include <X11/Xaw/Label.h> 44#include <X11/Xaw/Command.h> 45#include <X11/Xaw/AsciiText.h> 46#include <X11/Xaw/Dialog.h> 47#include <X11/Xaw/Cardinals.h> 48#include <X11/IntrinsicP.h> 49#include <X11/Xaw/TextP.h> 50#include <X11/Xfuncs.h> 51 52#ifdef XKB 53#include <X11/extensions/XKBbells.h> 54#endif 55 56#include <stdlib.h> 57 58#define Command commandWidgetClass 59#define Label labelWidgetClass 60#define Text asciiTextWidgetClass 61 62#define INFINITY 10000000 /* pretty big, huh? */ 63 64typedef struct _Clip { 65 struct _Clip *next, *prev; 66 char *clip; 67 char *filename; 68 int avail; 69} ClipRec, *ClipPtr; 70 71static Atom wm_delete_window; 72static Atom wm_protocols; 73 74static void EraseTextWidget ( void ); 75static void NewCurrentClipContents ( char *data, int len ); 76 77static long 78TextLength(Widget w) 79{ 80 return XawTextSourceScan (XawTextGetSource (w), 81 (XawTextPosition) 0, 82 XawstAll, XawsdRight, 1, TRUE); 83} 84 85static void 86SaveClip(Widget w, ClipPtr clip) 87{ 88 Arg args[1]; 89 char *data; 90 int len; 91 Widget source; 92 93 source = XawTextGetSource (w); 94 XtSetArg (args[0], XtNstring, &data); 95 XtGetValues (source, args, 1); 96 len = strlen (data); 97 if (len >= clip->avail) 98 { 99 if (clip->clip) 100 free (clip->clip); 101 clip->clip = malloc (len + 1); 102 if (!clip->clip) 103 clip->avail = 0; 104 else 105 clip->avail = len + 1; 106 } 107 if (clip->avail) 108 { 109 strcpy (clip->clip, data); 110 } 111} 112 113static void 114RestoreClip(Widget w, ClipPtr clip) 115{ 116 Arg args[1]; 117 Widget source; 118 119 source = XawTextGetSource (w); 120 XtSetArg (args[0], XtNstring, clip->clip); 121 XtSetValues (source, args, 1); 122} 123 124/*ARGSUSED*/ 125static ClipPtr 126NewClip(Widget w, ClipPtr old) 127{ 128 ClipPtr newClip; 129 130 newClip = (ClipPtr) malloc (sizeof (ClipRec)); 131 if (!newClip) 132 return newClip; 133 newClip->clip = 0; 134 newClip->avail = 0; 135 newClip->prev = old; 136 newClip->next = NULL; 137 newClip->filename = NULL; 138 if (old) 139 { 140 newClip->next = old->next; 141 old->next = newClip; 142 } 143 return newClip; 144} 145 146/*ARGSUSED*/ 147static void 148DeleteClip(Widget w, ClipPtr clip) 149{ 150 if (clip->prev) 151 clip->prev->next = clip->next; 152 if (clip->next) 153 clip->next->prev = clip->prev; 154 if (clip->clip) 155 free (clip->clip); 156 free ((char *) clip); 157} 158 159static ClipPtr currentClip; 160static Widget top; 161static Widget text, nextButton, prevButton, indexLabel; 162static Widget fileDialog, fileDialogShell; 163static Widget failDialog, failDialogShell; 164 165static int 166IndexCurrentClip (void) 167{ 168 int i = 0; 169 ClipPtr clip; 170 171 for (clip = currentClip; clip; clip = clip->prev) 172 i++; 173 return i; 174} 175 176static void 177set_button_state (void) 178{ 179 Boolean prevvalid, nextvalid; 180 Arg arg; 181 char labelString[10]; 182 183 prevvalid = currentClip->prev != NULL; 184 nextvalid = currentClip->next != NULL; 185 XtSetArg (arg, XtNsensitive, prevvalid); 186 XtSetValues (prevButton, &arg, ONE); 187 XtSetArg (arg, XtNsensitive, nextvalid); 188 XtSetValues (nextButton, &arg, ONE); 189 sprintf (labelString, "%d", IndexCurrentClip ()); 190 XtSetArg (arg, XtNlabel, labelString); 191 XtSetValues (indexLabel, &arg, ONE); 192} 193 194/* ARGSUSED */ 195static void 196NextCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 197{ 198 if (currentClip->next) 199 { 200 SaveClip (text, currentClip); 201 currentClip = currentClip->next; 202 RestoreClip (text, currentClip); 203 set_button_state (); 204 } 205} 206 207/* ARGSUSED */ 208static void 209PrevCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 210{ 211 if (currentClip->prev) 212 { 213 SaveClip (text, currentClip); 214 currentClip = currentClip->prev; 215 RestoreClip (text, currentClip); 216 set_button_state (); 217 } 218} 219 220/* ARGSUSED */ 221static void 222DeleteCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 223{ 224 ClipPtr newCurrent; 225 226 if (currentClip->prev) 227 newCurrent = currentClip->prev; 228 else 229 newCurrent = currentClip->next; 230 if (newCurrent) 231 { 232 DeleteClip (text, currentClip); 233 currentClip = newCurrent; 234 RestoreClip (text, currentClip); 235 } 236 else 237 EraseTextWidget (); 238 set_button_state (); 239} 240 241/* ARGSUSED */ 242static void 243Quit(Widget w, XEvent *ev, String *parms, Cardinal *np) 244{ 245 XtCloseDisplay (XtDisplay (text)); 246 exit (0); 247} 248 249static void 250CenterWidgetAtPoint(Widget w, int x, int y) 251{ 252 Arg args[2]; 253 Dimension width, height; 254 255 XtSetArg(args[0], XtNwidth, &width); 256 XtSetArg(args[1], XtNheight, &height); 257 XtGetValues (w, args, 2); 258 x = x - (int) width / 2; 259 y = y - (int) height / 2; 260 if (x < 0) 261 x = 0; 262 else { 263 int scr_width = WidthOfScreen (XtScreen(w)); 264 if (x + (int)width > scr_width) 265 x = scr_width - width; 266 } 267 if (y < 0) 268 y = 0; 269 else { 270 int scr_height = HeightOfScreen (XtScreen(w)); 271 if (y + (int)height > scr_height) 272 y = scr_height - height; 273 } 274 XtSetArg(args[0], XtNx, x); 275 XtSetArg(args[1], XtNy, y); 276 XtSetValues (w, args, 2); 277} 278 279static void 280CenterWidgetOnEvent(Widget w, XEvent *e) 281{ 282 CenterWidgetAtPoint (w, e->xbutton.x_root, e->xbutton.y_root); 283} 284 285static void 286CenterWidgetOnWidget(Widget w, Widget wT) 287{ 288 Position rootX, rootY; 289 Dimension width, height; 290 Arg args[2]; 291 292 XtSetArg (args[0], XtNwidth, &width); 293 XtSetArg (args[1], XtNheight, &height); 294 XtGetValues (wT, args, 2); 295 XtTranslateCoords (wT, (Position) width/2, (Position) height/2, &rootX, &rootY); 296 CenterWidgetAtPoint (w, (int) rootX, (int) rootY); 297} 298 299/*ARGSUSED*/ 300static void 301SaveToFile(Widget w, XEvent *e, String *argv, Cardinal *argc) 302{ 303 Arg args[1]; 304 char *filename; 305 306 filename = "clipboard"; 307 if (currentClip->filename) 308 filename = currentClip->filename; 309 XtSetArg(args[0], XtNvalue, filename); 310 XtSetValues (fileDialog, args, 1); 311 CenterWidgetOnEvent (fileDialogShell, e); 312 XtPopup (fileDialogShell, XtGrabNone); 313} 314 315/*ARGSUSED*/ 316static void 317AcceptSaveFile(Widget w, XEvent *e, String *argv, Cardinal *argc) 318{ 319 char *filename; 320 Boolean success; 321 Arg args[1]; 322 323 filename = XawDialogGetValueString (fileDialog); 324 success = XawAsciiSaveAsFile (XawTextGetSource (text), filename); 325 XtPopdown (fileDialogShell); 326 if (!success) 327 { 328 char failMessage[1024]; 329 330 sprintf (failMessage, "Can't open file \"%s\"", filename); 331 XtSetArg (args[0], XtNlabel, failMessage); 332 XtSetValues (failDialog, args, 1); 333 CenterWidgetOnEvent (failDialogShell, e); 334 XtPopup (failDialogShell, XtGrabNone); 335 } 336 else 337 { 338 if (currentClip->filename) 339 free (currentClip->filename); 340 currentClip->filename = malloc (strlen (filename) + 1); 341 if (currentClip->filename) 342 strcpy (currentClip->filename, filename); 343 } 344} 345 346/* ARGSUSED */ 347static void 348CancelSaveFile(Widget w, XEvent *ev, String *parms, Cardinal *np) 349{ 350 XtPopdown (fileDialogShell); 351} 352 353/* ARGSUSED */ 354static void 355FailContinue(Widget w, XEvent *ev, String *parms, Cardinal *np) 356{ 357 XtPopdown (failDialogShell); 358} 359 360/*ARGSUSED*/ 361static void 362WMProtocols(Widget w, XEvent *ev, String *params, Cardinal *n) 363{ 364 if (ev->type == ClientMessage && 365 ev->xclient.message_type == wm_protocols && 366 ev->xclient.data.l[0] == (long) wm_delete_window) { 367 while (w && !XtIsShell(w)) 368 w = XtParent(w); 369 if (w == top) 370 Quit(w, ev, params, n); 371 else if (w == fileDialogShell) 372 CancelSaveFile(w, ev, params, n); 373 else if (w == failDialogShell) 374 FailContinue(w, ev, params, n); 375 } 376} 377 378/* ARGUSED */ 379static void 380NewCurrentClip(Widget w, XEvent *ev, String *parms, Cardinal *np) 381{ 382 NewCurrentClipContents ("", 0); 383} 384 385static void 386NewCurrentClipContents(char *data, int len) 387{ 388 XawTextBlock textBlock; 389 390 SaveClip (text, currentClip); 391 392 /* append new clips at the end */ 393 while (currentClip && currentClip->next) 394 currentClip = currentClip->next; 395 /* any trailing clips with no text get overwritten */ 396 if (strlen (currentClip->clip) != 0) 397 currentClip = NewClip (text, currentClip); 398 399 textBlock.ptr = data; 400 textBlock.firstPos = 0; 401 textBlock.length = len; 402 textBlock.format = FMT8BIT; 403 if (XawTextReplace(text, 0, TextLength (text), &textBlock)) { 404#ifdef XKB 405 XkbStdBell(XtDisplay(text), XtWindow(text), 0, XkbBI_Info); 406#else 407 XBell( XtDisplay(text), 0); 408#endif 409 } 410 set_button_state (); 411} 412 413static void 414EraseTextWidget(void) 415{ 416 XawTextBlock block; 417 418 block.ptr = ""; 419 block.length = 0; 420 block.firstPos = 0; 421 block.format = FMT8BIT; 422 423 XawTextReplace(text, 0, INFINITY, &block); 424 /* If this fails, too bad. */ 425} 426 427 428XtActionsRec xclipboard_actions[] = { 429 { "NewClip", NewCurrentClip }, 430 { "NextClip", NextCurrentClip }, 431 { "PrevClip", PrevCurrentClip }, 432 { "DeleteClip", DeleteCurrentClip }, 433 { "Save", SaveToFile }, 434 { "AcceptSave", AcceptSaveFile }, 435 { "CancelSave", CancelSaveFile }, 436 { "FailContinue", FailContinue }, 437 { "Quit", Quit }, 438 { "WMProtocols", WMProtocols } 439}; 440 441static XrmOptionDescRec table[] = { 442 {"-w", "wrap", XrmoptionNoArg, "on"}, 443/* {"-nw", "wrap", XrmoptionNoArg, "False"} */ 444}; 445 446static Boolean ConvertSelection ( Widget w, Atom *selection, Atom *target, 447 Atom *type, XtPointer *value, 448 unsigned long *length, int *format ); 449static void LoseSelection ( Widget w, Atom *selection ); 450 451static Atom ManagerAtom, ClipboardAtom; 452 453/*ARGSUSED*/ 454static void 455InsertClipboard(Widget w, XtPointer client_data, Atom *selection, 456 Atom *type, XtPointer value, unsigned long *length, 457 int *format) 458{ 459 if (*type != XT_CONVERT_FAIL) 460 NewCurrentClipContents ((char *) value, *length); 461 else 462 { 463 Arg arg; 464 XtSetArg (arg, XtNlabel, "CLIPBOARD selection conversion failed"); 465 XtSetValues (failDialog, &arg, 1); 466 CenterWidgetOnWidget (failDialogShell, text); 467 XtPopup (failDialogShell, XtGrabNone); 468#ifdef XKB 469 XkbStdBell( XtDisplay(w), XtWindow(w), 0, XkbBI_MinorError ); 470#else 471 XBell( XtDisplay(w), 0 ); 472#endif 473 } 474 475 XtOwnSelection(top, ClipboardAtom, CurrentTime, 476 ConvertSelection, LoseSelection, NULL); 477 XFree(value); 478} 479 480static Boolean 481ConvertSelection(Widget w, Atom *selection, Atom *target, 482 Atom *type, XtPointer *value, unsigned long *length, 483 int *format) 484{ 485 Display* d = XtDisplay(w); 486 XSelectionRequestEvent* req = 487 XtGetSelectionRequest(w, *selection, (XtRequestId)NULL); 488 489 if (*target == XA_TARGETS(d)) { 490 Atom* targetP; 491 Atom* std_targets; 492 unsigned long std_length; 493 XmuConvertStandardSelection(w, req->time, selection, target, type, 494 (XPointer*)&std_targets, &std_length, 495 format); 496 *value = XtMalloc(sizeof(Atom)*(std_length + 5)); 497 targetP = *(Atom**)value; 498 *targetP++ = XA_STRING; 499 *targetP++ = XA_TEXT(d); 500 *targetP++ = XA_LENGTH(d); 501 *targetP++ = XA_LIST_LENGTH(d); 502 *targetP++ = XA_CHARACTER_POSITION(d); 503 *length = std_length + (targetP - (*(Atom **) value)); 504 memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length); 505 XtFree((char*)std_targets); 506 *type = XA_ATOM; 507 *format = 32; 508 return True; 509 } 510 511 if (*target == XA_LIST_LENGTH(d) || 512 *target == XA_LENGTH(d)) 513 { 514 long * temp; 515 516 temp = (long *) XtMalloc(sizeof(long)); 517 if (*target == XA_LIST_LENGTH(d)) 518 *temp = 1L; 519 else /* *target == XA_LENGTH(d) */ 520 *temp = (long) TextLength (text); 521 522 *value = (XPointer) temp; 523 *type = XA_INTEGER; 524 *length = 1L; 525 *format = 32; 526 return True; 527 } 528 529 if (*target == XA_CHARACTER_POSITION(d)) 530 { 531 long * temp; 532 533 temp = (long *) XtMalloc(2 * sizeof(long)); 534 temp[0] = (long) 0; 535 temp[1] = TextLength (text); 536 *value = (XPointer) temp; 537 *type = XA_SPAN(d); 538 *length = 2L; 539 *format = 32; 540 return True; 541 } 542 543 if (*target == XA_STRING || 544 *target == XA_TEXT(d) || 545 *target == XA_COMPOUND_TEXT(d)) 546 { 547 if (*target == XA_COMPOUND_TEXT(d)) 548 *type = *target; 549 else 550 *type = XA_STRING; 551 *length = TextLength (text); 552 *value = _XawTextGetSTRING((TextWidget) text, 0, *length); 553 *format = 8; 554 return True; 555 } 556 557 if (XmuConvertStandardSelection(w, req->time, selection, target, type, 558 (XPointer *) value, length, format)) 559 return True; 560 561 return False; 562} 563 564static void 565LoseSelection(Widget w, Atom *selection) 566{ 567 XtGetSelectionValue(w, *selection, XA_STRING, InsertClipboard, 568 NULL, CurrentTime); 569} 570 571/*ARGSUSED*/ 572static Boolean 573RefuseSelection(Widget w, Atom *selection, Atom *target, 574 Atom *type, XtPointer *value, unsigned long *length, 575 int *format) 576{ 577 return False; 578} 579 580/*ARGSUSED*/ 581static void 582LoseManager(Widget w, Atom *selection) 583{ 584 XtError("another clipboard has taken over control\n"); 585} 586 587typedef struct { 588 Boolean wrap; 589} ResourceData, *ResourceDataPtr; 590 591static ResourceData userOptions; 592 593#define Offset(field) XtOffsetOf(ResourceData, field) 594 595XtResource resources[] = { 596 {"wrap", "Wrap", XtRBoolean, sizeof(Boolean), 597 Offset(wrap), XtRImmediate, (XtPointer)False} 598}; 599 600#undef Offset 601 602int 603main(int argc, char *argv[]) 604{ 605 Arg args[4]; 606 Cardinal n; 607 XtAppContext xtcontext; 608 Widget parent; 609 610 XtSetLanguageProc(NULL, NULL, NULL); 611 612 top = XtAppInitialize( &xtcontext, "XClipboard", table, XtNumber(table), 613 &argc, argv, NULL, NULL, 0); 614 615 XtGetApplicationResources(top, (XtPointer)&userOptions, resources, 616 XtNumber(resources), NULL, 0); 617 618 XtAppAddActions (xtcontext, 619 xclipboard_actions, XtNumber (xclipboard_actions)); 620 /* CLIPBOARD_MANAGER is a non-standard mechanism */ 621 ManagerAtom = XInternAtom(XtDisplay(top), "CLIPBOARD_MANAGER", False); 622 ClipboardAtom = XA_CLIPBOARD(XtDisplay(top)); 623 if (XGetSelectionOwner(XtDisplay(top), ManagerAtom)) 624 XtError("another clipboard is already running\n"); 625 626 parent = XtCreateManagedWidget("form", formWidgetClass, top, NULL, ZERO); 627 (void) XtCreateManagedWidget("quit", Command, parent, NULL, ZERO); 628 (void) XtCreateManagedWidget("delete", Command, parent, NULL, ZERO); 629 (void) XtCreateManagedWidget("new", Command, parent, NULL, ZERO); 630 (void) XtCreateManagedWidget("save", Command, parent, NULL, ZERO); 631 nextButton = XtCreateManagedWidget("next", Command, parent, NULL, ZERO); 632 prevButton = XtCreateManagedWidget("prev", Command, parent, NULL, ZERO); 633 indexLabel = XtCreateManagedWidget("index", Label, parent, NULL, ZERO); 634 635 n=0; 636 XtSetArg(args[n], XtNtype, XawAsciiString); n++; 637 XtSetArg(args[n], XtNeditType, XawtextEdit); n++; 638 if (userOptions.wrap) { 639 XtSetArg(args[n], XtNwrap, XawtextWrapWord); n++; 640 XtSetArg(args[n], XtNscrollHorizontal, False); n++; 641 } 642 643 text = XtCreateManagedWidget( "text", Text, parent, args, n); 644 645 currentClip = NewClip (text, (ClipPtr) 0); 646 647 set_button_state (); 648 649 fileDialogShell = XtCreatePopupShell("fileDialogShell", 650 transientShellWidgetClass, 651 top, NULL, ZERO); 652 fileDialog = XtCreateManagedWidget ("fileDialog", dialogWidgetClass, 653 fileDialogShell, NULL, ZERO); 654 XawDialogAddButton(fileDialog, "accept", NULL, NULL); 655 XawDialogAddButton(fileDialog, "cancel", NULL, NULL); 656 657 failDialogShell = XtCreatePopupShell("failDialogShell", 658 transientShellWidgetClass, 659 top, NULL, ZERO); 660 failDialog = XtCreateManagedWidget ("failDialog", dialogWidgetClass, 661 failDialogShell, NULL, ZERO); 662 XawDialogAddButton (failDialog, "continue", NULL, NULL); 663 664 XtRealizeWidget(top); 665 XtRealizeWidget(fileDialogShell); 666 XtRealizeWidget(failDialogShell); 667 XtOwnSelection(top, ManagerAtom, CurrentTime, 668 RefuseSelection, LoseManager, NULL); 669 if (XGetSelectionOwner (XtDisplay(top), ClipboardAtom)) { 670 LoseSelection (top, &ClipboardAtom); 671 } else { 672 XtOwnSelection(top, ClipboardAtom, CurrentTime, 673 ConvertSelection, LoseSelection, NULL); 674 } 675 wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", False); 676 wm_protocols = XInternAtom(XtDisplay(top), "WM_PROTOCOLS", False); 677 (void) XSetWMProtocols(XtDisplay(top), XtWindow(top), &wm_delete_window,1); 678 (void) XSetWMProtocols(XtDisplay(top), XtWindow(fileDialogShell), 679 &wm_delete_window,1); 680 (void) XSetWMProtocols(XtDisplay(top), XtWindow(failDialogShell), 681 &wm_delete_window,1); 682 XtAppMainLoop(xtcontext); 683 exit(0); 684} 685