Logo Search packages:      
Sourcecode: fbi version File versions  Download package

selections.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <Xm/Xm.h>
#include <Xm/Transfer.h>
#include <Xm/TransferP.h>
#include <Xm/DragIcon.h>

#include "ida.h"
#include "readers.h"
#include "writers.h"
#include "viewer.h"
#include "xwd.h"
#include "xdnd.h"
#include "selections.h"
#include "list.h"

Atom XA_TARGETS, XA_DONE;
Atom XA_FILE_NAME, XA_FILE;
Atom XA_BACKGROUND, XA_FOREGROUND, XA_PIXEL;
Atom _MOTIF_EXPORT_TARGETS;
Atom _MOTIF_CLIPBOARD_TARGETS;
Atom _MOTIF_DEFERRED_CLIPBOARD_TARGETS;
Atom _MOTIF_SNAPSHOT;
Atom _MOTIF_DROP;
Atom _MOTIF_LOSE_SELECTION;
Atom _NETSCAPE_URL;
Atom MIME_TEXT_URI_LIST;
Atom MIME_IMAGE_PPM;
Atom MIME_IMAGE_PGM;
Atom MIME_IMAGE_XPM;
Atom MIME_IMAGE_BMP;
Atom MIME_IMAGE_JPEG;
Atom MIME_IMAGE_GIF;
Atom MIME_IMAGE_PNG;
Atom MIME_IMAGE_TIFF;

/* ---------------------------------------------------------------------- */
/* send data (drags, copy)                                                */

struct sel_data {
    struct list_head  list;
    Atom              atom;
    struct ida_image  img;
    Pixmap            pixmap;
    char              *filename;
    Pixmap            icon_pixmap;
    Widget            icon_widget;
};
static struct list_head selections;

static void
iconify(Widget widget, struct sel_data *data)
{
    struct ida_image small;
    unsigned int scale,x,y,depth;
    char *src,*dst;
    Arg args[4];
    Cardinal n=0;

    /* calc size */
    memset(&small,0,sizeof(small));
    for (scale = 1;; scale++) {
      small.i.width  = data->img.i.width  / scale;
      small.i.height = data->img.i.height / scale;
      if (small.i.width < 128 && small.i.height < 128)
          break;
    }

    /* scale down & create pixmap */
    dst = small.data = malloc(small.i.width * small.i.height * 3);
    for (y = 0; y < small.i.height; y++) {
      src = data->img.data + 3 * y * scale * data->img.i.width;
      for (x = 0; x < small.i.width; x++) {
          dst[0] = src[0];
          dst[1] = src[1];
          dst[2] = src[2];
          dst += 3;
          src += 3*scale;
      }
    }
    data->icon_pixmap = image_to_pixmap(&small);

    /* build DnD icon */
    n = 0;
    depth = DefaultDepthOfScreen(XtScreen(widget));
    XtSetArg(args[n], XmNpixmap, data->icon_pixmap); n++;
    XtSetArg(args[n], XmNwidth,  small.i.width); n++;
    XtSetArg(args[n], XmNheight, small.i.height); n++;
    XtSetArg(args[n], XmNdepth,  depth); n++;
    data->icon_widget = XmCreateDragIcon(widget,"dragicon",args,n);
    
    free(small.data);
}

static struct sel_data*
sel_find(Atom selection)
{
    struct list_head  *item;
    struct sel_data   *sel;
    
    list_for_each(item,&selections) {
      sel = list_entry(item, struct sel_data, list);
      if (sel->atom == selection)
          return sel;
    }
    return NULL;
}

static void
sel_free(Atom selection)
{
    struct sel_data   *sel;

    sel = sel_find(selection);
    if (NULL == sel)
      return;
    if (sel->filename) {
      unlink(sel->filename);
      free(sel->filename);
    }
    if (sel->icon_widget)
      XtDestroyWidget(sel->icon_widget);
    if (sel->icon_pixmap)
      XFreePixmap(dpy,sel->icon_pixmap);
    if (sel->pixmap)
      XFreePixmap(dpy,sel->pixmap);
    if (sel->img.data)
      free(sel->img.data);

    list_del(&sel->list);
    free(sel);
}

static struct sel_data*
sel_init(Atom selection)
{
    struct sel_data   *sel;

    sel_free(selection);
    sel = malloc(sizeof(*sel));
    memset(sel,0,sizeof(*sel));

    sel->atom = selection;
    sel->img  = ida->img;
    sel->img.data = malloc(ida->img.i.width * ida->img.i.height * 3);
    memcpy(sel->img.data, ida->img.data,
         ida->img.i.width * ida->img.i.height * 3);

    list_add_tail(&sel->list,&selections);
    return sel;
}

static void
sel_tmpfile(struct sel_data *sel, struct ida_writer *wr)
{
    static char *base = "ida";
    char *tmpdir;
    FILE *fp;
    int fd;

    tmpdir = getenv("TMPDIR");
    if (NULL == tmpdir)
      tmpdir="/tmp";
    sel->filename = malloc(strlen(tmpdir)+strlen(base)+16);
    sprintf(sel->filename,"%s/%s-XXXXXX",tmpdir,base);
    fd = mkstemp(sel->filename);
    fp = fdopen(fd,"w");
    wr->write(fp,&sel->img);
    fclose(fp);
}

Atom sel_unique_atom(Widget widget)
{
    char id_name[32];
    Atom id;
    int i;

    for (i = 0;; i++) {
      sprintf(id_name,"_IDA_DATA_%lX_%d",XtWindow(widget),i);
      id = XInternAtom(XtDisplay(widget),id_name,False);
      if (NULL == sel_find(id))
          break;
    }
    return id;
}

void
selection_convert(Widget widget, XtPointer ignore, XtPointer call_data)
{
    XmConvertCallbackStruct *ccs = call_data;
    unsigned long *ldata;
    unsigned char *cdata;
    struct sel_data *sel;
    char *filename;
    int n;

    if (debug) {
      char *t = !ccs->target    ? NULL : XGetAtomName(dpy,ccs->target);
      char *s = !ccs->selection ? NULL : XGetAtomName(dpy,ccs->selection);
      fprintf(stderr,"conv: target=%s selection=%s\n",t,s);
      if (t) XFree(t);
      if (s) XFree(s);
    }

    /* tell which formats we can handle */
    if ((ccs->target == XA_TARGETS) ||
      (ccs->target == _MOTIF_CLIPBOARD_TARGETS) ||
      (ccs->target == _MOTIF_DEFERRED_CLIPBOARD_TARGETS) ||
      (ccs->target == _MOTIF_EXPORT_TARGETS)) {
      n = 0;
      ldata = (Atom*)XtMalloc(sizeof(Atom)*12);
      if (ccs->target != _MOTIF_CLIPBOARD_TARGETS) {
          ldata[n++] = XA_TARGETS;
          ldata[n++] = MIME_IMAGE_PPM;
          ldata[n++] = XA_PIXMAP;
          ldata[n++] = XA_FOREGROUND;
          ldata[n++] = XA_BACKGROUND;
          ldata[n++] = XA_COLORMAP;
          ldata[n++] = XA_FILE_NAME;
          ldata[n++] = XA_FILE;
          ldata[n++] = MIME_TEXT_URI_LIST;
          ldata[n++] = _NETSCAPE_URL;
      }
      ccs->value  = ldata;
      ccs->length = n;
      ccs->type   = XA_ATOM;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;
      return;

    } else if (ccs->target == _MOTIF_SNAPSHOT) {
      /* save away clipboard data */
      n = 0;
      ldata = (Atom*)XtMalloc(sizeof(Atom));
      ldata[n++] = sel_unique_atom(widget);
      sel_init(ldata[0]);
      ccs->value  = ldata;
      ccs->length = n;
      ccs->type   = XA_ATOM;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;
      return;
    }

    sel = sel_find(ccs->selection);
    if (NULL == sel) {
      /* should not happen */
      fprintf(stderr,"Oops: selection not found\n");
      ccs->status = XmCONVERT_REFUSE;
      return;
    }

    if ((ccs->target == _MOTIF_LOSE_SELECTION) ||
      (ccs->target == XA_DONE)) {
      /* free stuff */
      sel_free(ccs->selection);
      ccs->value  = NULL;
      ccs->length = 0;
      ccs->type   = XA_INTEGER;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;
      return;
    }

    /* convert data */
    if (ccs->target == XA_BACKGROUND ||
      ccs->target == XA_FOREGROUND ||
      ccs->target == XA_COLORMAP) {
      n = 0;
      ldata = (Atom*)XtMalloc(sizeof(Atom)*8);
      if (ccs->target == XA_BACKGROUND) {
          ldata[n++] = WhitePixelOfScreen(XtScreen(widget));
          ccs->type  = XA_PIXEL;
      }
      if (ccs->target == XA_FOREGROUND) {
          ldata[n++] = BlackPixelOfScreen(XtScreen(widget));
          ccs->type  = XA_PIXEL;
      }
      if (ccs->target == XA_COLORMAP) {
          ldata[n++] = DefaultColormapOfScreen(XtScreen(widget));
          ccs->type  = XA_COLORMAP;
      }
      ccs->value  = ldata;
      ccs->length = n;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;

    } else if (ccs->target == XA_PIXMAP) {
      /* xfer pixmap id */
      if (!sel->pixmap)
          sel->pixmap = image_to_pixmap(&sel->img);
      ldata = (Pixmap*)XtMalloc(sizeof(Pixmap));
      ldata[0] = sel->pixmap;
      if (debug)
          fprintf(stderr,"conv: pixmap id is 0x%lx\n",ldata[0]);
      ccs->value  = ldata;
      ccs->length = 1;
      ccs->type   = XA_DRAWABLE;
      ccs->format = 32;
      ccs->status = XmCONVERT_DONE;

    } else if (ccs->target == MIME_IMAGE_PPM) {
      /* xfer image data directly */
      cdata = XtMalloc(sel->img.i.width * sel->img.i.height * 3 + 32);
      n = sprintf(cdata,"P6\n%d %d\n255\n",
                sel->img.i.width, sel->img.i.height);
      memcpy(cdata+n, sel->img.data, sel->img.i.width*sel->img.i.height*3);
      ccs->value  = cdata;
      ccs->length = n + sel->img.i.width * sel->img.i.height * 3;
      ccs->type   = MIME_IMAGE_PPM;
      ccs->format = 8;
      ccs->status = XmCONVERT_DONE;

    } else if (ccs->target == XA_FILE_NAME       ||
             ccs->target == XA_FILE            ||
             ccs->target == XA_STRING          ||
             ccs->target == MIME_TEXT_URI_LIST ||
             ccs->target == _NETSCAPE_URL) {
      /* xfer image via tmp file */
      if (NULL == sel->filename)
          sel_tmpfile(sel,&jpeg_writer);
      if (ccs->target == MIME_TEXT_URI_LIST ||
          ccs->target == _NETSCAPE_URL) {
          /* filename => url */
          filename = XtMalloc(strlen(sel->filename)+8);
          sprintf(filename,"file:%s\r\n",sel->filename);
          ccs->type = ccs->target;
          if (debug)
            fprintf(stderr,"conv: tmp url is %s\n",filename);
      } else {
          filename = XtMalloc(strlen(sel->filename));
          strcpy(filename,sel->filename);
          ccs->type = XA_STRING;
          if (debug)
            fprintf(stderr,"conv: tmp file is %s\n",filename);
      }
      ccs->value  = filename;
      ccs->length = strlen(filename);
      ccs->format = 8;
      ccs->status = XmCONVERT_DONE;

    } else {
      /* shouldn't happen */
      fprintf(stderr,"Huh? unknown target\n");
      ccs->status = XmCONVERT_REFUSE;
    }
}

static void
dnd_done(Widget widget, XtPointer ignore, XtPointer call_data)
{
    if (debug)
      fprintf(stderr,"conv: transfer finished\n");
    sel_free(_MOTIF_DROP);
}

/* ---------------------------------------------------------------------- */
/* receive data (drops, paste)                                            */

static Atom targets[16];
static Cardinal ntargets;

static void
selection_xfer(Widget widget, XtPointer ignore, XtPointer call_data)
{
    XmSelectionCallbackStruct *scs = call_data;
    unsigned char *cdata = scs->value;
    unsigned long *ldata = scs->value;
    Atom target = 0;
    unsigned int i,j,pending;
    char *file,*tmp;

    if (debug) {
      char *y = !scs->type      ? NULL : XGetAtomName(dpy,scs->type);
      char *t = !scs->target    ? NULL : XGetAtomName(dpy,scs->target);
      char *s = !scs->selection ? NULL : XGetAtomName(dpy,scs->selection);
      fprintf(stderr,"xfer: id=%p target=%s type=%s selection=%s\n",
            scs->transfer_id,t,y,s);
      if (y) XFree(y);
      if (t) XFree(t);
      if (s) XFree(s);
    }

    pending = scs->remaining;
    if (scs->target == XA_TARGETS) {
      /* look if we find a target we can deal with ... */
      for (i = 0; !target && i < scs->length; i++) {
          for (j = 0; j < ntargets; j++) {
            if (ldata[i] == targets[j]) {
                target = ldata[i];
                break;
            }
          }
      }
      if (target) {
          XmTransferValue(scs->transfer_id, target, selection_xfer,
                      NULL, XtLastTimestampProcessed(dpy));
          pending++;
      }
      if (debug) {
          fprintf(stderr,"xfer: available targets: ");
          for (i = 0; i < scs->length; i++) {
            char *name = !ldata[i] ? NULL : XGetAtomName(dpy,ldata[i]);
            fprintf(stderr,"%s%s", i != 0 ? ", " : "", name);
            XFree(name);
          }
          fprintf(stderr,"\n");
          if (0 == scs->length)
            fprintf(stderr,"xfer: Huh? no TARGETS available?\n");
      }
    }

    if (scs->target == XA_FILE_NAME ||
      scs->target == XA_FILE) {
      /* load file */
      if (debug)
          fprintf(stderr,"xfer: => \"%s\"\n",cdata);
      new_file(cdata,1);
    }

    if (scs->target == _NETSCAPE_URL) {
      /* load file */
      if (NULL != (tmp = strchr(cdata,'\n')))
          *tmp = 0;
      if (NULL != (tmp = strchr(cdata,'\r')))
          *tmp = 0;
      if (debug)
          fprintf(stderr,"xfer: => \"%s\"\n",cdata);
      new_file(cdata,1);
    }

    if (scs->target == MIME_TEXT_URI_LIST) {
      /* load file(s) */
      for (file = strtok(cdata,"\r\n");
           NULL != file;
           file = strtok(NULL,"\r\n")) {
          if (debug)
            fprintf(stderr,"xfer: => \"%s\"\n",file);
          new_file(file,1);
      }
    }

    if (scs->target == XA_STRING) {
      /* might be a file name too, but don't complain if not */
      if (debug)
          fprintf(stderr,"xfer: => \"%s\"\n",cdata);
      new_file(cdata,0);
    }

    if (scs->target == MIME_IMAGE_PPM   ||
      scs->target == MIME_IMAGE_PGM   ||
      scs->target == MIME_IMAGE_JPEG  ||
      scs->target == MIME_IMAGE_GIF   ||
      scs->target == MIME_IMAGE_PNG   ||
      scs->target == MIME_IMAGE_TIFF  ||
      scs->target == MIME_IMAGE_XPM   ||
      scs->target == MIME_IMAGE_BMP) {
      /* xfer image data directly */
      char *filename = load_tmpfile("ida");
      int fd;
      fd = mkstemp(filename);
      write(fd,scs->value,scs->length);
      close(fd);
      if (0 == viewer_loadimage(ida,filename,0)) {
          ida->file = "selection";
          resize_shell();
      }
      unlink(filename);
      free(filename);
    }

    if (scs->target == XA_PIXMAP) {
      /* beaming pixmaps between apps */
      Screen *scr;
      Window root;
      Pixmap pix;
      int x,y,w,h,bw,depth;
      XImage *ximage;
      struct ida_image img;
      
      pix = ldata[0];
      if (debug)
          fprintf(stderr,"xfer: => id=0x%lx\n",pix);
      scr = XtScreen(widget);
      XGetGeometry(dpy,pix,&root,&x,&y,&w,&h,&bw,&depth);
      ximage = XGetImage(dpy,pix,0,0,w,h,-1,ZPixmap);
      parse_ximage(&img, ximage);
      XDestroyImage(ximage);
      viewer_setimage(ida,&img,"selection");
      resize_shell();
    }

    XFree(scs->value);
    if (1 == pending) {
      /* all done -- clean up */
      if (debug)
          fprintf(stderr,"xfer: all done\n");
      XmTransferDone(scs->transfer_id, XmTRANSFER_DONE_SUCCEED);
      XdndDropFinished(widget,scs);
    }
}

void
selection_dest(Widget  w, XtPointer ignore, XtPointer call_data)
{
    XmDestinationCallbackStruct *dcs = call_data;

    if (NULL != sel_find(_MOTIF_DROP)) {
      if (debug)
          fprintf(stderr,"dest: ignore self drop\n");
      XmTransferDone(dcs->transfer_id, XmTRANSFER_DONE_FAIL);
      return;
    }
    if (debug)
      fprintf(stderr,"dest: xfer id=%p\n",dcs->transfer_id);
    XmTransferValue(dcs->transfer_id, XA_TARGETS, selection_xfer,
                NULL, XtLastTimestampProcessed(dpy));
}

/* ---------------------------------------------------------------------- */

void ipc_ac(Widget widget, XEvent *event, String *argv, Cardinal *argc)
{
    struct    sel_data *sel;
    Widget    drag;
    Arg       args[4];
    Cardinal  n=0;

    if (0 == *argc)
      return;

    if (debug)
      fprintf(stderr,"ipc: %s\n",argv[0]);
    
    if (0 == strcmp(argv[0],"paste")) {
      XmeClipboardSink(ida->widget,XmCOPY,NULL);

    } else if (0 == strcmp(argv[0],"copy")) {
      XmeClipboardSource(ida->widget,XmCOPY,XtLastTimestampProcessed(dpy));

    } else if (0 == strcmp(argv[0],"drag")) {
      sel = sel_init(_MOTIF_DROP);
      iconify(widget,sel);
      XtSetArg(args[n], XmNdragOperations, XmDROP_COPY); n++;
      XtSetArg(args[n], XmNsourcePixmapIcon, sel->icon_widget); n++;
      drag = XmeDragSource(ida->widget, NULL, event, args, n);
      XtAddCallback(drag, XmNdragDropFinishCallback, dnd_done, NULL);
    }
}

void dnd_add(Widget widget)
{
    Arg       args[4];
    Cardinal  n=0;

    XtSetArg(args[n], XmNimportTargets, targets); n++;
    XtSetArg(args[n], XmNnumImportTargets, ntargets); n++;
    XmeDropSink(widget,args,n);
    XdndDropSink(widget);
}

void ipc_init()
{
    _MOTIF_EXPORT_TARGETS =
      XInternAtom(dpy, "_MOTIF_EXPORT_TARGETS", False);
    _MOTIF_CLIPBOARD_TARGETS =
      XInternAtom(dpy, "_MOTIF_CLIPBOARD_TARGETS", False);
    _MOTIF_DEFERRED_CLIPBOARD_TARGETS =
      XInternAtom(dpy, "_MOTIF_DEFERRED_CLIPBOARD_TARGETS", False);
    _MOTIF_SNAPSHOT =
      XInternAtom(dpy, "_MOTIF_SNAPSHOT", False);
    _MOTIF_DROP =
      XInternAtom(dpy, "_MOTIF_DROP", False);
    _MOTIF_LOSE_SELECTION =
      XInternAtom(dpy, "_MOTIF_LOSE_SELECTION", False);

    XA_TARGETS         = XInternAtom(dpy, "TARGETS",       False);
    XA_DONE            = XInternAtom(dpy, "DONE",          False);
    XA_FILE_NAME       = XInternAtom(dpy, "FILE_NAME",     False);
    XA_FILE            = XInternAtom(dpy, "FILE",          False);
    XA_FOREGROUND      = XInternAtom(dpy, "FOREGROUND",    False);
    XA_BACKGROUND      = XInternAtom(dpy, "BACKGROUND",    False);
    XA_PIXEL           = XInternAtom(dpy, "PIXEL",         False);
    _NETSCAPE_URL      = XInternAtom(dpy, "_NETSCAPE_URL", False);

    MIME_TEXT_URI_LIST = XInternAtom(dpy, "text/uri-list", False);
    MIME_IMAGE_PPM     = XInternAtom(dpy, "image/ppm",     False);
    MIME_IMAGE_PGM     = XInternAtom(dpy, "image/pgm",     False);
    MIME_IMAGE_XPM     = XInternAtom(dpy, "image/xpm",     False);
    MIME_IMAGE_BMP     = XInternAtom(dpy, "image/bmp",     False);
    MIME_IMAGE_JPEG    = XInternAtom(dpy, "image/jpeg",    False);
    MIME_IMAGE_GIF     = XInternAtom(dpy, "image/gif",     False);
    MIME_IMAGE_PNG     = XInternAtom(dpy, "image/png",     False);
    MIME_IMAGE_TIFF    = XInternAtom(dpy, "image/tiff",    False);

    targets[ntargets++] = XA_FILE_NAME;
    targets[ntargets++] = XA_FILE;
    targets[ntargets++] = _NETSCAPE_URL;
    targets[ntargets++] = MIME_TEXT_URI_LIST;
    targets[ntargets++] = MIME_IMAGE_PPM;
    targets[ntargets++] = MIME_IMAGE_PGM;
    targets[ntargets++] = MIME_IMAGE_XPM;
    targets[ntargets++] = MIME_IMAGE_BMP;
    targets[ntargets++] = MIME_IMAGE_JPEG;
#ifdef HAVE_LIBUNGIF
    targets[ntargets++] = MIME_IMAGE_GIF;
#endif
#ifdef HAVE_LIBPNG
    targets[ntargets++] = MIME_IMAGE_PNG;
#endif
#ifdef HAVE_LIBTIFF
    targets[ntargets++] = MIME_IMAGE_TIFF;
#endif
    targets[ntargets++] = XA_PIXMAP;
    targets[ntargets++] = XA_STRING;

    INIT_LIST_HEAD(&selections);
}

Generated by  Doxygen 1.6.0   Back to index