home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / xv310a / xvcut.c < prev    next >
Text File  |  1995-06-12  |  49KB  |  1,863 lines

  1. /*
  2.  * xvcut.c  -  xv cut-n-paste (and eventually drag-n-drop) related functions
  3.  *
  4.  * Contains:
  5.  *             int  CutAllowed();
  6.  *             int  PasteAllowed();
  7.  *             void DoImgCopy();
  8.  *             void DoImgCut();
  9.  *             void DoImgClear();
  10.  *             void DoImgPaste();
  11.  *
  12.  *      static byte *getSelection     ();
  13.  *      static byte *getFromClip      ();
  14.  *             void  SaveToClip       (data);
  15.  *      static void  clearSelectedArea();
  16.  *      static void  makeClipFName    ();
  17.  *      static int   countcols24      (byte *, int,int, int,int,int,int));
  18.  *      static int   countNewCols     (byte*, int, int, byte*, int, 
  19.  *                                     int, int, int, int);
  20.  *
  21.  *             void  InitSelection  ();
  22.  *             int   HaveSelection  ();
  23.  *             int   GetSelType     ();
  24.  *             void  GetSelRCoords  (&x, &y, &w, &h);
  25.  *             void  EnableSelection(onoff);
  26.  *             void  DrawSelection  (onoff);
  27.  *             int   DoSelection    (XButtonEvent *);
  28.  *      static int   dragHandle     (XButtonEvent *);
  29.  *      static void  dragSelection  (XButtonEvent *, u_int bmask, dndrop);
  30.  *      static void  rectSelection  (XButtonEvent *);
  31.  *             void  MoveGrowSelection(dx,dy,dw,dh);
  32.  *
  33.  *             void  BlinkSelection (cnt);
  34.  *             void  FlashSelection (cnt);
  35.  *
  36.  *      static void  makePasteSel    (data);
  37.  *
  38.  *             void  CropRect2Rect    (int*,int*,int*,int*, int,int,int,int);
  39.  *
  40.  * coordinate system transforms between pic, cpic, and epic coords
  41.  *             void  CoordE2C(ex, ey, &cx, &cy);
  42.  *             void  CoordC2E(cx, cy, &ex, &ey);
  43.  *             void  CoordP2C(px, py, &cx, &cy);
  44.  *             void  CoordC2P(cx, cy, &px, &py);
  45.  *             void  CoordE2P(ex, ey, &px, &py);
  46.  *             void  CoordP2E(px, py, &ex, &ey);
  47.  */
  48.  
  49.  
  50. #define  NEEDSDIR               /* for stat() */
  51. #include "copyright.h"
  52. #include "xv.h"
  53.  
  54. #include "bits/cut"
  55. #include "bits/cutm"
  56. #include "bits/copy"
  57. #include "bits/copym"
  58.  
  59. #define DBLCLKTIME 500
  60. #define CLIPPROP   "XV_CLIPBOARD"
  61.  
  62.  
  63.  
  64. /***
  65.  *** local functions
  66.  ***/
  67.  
  68. static void  doPaste           PARM((byte *));
  69. static void  buildCursors      PARM((void));
  70. static byte *getSelection      PARM((void));
  71. static byte *getFromClip       PARM((void));
  72. static void clearSelectedArea  PARM((void));
  73. static void makeClipFName      PARM((void));
  74. static int  countcols24        PARM((byte *, int, int, int, int, int, int));
  75. static int  countNewCols       PARM((byte *, int, int, byte *, int, 
  76.                      int, int, int, int));
  77. static int  dragHandle         PARM((XButtonEvent *));
  78. static void dragSelection      PARM((XButtonEvent *, u_int, int));
  79. static void rectSelection      PARM((XButtonEvent *));
  80. static void makePasteSel       PARM((byte *));
  81.  
  82. static void CoordE2Prnd        PARM((int, int, int *, int *));
  83.  
  84.  
  85. /***
  86.  *** local data
  87.  ***/
  88.  
  89. static char *clipfname = (char *) NULL;
  90. static char  clipfnstr[MAXPATHLEN];
  91.  
  92. static int   selrx, selry, selrw, selrh;   /* PIC coords of sel. rect */
  93. static int   haveSel;                          /* have a selection? */
  94. static int   selType;                          /* type of sel. (RECT, etc) */
  95. static int   selFilled;                        /* if true flash whole sel */
  96. static int   selColor;                         /* selection 'mask' 0-7 */
  97. static int   selTracking;                      /* in a 'tracking' loop */
  98.  
  99. static Cursor dragcurs = (Cursor) 0;
  100. static Cursor copycurs = (Cursor) 0;
  101. static Cursor cutcurs  = (Cursor) 0;
  102.  
  103.  
  104.  
  105.  
  106. /********************************************/
  107. int CutAllowed()
  108. {
  109.   /* returns '1' if cut/copy/clear commands should be enabled (ie, if
  110.      there's a selection of some sort */
  111.  
  112.   return (but[BCROP].active);
  113. }
  114.  
  115.  
  116. /********************************************/
  117. int PasteAllowed()
  118. {
  119.   Atom        clipAtom;
  120.   struct stat st;
  121.  
  122.   /* returns '1' if there's something on the clipboard to be pasted */
  123.  
  124.   /* also, can't paste if we're in a root mode */
  125.   if (useroot) return 0;
  126.  
  127.   /* see if there's a CLIPPROP associated with the root window,
  128.      if there is, we'll use that as the clipboard:  return '1' */
  129.  
  130.   clipAtom = XInternAtom(theDisp, CLIPPROP, True);
  131.   if (clipAtom != None) return 1;   /* clipboard property exists: can paste */
  132.  
  133.  
  134.   /* barring that, see if the CLIPFILE exists. if so, we can paste */
  135.   if (!clipfname) makeClipFName();
  136.   if (stat(clipfname, &st)==0) return 1;       /* file exists: can paste */
  137.  
  138.   return 0;
  139. }
  140.  
  141.  
  142. /********************************************/
  143. void DoImgCopy()
  144. {
  145.   /*
  146.    * called when 'Copy' command is issued.  does entire user interface,
  147.    * such as it is, and puts appropriate data onto the clipboard.
  148.    * Does all complaining on any errors
  149.    */
  150.  
  151.   byte *cimg;
  152.  
  153.   if (!HaveSelection())     return;
  154.   cimg = getSelection();
  155.   if (!cimg) return;
  156.  
  157.   SaveToClip(cimg);
  158.   free(cimg);
  159.  
  160.   FlashSelection(2);
  161.   SetCursors(-1);
  162. }
  163.  
  164.  
  165.  
  166. /********************************************/
  167. void DoImgCut()
  168. {
  169.   /*
  170.    * called when 'Cut' command is issued.  does entire user interface,
  171.    * such as it is, and puts appropriate data onto the clipboard.
  172.    * Does all complaining on any errors
  173.    */
  174.  
  175.   byte *cimg;
  176.  
  177.   if (!HaveSelection()) return;
  178.   FlashSelection(2);
  179.  
  180.   cimg = getSelection();
  181.   if (!cimg) return;
  182.  
  183.   SaveToClip(cimg);
  184.   free(cimg);
  185.  
  186.   clearSelectedArea();
  187.   SetCursors(-1);
  188. }
  189.  
  190.  
  191.  
  192. /********************************************/
  193. void DoImgClear()
  194. {
  195.   /* called when 'Clear' command is issued */
  196.  
  197.   if (!HaveSelection()) return;
  198.   FlashSelection(2);
  199.   clearSelectedArea();
  200.   SetCursors(-1);
  201. }
  202.  
  203.  
  204.  
  205. /********************************************/
  206. void DoImgPaste()
  207. {
  208.   byte *cimg;
  209.  
  210.   if (!PasteAllowed()) { XBell(theDisp, 0);  return; }
  211.  
  212.   cimg = getFromClip();
  213.   if (!cimg) return;  
  214.  
  215.   /* if there's no selection, make one! */
  216.   if (!HaveSelection()) makePasteSel(cimg);
  217.                    else doPaste(cimg);
  218.  
  219.   free(cimg);
  220.   SetCursors(-1);
  221. }
  222.  
  223.  
  224. /********************************************/
  225. static void doPaste(cimg)
  226.      byte *cimg;
  227. {
  228.   /*
  229.    * This is fairly hairy.
  230.    */
  231.  
  232.   byte *dp, *dpic, *clippic, *clipcmap;
  233.   int   clipw, cliph, clipis24, len, istran, trval;
  234.   int   i, j, sx,sy,sw,sh, cx,cy,cw,ch, dx,dy,dw,dh,dx2,dy2;
  235.  
  236.  
  237.   /*
  238.    * verify clipboard data
  239.    */
  240.  
  241.   if (!cimg) return;
  242.  
  243.   len = ((int)  cimg[CIMG_LEN + 0])      |
  244.         ((int) (cimg[CIMG_LEN + 1]<<8))  |
  245.     ((int) (cimg[CIMG_LEN + 2]<<16)) |
  246.     ((int) (cimg[CIMG_LEN + 3]<<24));
  247.  
  248.   if (len < CIMG_PIC24) return;        
  249.  
  250.   istran    = cimg[CIMG_TRANS];
  251.   trval     = cimg[CIMG_TRVAL];
  252.   clipis24  = cimg[CIMG_24];
  253.   clipw     = cimg[CIMG_W] | ((int) (cimg[CIMG_W+1]<<8));
  254.   cliph     = cimg[CIMG_H] | ((int) (cimg[CIMG_H+1]<<8));
  255.   if (clipw<1 || cliph<1) return;
  256.  
  257.   if (( clipis24 && len != CIMG_PIC24 + clipw * cliph * 3) ||
  258.       (!clipis24 && len != CIMG_PIC8  + clipw * cliph)        ) {
  259.     ErrPopUp("The clipboard data is not in a recognized format!", "\nMoof!");
  260.     goto exit;
  261.   }
  262.  
  263.   clippic  = cimg + ((clipis24) ? CIMG_PIC24 : CIMG_PIC8);
  264.   clipcmap = cimg + CIMG_CMAP;
  265.  
  266.  
  267.   /* determine if we're going to promote 'pic' to 24-bits (if it isn't
  268.    * already, because if we *are*, we'd prefer to do any clipboard rescaling
  269.    * in 24-bit space for the obvious reasons.
  270.    *
  271.    * possibilities:  
  272.    *   PIC24  -  easy, do clipboard rescale in 24-bit space
  273.    *   PIC8, and clipboard is 8 bits, (or 24-bits, but with <=256 colors)
  274.    *      and total unique colors < 256:
  275.    *         do 8-bit rescale & paste, don't ask anything
  276.    *   PIC8, and clipboard is 24 bits, or 8-bits but total # of cols >256:
  277.    *         *ask* if they want to promote pic.  if so, do it, otherwise
  278.    *         do clipboard rescale in 8-bits, and do the best we can...
  279.    */
  280.  
  281.  
  282.   GetSelRCoords(&sx, &sy, &sw, &sh);  /* sel rect in pic coords */
  283.  
  284.   /* dx,dy,dw,dh is the rectangle (in PIC coords) where the paste will occur
  285.      (cropped to be entirely within PIC */
  286.   
  287.   dx = sx;  dy = sy;  dw = sw;  dh = sh;
  288.   CropRect2Rect(&dx, &dy, &dw, &dh, 0, 0, pWIDE, pHIGH);
  289.  
  290.  
  291.   /* cx,cy,cw,ch is the rectangle of the clipboard data (in clipboard coords)
  292.      that will actually be used in the paste operation */
  293.  
  294.   cx = (sx>=0) ? 0 : ((-sx) * clipw) / sw;  
  295.   cy = (sy>=0) ? 0 : ((-sy) * cliph) / sh;
  296.   cw = (dw * clipw) / sw;
  297.   ch = (dh * cliph) / sh;
  298.  
  299.  
  300.   /* see if we have to ask any questions */
  301.  
  302.   if (picType == PIC8) {
  303.     int ncc, keep8;
  304.     char buf[512];
  305.     
  306.     if (clipis24) { /* pasting in a 24-bit image that *requires* promotion */
  307.       static char *bnames[] = { "\nOkay", "\033Cancel" };
  308.       strcpy(buf, "Warning:  Pasting this 24-bit image will require ");
  309.       strcat(buf, "promoting the current image to 24 bits.");
  310.       
  311.       if (PopUp(buf, bnames, 2)) goto exit;   /* Cancelled */
  312.       else Change824Mode(PIC24);              /* promote pic to 24 bits */
  313.     }
  314.  
  315.     else {   /* clip is 8 bits */
  316.       ncc = countNewCols(clippic,clipw,cliph,clipcmap,clipis24,cx,cy,cw,ch);
  317.       
  318.       if (ncc + numcols > 256) {
  319.     static char *bnames[] = { "\nPromote", "8Keep 8-bit", "\033Cancel" };
  320.     strcpy(buf,"Warning:  The image and the clipboard combine to have ");
  321.     strcat(buf,"more than 256 unique colors.  Promoting the ");
  322.     strcat(buf,"image to 24 bits is recommended, otherwise the contents ");
  323.     strcat(buf,"of the clipboard will probably lose some colors.");
  324.     
  325.     keep8 = PopUp(buf, bnames, 3);
  326.     if      (keep8==2) goto exit;              /* Cancel */
  327.     else if (keep8==0) Change824Mode(PIC24);   /* promote pic to 24 bits */
  328.       }
  329.     }
  330.   }
  331.  
  332.  
  333.  
  334.   
  335.   
  336.   /* legal possibilities at this point:
  337.    *   pic is PIC24:  clip is 8 or 24
  338.    *   pic is PIC8:   clip is 8, or clip is 24 but has 256 or fewer colors
  339.    */
  340.  
  341.  
  342.  
  343.   if (picType == PIC8) {
  344.     int   clx, cly, r,g,b,k,mind,close,newcols;
  345.     byte *cp, *clp, *pp, *ccp, newr[256], newg[256], newb[256], remap[256];
  346.     byte  order[256], trans[256];
  347.     int   bperpix, dpncols;
  348.     
  349.     dpic = (byte *) malloc((size_t) dw * dh);
  350.     if (!dpic) FatalError("Out of memory in DoImgPaste()\n");
  351.     
  352.     bperpix = (clipis24) ? 3 : 1;
  353.     newcols = 0;
  354.     
  355.     /* dpic = a scaled, 8-bit representation of clippic[cx,cy,cw,ch] */
  356.     
  357.     if (!clipis24) {   /* copy colormap from clip data into newr,g,b[] */
  358.       for (i=0; i<256; i++) {
  359.     newr[i] = clipcmap[i*3];
  360.     newg[i] = clipcmap[i*3 + 1];
  361.     newb[i] = clipcmap[i*3 + 2];
  362.       }
  363.     }
  364.  
  365.     for (i=0; i<dh; i++) {                     /* un-smooth 8-bit resize */
  366.       dp = dpic + i*dw;
  367.       cly = cy + (i * ch) / dh;
  368.       clp = clippic + (cly*clipw * bperpix);
  369.       
  370.       for (j=0; j<dw; j++, dp++) {
  371.     /* get appropriate pixel from clippic */
  372.     clx = cx + (j * cw) / dw;
  373.     cp = clp + (clx * bperpix);
  374.     
  375.     if (!clipis24) *dp = *cp;
  376.     else {                            /* build colormap as we go... */
  377.       r = *cp++;  g = *cp++;  b = *cp++;
  378.       
  379.       /* look it up in new colormap, add if not there */
  380.       for (k=0; k<newcols && (r!=newr[k] || g!=newg[k] ||b!=newb[k]); k++);
  381.       if (k==newcols && k<256) {
  382.         newr[k]=r;  newg[k]=g;  newb[k]=b;  newcols++;
  383.       }
  384.       
  385.       *dp = (byte) (k & 0xff);
  386.     }
  387.       }
  388.     }
  389.  
  390.  
  391.     SortColormap(dpic, dw, dh, &dpncols, newr,newg,newb, order, trans);
  392.     for (i=0, dp=dpic; i<dw*dh; i++, dp++) *dp = trans[*dp];
  393.  
  394.     if (istran) {
  395.       if (!clipis24) trval = trans[trval];
  396.       else {
  397.     for (i=0; i<dpncols; i++) {
  398.       if (cimg[CIMG_TRR] == newr[i] &&
  399.           cimg[CIMG_TRG] == newg[i] &&
  400.           cimg[CIMG_TRB] == newb[i]) { trval = i;  break; }
  401.     }
  402.       }
  403.     }
  404.     
  405.     
  406.     
  407.     /* COLORMAP MERGING */
  408.     
  409.     newcols = 0;
  410.     
  411.     for (i=0; i<dpncols; i++) {
  412.       if (istran && i==trval) continue;
  413.       
  414.       for (j=0; j<numcols; j++) {              /* look for an exact match */
  415.     if (rMap[j]==newr[i] && gMap[j]==newg[i] && bMap[j]==newb[i]) break;
  416.       }
  417.       if (j<numcols) remap[i] = j;
  418.       else {                                   /* no exact match */
  419.     newcols++;
  420.     
  421.     if (numcols < 256) {
  422.       rMap[numcols] = newr[i];
  423.       gMap[numcols] = newg[i];
  424.       bMap[numcols] = newb[i];
  425.       remap[i] = numcols;
  426.       numcols++;
  427.     }
  428.     else {  /* map to closest in image colormap */
  429.       r = newr[i];  g=newg[i];  b=newb[i];
  430.       mind = 256*256 + 256*256 + 256*256;
  431.       for (j=close=0; j<numcols; j++) {
  432.         k = ((rMap[j]-r) * (rMap[j]-r)) + 
  433.           ((gMap[j]-g) * (gMap[j]-g)) +
  434.         ((bMap[j]-b) * (bMap[j]-b));
  435.         if (k<mind) { mind = k;  close = j; }
  436.       }
  437.       remap[i] = (byte) close;
  438.     }
  439.       }
  440.     }
  441.     
  442.     
  443.     /* copy the data into PIC */
  444.     
  445.     dp = dpic;
  446.     for (i=dy; i<dy+dh; i++) {
  447.       pp = pic + (i*pWIDE) + dx;
  448.       for (j=dx; j<dx+dw; j++) {
  449.     if (istran && *dp==trval) { pp++;  dp++; }
  450.                          else { *pp++ = remap[*dp++]; }
  451.       }
  452.     }
  453.     free(dpic);
  454.     
  455.     if (newcols) InstallNewPic();      /* does color reallocation, etc. */
  456.     else {
  457.       GenerateCpic();
  458.       GenerateEpic(eWIDE, eHIGH);
  459.       DrawEpic();
  460.     }
  461.   }
  462.   
  463.  
  464.   /******************** PIC24 handling **********************/
  465.   
  466.   
  467.   else {
  468.     byte *tmppic, *cp, *pp, *clp;
  469.     int   bperpix;
  470.     int   trr, trg, trb, clx, cly;
  471.     
  472.     trr = trg = trb = 0;
  473.     if (istran) {
  474.       if (clipis24) {
  475.     trr = cimg[CIMG_TRR];
  476.     trg = cimg[CIMG_TRG];
  477.     trb = cimg[CIMG_TRB];
  478.       }
  479.       else {
  480.     trr = clipcmap[trval*3];
  481.     trg = clipcmap[trval*3+1];
  482.     trb = clipcmap[trval*3+2];
  483.       }
  484.     }
  485.     
  486.     bperpix = (clipis24) ? 3 : 1;
  487.  
  488.     if (!istran && (cw != dw || ch != dh)) {  /* need to resize, can smooth */
  489.       byte rmap[256], gmap[256], bmap[256];
  490.       
  491.       tmppic = (byte *) malloc((size_t) cw * ch * bperpix);
  492.       if (!tmppic) FatalError("Out of memory in DoImgPaste()\n");
  493.       
  494.       /* copy relevant hunk of clippic into tmppic (Smooth24 only works on 
  495.      complete images */
  496.       
  497.       for (i=0; i<ch; i++) {
  498.     dp = tmppic + i*cw*bperpix;
  499.     cp = clippic + ((i+cy)*clipw + cx) * bperpix;
  500.     for (j=0; j<cw*bperpix; j++) *dp++ = *cp++;
  501.       }
  502.       
  503.       if (!clipis24) {
  504.     for (i=0; i<256; i++) {
  505.       rmap[i] = clipcmap[i*3];
  506.       gmap[i] = clipcmap[i*3+1];
  507.       bmap[i] = clipcmap[i*3+2];
  508.     }
  509.       }
  510.       
  511.       dpic = Smooth24(tmppic, clipis24, cw,ch, dw,dh, rmap,gmap,bmap);
  512.       if (!dpic) FatalError("Out of memory (2) in DoImgPaste()\n");
  513.       free(tmppic);
  514.       
  515.       /* copy the resized, smoothed, 24-bit data into 'pic' */
  516.       
  517.       /* XXX: (deal with smooth-resized transparent imgs) */
  518.       
  519.       dp = dpic;
  520.       for (i=dy; i<dy+dh; i++) {
  521.     pp = pic + (i*pWIDE + dx) * 3;
  522.     for (j=0; j<dw; j++) {
  523.       if (istran && dp[0]==trr && dp[1]==trg && dp[2]==trb) {
  524.         pp +=3;  dp += 3;
  525.       } else {
  526.         *pp++ = *dp++;  *pp++ = *dp++;  *pp++ = *dp++;
  527.       }
  528.     }
  529.       }
  530.       free(dpic);
  531.     }
  532.  
  533.  
  534.     else {   /* can't do smooth resize.  Do non-smooth resize (if any!) */
  535.       for (i=0; i<dh; i++) {
  536.     pp = pic + ((i+dy)*pWIDE + dx) * 3;
  537.     cly = cy + (i * ch) / dh;
  538.     clp = clippic + (cly*clipw * bperpix);
  539.     
  540.     for (j=0; j<dw; j++, pp+=3) {
  541.       clx = cx + (j * cw) / dw;
  542.       cp = clp + (clx * bperpix);
  543.       
  544.       if (clipis24) {
  545.         if (!istran || cp[0]!=trr || cp[1]!=trg || cp[2]==trb) {
  546.           pp[0] = *cp++;  pp[1] = *cp++;  pp[2] = *cp++;
  547.         }
  548.       }
  549.       else {   /* clip is 8 bit */
  550.         if (!istran || *cp != trval) {
  551.           pp[0]  = clipcmap[*cp * 3];
  552.           pp[1]  = clipcmap[*cp * 3 + 1];
  553.           pp[2]  = clipcmap[*cp * 3 + 2];
  554.         }
  555.       }
  556.     }
  557.       }
  558.     }
  559.  
  560.     
  561.     GenerateCpic();
  562.     GenerateEpic(eWIDE, eHIGH);
  563.     DrawEpic();
  564.   }
  565.   
  566.     
  567.  exit:  
  568.   SetCursors(-1);
  569. }
  570.  
  571.  
  572.  
  573. /********************************************/
  574. static void buildCursors()
  575. {
  576.   Pixmap p1,p2,p3,p4;
  577.   XColor cfg, cbg;
  578.  
  579.   dragcurs = XCreateFontCursor(theDisp, XC_fleur);
  580.   p1 = XCreatePixmapFromBitmapData(theDisp, rootW, (char *) cut_bits, 
  581.                    cut_width,  cut_height, 1L, 0L, 1);
  582.   p2 = XCreatePixmapFromBitmapData(theDisp, rootW, (char *) cutm_bits, 
  583.                    cutm_width, cutm_height, 1L, 0L, 1);
  584.   p3 = XCreatePixmapFromBitmapData(theDisp, rootW, (char *) copy_bits, 
  585.                    copy_width,  copy_height, 1L, 0L, 1);
  586.   p4 = XCreatePixmapFromBitmapData(theDisp, rootW, (char *) copym_bits, 
  587.                    copym_width, copym_height, 1L, 0L, 1);
  588.   if (p1 && p2 && p3 && p4) {
  589.     cfg.red = cfg.green = cfg.blue = 0;
  590.     cbg.red = cbg.green = cbg.blue = 0xffff;
  591.     cutcurs = XCreatePixmapCursor(theDisp, p1,p2, &cfg, &cbg, 
  592.                   cut_x_hot, cut_y_hot);
  593.     copycurs = XCreatePixmapCursor(theDisp, p3,p4, &cfg, &cbg, 
  594.                   copy_x_hot, copy_y_hot);
  595.     if (!cutcurs || !copycurs) FatalError("can't create cut/copy cursors...");
  596.   }
  597.  
  598.   if (p1) XFreePixmap(theDisp, p1);
  599.   if (p2) XFreePixmap(theDisp, p2);
  600.   if (p3) XFreePixmap(theDisp, p3);
  601.   if (p4) XFreePixmap(theDisp, p4);
  602.  
  603. }
  604.  
  605.  
  606. /********************************************/
  607. static byte *getSelection()
  608. {
  609.   /* alloc's and builds image with values based on currently selected
  610.    * portion of the image.  Returns NULL on failure
  611.    *
  612.    * also note: getSelection will always fill trans,r,g,b with 0, for now
  613.    * as you can't 'select' transparent regions.  Other code (TextPaste()),
  614.    * *can* generate semi-transparent objects to be pasted
  615.    */
  616.  
  617.   byte *pp, *dp, *cimg;
  618.   int   i, j, k, x, y, w, h, do24, len;
  619.  
  620.   if (!CutAllowed()) {  XBell(theDisp, 0);  return (byte *) NULL; }
  621.   if (!HaveSelection()) return (byte *) NULL;
  622.   
  623.   GetSelRCoords(&x,&y,&w,&h);
  624.   CropRect2Rect(&x,&y,&w,&h, 0,0,pWIDE,pHIGH);
  625.  
  626.   /* make selection be entirely within image */
  627.   EnableSelection(0);
  628.   selrx = x;  selry = y;  selrw = w;  selrh = h;
  629.   EnableSelection(1);
  630.  
  631.  
  632.   if (picType == PIC24 && countcols24(pic,pWIDE,pHIGH, x,y,w,h)>256) {
  633.     do24=1;
  634.     len = CIMG_PIC24 + w*h*3;
  635.   }
  636.   else {
  637.     do24=0;
  638.     len = CIMG_PIC8 + w*h;
  639.   }
  640.  
  641.   cimg = (byte *) malloc((size_t) len);
  642.   if (!cimg) {
  643.     ErrPopUp("Unable to malloc() temporary space for the selection.",
  644.          "\nByte Me!");
  645.     return (byte *) NULL;
  646.   }
  647.  
  648.   cimg[CIMG_LEN  ] =  len      & 0xff;
  649.   cimg[CIMG_LEN+1] = (len>> 8) & 0xff;
  650.   cimg[CIMG_LEN+2] = (len>>16) & 0xff;
  651.   cimg[CIMG_LEN+3] = (len>>24) & 0xff;
  652.  
  653.   cimg[CIMG_W  ] =  w     & 0xff;
  654.   cimg[CIMG_W+1] = (w>>8) & 0xff;
  655.  
  656.   cimg[CIMG_H  ] =  h     & 0xff;
  657.   cimg[CIMG_H+1] = (h>>8) & 0xff;
  658.  
  659.   cimg[CIMG_24]    = do24;
  660.   cimg[CIMG_TRANS] = 0;
  661.  
  662.  
  663.   if (picType == PIC24 && !do24) {                  /* 24-bit data as 8-bit */
  664.     int nc,pr,pg,pb;
  665.     byte *cm;
  666.     
  667.     nc = 0;
  668.     dp = cimg + CIMG_PIC8;
  669.     
  670.     for (i=y; i<y+h; i++) {
  671.       pp = pic + i*pWIDE*3 + x*3;
  672.       for (j=x; j<x+w; j++, pp+=3) {
  673.     pr = pp[0];  pg = pp[1];  pb = pp[2];
  674.     
  675.     cm = cimg + CIMG_CMAP;
  676.     for (k=0; k<nc; k++,cm+=3) {
  677.       if (pr==cm[0] && pg==cm[1] && pb==cm[2]) break;
  678.     }
  679.     if (k==nc) {
  680.       nc++;
  681.       cimg[CIMG_CMAP + nc*3    ] = pr;
  682.       cimg[CIMG_CMAP + nc*3 + 1] = pg;
  683.       cimg[CIMG_CMAP + nc*3 + 2] = pb;
  684.     }
  685.     
  686.     *dp++ = (byte) k;
  687.       }
  688.     }
  689.   }
  690.   
  691.  
  692.   else if (picType == PIC24) {                     /* 24-bit data as 24-bit */
  693.     dp = cimg + CIMG_PIC24;
  694.     for (i=y; i<y+h; i++) {
  695.       pp = pic + i*pWIDE*3 + x*3;
  696.       for (j=x; j<x+w; j++) {
  697.     *dp++ = *pp++;
  698.     *dp++ = *pp++;
  699.     *dp++ = *pp++;
  700.       }
  701.     }
  702.   }
  703.  
  704.  
  705.   else if (picType == PIC8) {                       /* 8-bit selection */
  706.     byte *cm = cimg + CIMG_CMAP;
  707.     for (i=0; i<256; i++) {                         /* copy colormap */
  708.       if (i<numcols) { 
  709.     *cm++ = rMap[i];
  710.     *cm++ = gMap[i];
  711.     *cm++ = bMap[i];
  712.       }
  713.     }
  714.     
  715.     dp = cimg + CIMG_PIC8;
  716.     for (i=y; i<y+h; i++) {                         /* copy image */
  717.       pp = pic + i*pWIDE + x;
  718.       for (j=x; j<x+w; j++) *dp++ = *pp++;
  719.     }
  720.   }
  721.     
  722.   return cimg;
  723. }
  724.  
  725.  
  726.  
  727.   
  728. /********************************************/
  729. static byte *getFromClip()
  730. {
  731.   /* gets whatever data is on the clipboard, in CIMG_* format */
  732.  
  733.   Atom          clipAtom, actType;
  734.   int           i, actFormat, len;
  735.   unsigned long nitems, nleft;
  736.   byte         *data, *data1, lbuf[4];
  737.  
  738.   FILE *fp;
  739.   char  str[512];
  740.  
  741.  
  742.   if (forceClipFile) {                           /* remove property, if any */
  743.     clipAtom = XInternAtom(theDisp, CLIPPROP, True);
  744.     if (clipAtom != None) XDeleteProperty(theDisp, rootW, clipAtom);
  745.   }
  746.   
  747.   
  748.   clipAtom = XInternAtom(theDisp, CLIPPROP, True);             /* find prop */
  749.   if (clipAtom != None) {
  750.  
  751.     /* try to retrieve the length of the data in the property */
  752.     i = XGetWindowProperty(theDisp, rootW, clipAtom, 0L, 1L, False, XA_STRING, 
  753.                &actType, &actFormat, &nitems, &nleft, 
  754.                (unsigned char **) &data);
  755.  
  756.     if (i==Success && actType==XA_STRING && actFormat==8 && nleft>0) {
  757.       /* got some useful data */
  758.       len  = data[0];
  759.       len |= ((int) data[1])<<8;
  760.       len |= ((int) data[2])<<16;
  761.       len |= ((int) data[3])<<24;
  762.  
  763.       XFree((void *) data);
  764.  
  765.       /* read the rest of the data (len bytes) */
  766.       i = XGetWindowProperty(theDisp, rootW, clipAtom, 1L, 
  767.                  (long) ((len-4)+3)/4, 
  768.                  False, XA_STRING, &actType, &actFormat, &nitems, 
  769.                  &nleft, (unsigned char **) &data);
  770.  
  771.       if (i==Success) {
  772.     /* copy data into regular 'malloc'd space, so we won't have to use
  773.        XFree() to get rid of it in calling procs */
  774.  
  775.     data1 = (byte *) malloc((size_t) len);
  776.     if (!data1) {
  777.       XFree((void *) data);
  778.       ErrPopUp("Insufficient memory to retrieve clipboard!", "\nShucks!");
  779.       return (byte *) NULL;
  780.     }
  781.  
  782.     data1[0] =  len      & 0xff;
  783.     data1[1] = (len>> 8) & 0xff;
  784.     data1[2] = (len>>16) & 0xff;
  785.     data1[3] = (len>>24) & 0xff;
  786.     xvbcopy((char *) data, (char *) data1+4, (size_t) len-4);
  787.  
  788.     XFree((void *) data);
  789.     return data1;
  790.       }
  791.     }
  792.   }
  793.  
  794.   
  795.   /* if we're still here, then the prop method was less than successful. 
  796.      use the file method, instead */
  797.  
  798.   if (!clipfname) makeClipFName();
  799.  
  800.   fp = fopen(clipfname, "r");
  801.   if (!fp) {
  802.     unlink(clipfname);
  803.     sprintf(str, "Can't read clipboard file '%s'\n\n  %s.", 
  804.         clipfname, ERRSTR(errno));
  805.     ErrPopUp(str,"\nBletch!");
  806.     return (byte *) NULL;
  807.   }
  808.  
  809.   /* get data length */
  810.   if (fread((char *) lbuf, (size_t) 1, (size_t) 4, fp) != 4) {
  811.     fclose(fp);
  812.     unlink(clipfname);
  813.     sprintf(str, "Error occurred while reading clipboard file.\n\n  %s.",
  814.         ERRSTR(errno));
  815.     ErrPopUp(str,"\nGlork!");
  816.     return (byte *) NULL;
  817.   }
  818.  
  819.   len  = lbuf[0];
  820.   len |= ((int) lbuf[1])<<8;
  821.   len |= ((int) lbuf[2])<<16;
  822.   len |= ((int) lbuf[3])<<24;
  823.  
  824.   data = (byte *) malloc((size_t) len);
  825.   if (!data) {
  826.     ErrPopUp("Insufficient memory to retrieve clipboard!", "\nShucks!");
  827.     return (byte *) NULL;
  828.   }
  829.  
  830.  
  831.   data[0] =  len      & 0xff;
  832.   data[1] = (len>> 8) & 0xff;
  833.   data[2] = (len>>16) & 0xff;
  834.   data[3] = (len>>24) & 0xff;
  835.  
  836.   /* get data */
  837.   if (fread((char *) data+4, (size_t) 1, (size_t) len-4, fp) != len-4) {
  838.     fclose(fp);
  839.     free(data);
  840.     unlink(clipfname);
  841.     sprintf(str, "Error occurred while reading clipboard file.\n\n  %s.",
  842.         ERRSTR(errno));
  843.     ErrPopUp(str,"\nNertz!");
  844.     return (byte *) NULL;
  845.   }
  846.  
  847.   fclose(fp);
  848.  
  849.   return data;
  850. }
  851.  
  852.  
  853.  
  854. /********************************************/
  855. void SaveToClip(cimg)
  856.      byte *cimg;
  857. {
  858.   /* takes the 'thing' pointed to by data and sticks it on the clipboard.
  859.      always tries to use the property method.  If it gets a BadAlloc
  860.      error (the X server ran out of memory (ie, probably an X terminal)),
  861.      it deletes the property, and falls back to using the file method */
  862.  
  863.   Atom  clipAtom;
  864.   FILE *fp;
  865.   char  str[512];
  866.   int   len;
  867.  
  868.   if (!cimg) return;
  869.  
  870.   len = ((int)  cimg[CIMG_LEN + 0])      |
  871.         ((int) (cimg[CIMG_LEN + 1]<<8))  |
  872.     ((int) (cimg[CIMG_LEN + 2]<<16)) |
  873.     ((int) (cimg[CIMG_LEN + 3]<<24));
  874.  
  875.  
  876.   if (forceClipFile) {                           /* remove property, if any */
  877.     clipAtom = XInternAtom(theDisp, CLIPPROP, True);
  878.     if (clipAtom != None) XDeleteProperty(theDisp, rootW, clipAtom);
  879.   }
  880.   
  881.   
  882.   if (!forceClipFile) {
  883.     clipAtom = XInternAtom(theDisp, CLIPPROP, False);  /* find or make prop */
  884.     if (clipAtom != None) {
  885.       /* try to store the data in the property */
  886.       
  887.       xerrcode = 0;
  888.       XChangeProperty(theDisp, rootW, clipAtom, XA_STRING, 8, PropModeReplace,
  889.               cimg, len);
  890.       XSync(theDisp, False);                         /* make it happen *now* */
  891.       if (!xerrcode) return;                         /* success! */
  892.       
  893.       /* failed, use file method */
  894.       XDeleteProperty(theDisp, rootW, clipAtom);
  895.     }
  896.   }
  897.  
  898.  
  899.   /* if we're still here, try the file method */
  900.  
  901.   if (!clipfname) makeClipFName();
  902.  
  903.   fp = fopen(clipfname, "w");
  904.   if (!fp) {
  905.     unlink(clipfname);
  906.     sprintf(str, "Can't write clipboard file '%s'\n\n  %s.", 
  907.         clipfname, ERRSTR(errno));
  908.     ErrPopUp(str,"\nBletch!");
  909.     return;
  910.   }
  911.  
  912.   if (fwrite((char *) cimg, (size_t) 1, (size_t) len, fp) != len) {
  913.     fclose(fp);
  914.     unlink(clipfname);
  915.     sprintf(str, "Error occurred while writing to clipboard file.\n\n  %s.",
  916.         ERRSTR(errno));
  917.     ErrPopUp(str,"\nGlork!");
  918.     return;
  919.   }
  920.  
  921.   fclose(fp);
  922. }
  923.  
  924.  
  925.  
  926. /********************************************/
  927. static void clearSelectedArea()
  928. {
  929.   /* called by 'Cut' or 'Clear' functions.  fills the selected area of the
  930.      image with either clearR,clearG,clearB (in PIC24 mode), or editColor
  931.      (in PIC8 mode).  Regens and redraws the image */
  932.  
  933.   int   i,j,x,y,w,h;
  934.   byte *pp;
  935.  
  936.   if (!HaveSelection()) return;
  937.   GetSelRCoords(&x,&y,&w,&h);
  938.   CropRect2Rect(&x,&y,&w,&h, 0,0,pWIDE,pHIGH);
  939.  
  940.   if (picType == PIC24) {
  941.     for (i=y; i<y+h && i<pHIGH; i++) {
  942.       pp = pic + i*pWIDE*3 + x*3;
  943.       for (j=x; j<x+w && j<pWIDE; j++) {
  944.     *pp++ = clearR;
  945.     *pp++ = clearG;
  946.     *pp++ = clearB;
  947.       }
  948.     }
  949.   }
  950.  
  951.   else {  /* PIC8 */
  952.     for (i=y; i<y+h && i<pHIGH; i++) {
  953.       pp = pic + i*pWIDE + x;
  954.       for (j=x; j<x+w && j<pWIDE; j++) *pp++ = editColor;
  955.     }
  956.   }
  957.  
  958.   GenerateCpic();
  959.   GenerateEpic(eWIDE,eHIGH);
  960.   DrawEpic();        /* redraws selection, also */
  961. }
  962.  
  963.  
  964. /********************************************/
  965. static void makeClipFName()
  966. {
  967.   char *homedir;
  968.  
  969.   if (clipfname) return;
  970.  
  971. #ifdef VMS
  972.   sprintf(clipfnstr, "SYS$LOGIN:%s", CLIPFILE);
  973. #else
  974.   homedir = (char *) getenv("HOME");
  975.   if (!homedir) homedir = ".";
  976.   sprintf(clipfnstr, "%s/%s", homedir, CLIPFILE);
  977. #endif
  978.  
  979.   clipfname = clipfnstr;
  980. }
  981.  
  982.  
  983.  
  984.  
  985.  
  986. /********************************************/
  987. static int countcols24(pic, pwide, phigh, x, y, w, h)
  988.      byte *pic;
  989.      int   pwide, phigh, x,y,w,h;
  990. {
  991.   /* counts the # of unique colors in a selected rect of a 24-bit image
  992.      returns '0-256' or >256 */
  993.  
  994.   int   i, j, k, nc;
  995.   u_int rgb[257], col;
  996.   byte *pp;
  997.  
  998.   nc = 0;
  999.   
  1000.   for (i=y; nc<257 && i<y+h; i++) {
  1001.     pp = pic + i*pwide*3 + x*3;
  1002.     for (j=x; nc<257 && j<x+w; j++, pp+=3) {
  1003.       col = (((u_int) pp[0])<<16) + (((u_int) pp[1])<<8) + pp[2];
  1004.  
  1005.       for (k=0; k<nc && col != rgb[k]; k++);
  1006.       if (k==nc) rgb[nc++] = col;  /* not found, add it */
  1007.     }
  1008.   }
  1009.  
  1010.   return nc;
  1011. }
  1012.  
  1013.  
  1014. /********************************************/
  1015. static int countNewCols(newpic, w,h, newcmap, is24, cx,cy,cw,ch)
  1016.      byte *newpic, *newcmap;
  1017.      int   w,h, is24, cx,cy,cw,ch;
  1018. {
  1019.   /* computes the number of NEW colors in the specified region of the
  1020.    * new pic, with respect to 'pic'.  returns 0-257 (where 257 means
  1021.    * 'some unknown # greater than 256')
  1022.    */
  1023.  
  1024.   int   i, j, k, nc, r,g,b;
  1025.   byte *pp, *cp;
  1026.   byte  newr[257], newg[257], newb[257];
  1027.  
  1028.   if (picType != PIC8) return 0;           /* shouldn't happen */
  1029.  
  1030.   nc = 0;    /* # of new colors */
  1031.  
  1032.   if (is24) {
  1033.     for (i=cy; i<cy+ch; i++) {
  1034.       pp = newpic + i*w*3 + cx*3;
  1035.       for (j=cx; j<cx+cw; j++) {
  1036.     r = *pp++;  g = *pp++;  b = *pp++;
  1037.     
  1038.     /* lookup r,g,b in 'pic's colormap and the newcolors colormap */
  1039.     for (k=0; k<nc && (r!=newr[k] || g!=newg[k] || b!=newb[k]); k++);
  1040.     if (k==nc) {
  1041.       for (k=0; k<numcols && (r!=rMap[k] || g!=gMap[k] || b!=bMap[k]);k++);
  1042.       if (k==numcols) {  /* it's a new color, alright */
  1043.         newr[nc] = r;  newg[nc] = g;  newb[nc] = b;
  1044.         nc++;
  1045.         if (nc==257) return nc;
  1046.       }
  1047.     }
  1048.       }
  1049.     }
  1050.   }
  1051.  
  1052.   else {      /* newpic is an 8-bit pic */
  1053.     int coluse[256];
  1054.     for (i=0; i<256; i++) coluse[i] = 0;
  1055.  
  1056.     /* figure out which colors in newcmap are used */
  1057.     for (i=cy; i<cy+ch; i++) {
  1058.       pp = newpic + i*w + cx;
  1059.       for (j=cx; j<cx+cw; j++, pp++) coluse[*pp] = 1;
  1060.     }
  1061.  
  1062.     /* now see which of the used colors are new */
  1063.     for (i=0, nc=0; i<256; i++) {
  1064.       if (!coluse[i]) continue;
  1065.       
  1066.       r = newcmap[i*3];  
  1067.       g = newcmap[i*3+1];  
  1068.       b = newcmap[i*3+2];
  1069.       
  1070.       /* lookup r,g,b in pic's colormap */
  1071.       for (k=0; k<numcols && (r!=rMap[k] || g!=gMap[k] || b!=bMap[k]);k++);
  1072.       if (k==numcols) {  /* it's a new color, alright */
  1073.     newr[nc] = r;  newg[nc] = g;  newb[nc] = b;
  1074.     nc++;
  1075.       }
  1076.     }
  1077.   }
  1078.   
  1079.   return nc;
  1080. }
  1081.  
  1082.  
  1083. /********************************************/
  1084. void CropRect2Rect(xp,yp,wp,hp, cx,cy,cw,ch)
  1085.     int *xp, *yp, *wp, *hp, cx, cy, cw, ch;
  1086. {
  1087.   /* crops rect xp,yp,wp,hp to be entirely within bounds of cx,cy,cw,ch */
  1088.  
  1089.   int x1,y1,x2,y2;
  1090.  
  1091.   x1 = *xp;            y1 = *yp;
  1092.   x2 = *xp + *wp - 1;  y2 = *yp + *hp - 1;
  1093.   RANGE(x1, cx, cx+cw-1);
  1094.   RANGE(y1, cy, cy+ch-1);
  1095.   RANGE(x2, cx, cx+cw-1);
  1096.   RANGE(y2, cy, cy+ch-1);
  1097.  
  1098.   if (x2<x1) x2=x1;
  1099.   if (y2<y1) y2=y1;
  1100.  
  1101.   *xp = x1;           *yp = y1;
  1102.   *wp = (x2 - x1)+1;  *hp = (y2 - y1)+1;
  1103. }
  1104.  
  1105.  
  1106. /********************************************/
  1107. /* SELECTION manipulation functions         */
  1108. /********************************************/
  1109.  
  1110.  
  1111. /********************************************/
  1112. void InitSelection()
  1113. {
  1114.   selrx = selry = selrw = selrh = 0;
  1115.   haveSel     = 0;
  1116.   selType     = SEL_RECT;
  1117.   selFilled   = 0;
  1118.   selColor    = 0;
  1119.   selTracking = 0;
  1120. }
  1121.  
  1122.  
  1123. /********************************************/
  1124. int HaveSelection()
  1125. {
  1126.   return haveSel;
  1127. }
  1128.  
  1129.  
  1130. /********************************************/
  1131. int GetSelType()
  1132. {
  1133.   return selType;
  1134. }
  1135.  
  1136.  
  1137. /********************************************/
  1138. void GetSelRCoords(xp, yp, wp, hp)
  1139.      int *xp, *yp, *wp, *hp;
  1140. {
  1141.   /* returns selection rectangle x,y,w,h in pic coordinates */
  1142.  
  1143.   /* NOTE:  SELECTION IS *NOT* GUARANTEED to be within the bounds of 'pic'.
  1144.      It is only guaranteed to *intersect* pic. */
  1145.  
  1146.   *xp = selrx;  *yp = selry;  
  1147.   *wp = selrw;  *hp = selrh;
  1148. }
  1149.  
  1150.  
  1151. /********************************************/
  1152. void EnableSelection(enab)
  1153.      int enab;
  1154. {
  1155.   haveSel = enab;
  1156.   BTSetActive(&but[BCROP], enab);
  1157.   BTSetActive(&but[BCUT],  enab);
  1158.   BTSetActive(&but[BCOPY], enab);
  1159.   BTSetActive(&but[BCLEAR],enab);
  1160.  
  1161.   if (dirUp == BSAVE) CBSetActive(&saveselCB, enab);
  1162.  
  1163.   SetSelectionString();
  1164.   DrawSelection(0);
  1165. }
  1166.  
  1167.  
  1168. /***********************************/
  1169. int DoSelection(ev)
  1170.      XButtonEvent *ev;
  1171. {
  1172.   int          px, py, rv;
  1173.   static Time  lastClickTime   = 0;
  1174.   static int   lastClickButton = Button3;
  1175.  
  1176.  
  1177.   /* actually, this handles all selection-oriented manipulation
  1178.    * if B1 clicked outside sel (or if no sel) remove sel and draw new one
  1179.    * if B1 clicked inside sel, drag sel around
  1180.    * if B1 clicked in sel handles, resize sel
  1181.    * if B1 dbl-clicked in sel, remove sel
  1182.    * if B1 dbl-clicked ouside of sel, make a pic-sized sel
  1183.    * if B2 clicked in sel, do a drag-n-drop operation
  1184.    * B3 not used
  1185.    *
  1186.    * returns '1' if event was handled, '0' otherwise
  1187.    */
  1188.  
  1189.  
  1190.   /* make sure it's even vaguely relevant */
  1191.   if (ev->type   != ButtonPress) return 0;
  1192.   if (ev->window != mainW)       return 0;
  1193.  
  1194.   rv = 0;
  1195.  
  1196.   CoordE2P(ev->x, ev->y, &px, &py);
  1197.  
  1198.   if (ev->button == Button1) {
  1199.     /* double clicked B1 ? */
  1200.     if (lastClickButton==Button1 && (ev->time - lastClickTime) < DBLCLKTIME) {
  1201.       lastClickButton=Button3;
  1202.       if (HaveSelection() && PTINRECT(px, py, selrx, selry, selrw, selrh)) {
  1203.     EnableSelection(0); 
  1204.     rv = 1;
  1205.       }
  1206.       else {
  1207.     selrx = cXOFF;  selry = cYOFF;  selrw = cWIDE;  selrh = cHIGH;
  1208.     EnableSelection(1);
  1209.     rv = 1;
  1210.       }
  1211.     }
  1212.  
  1213.     else if (HaveSelection() && PTINRECT(px,py,selrx,selry,selrw,selrh)) {
  1214.       if (dragHandle(ev)) {}
  1215.       else dragSelection(ev, Button1Mask, 0);
  1216.       rv = 1;
  1217.     }
  1218.  
  1219.     else if (!HaveSelection() || !PTINRECT(px,py,selrx,selry,selrw,selrh)) {
  1220.       if (HaveSelection()) EnableSelection(0);
  1221.       rectSelection(ev);
  1222.     }
  1223.   }
  1224.  
  1225.   else if (ev->button == Button2) {      /* do a drag & drop operation */
  1226.     if (HaveSelection() && PTINRECT(px,py,selrx,selry,selrw,selrh)) {
  1227.       /* clip selection rect to pic */
  1228.       EnableSelection(0);  
  1229.       CropRect2Rect(&selrx, &selry, &selrw, &selrh, 0, 0, pWIDE, pHIGH);
  1230.  
  1231.       if (selrw<1 || selrh<1) rv = 0;
  1232.       else {
  1233.     EnableSelection(1);
  1234.     dragSelection(ev, Button2Mask, 1);
  1235.     rv = 1;
  1236.       }
  1237.     }
  1238.   }
  1239.  
  1240.   lastClickTime   = ev->time;
  1241.   lastClickButton = ev->button;
  1242.   return rv;
  1243. }
  1244.  
  1245.  
  1246. /********************************************/
  1247. static int dragHandle(ev)
  1248.      XButtonEvent *ev;
  1249. {
  1250.   /* called on a B1 press inside the selection area.  if mouse clicked on
  1251.    * one of the selection handles, drag the handle until released.
  1252.    * Selection may be dragged outside of 'pic' boundaries
  1253.    * holding SHIFT constrains selection to be square,
  1254.    * holding CTRL  constrains selection to keep original aspect ratio
  1255.    */
  1256.   
  1257.   int          i, mex, mey, mpx, mpy, offx,offy;
  1258.   int          sex, sey, sex2, sey2, sew, seh, sew2, seh2, hs, h2;
  1259.   int          istp, isbt, islf, isrt, isvm, ishm;
  1260.   int          cnstsq, cnstasp;
  1261.   double       orgaspect;
  1262.   Window       rW, cW;
  1263.   int          mx, my, RX, RY;
  1264.   unsigned int mask;
  1265.  
  1266.   mex = ev->x;  mey = ev->y;
  1267.  
  1268.   CoordP2E(selrx,       selry,       &sex,  &sey);
  1269.   CoordP2E(selrx+selrw, selry+selrh, &sex2, &sey2);
  1270.   sew  = sex2-sex;
  1271.   seh  = sey2-sey;
  1272.   sew2 = sew/2;
  1273.   seh2 = seh/2;
  1274.   sex2--;  sey2--;
  1275.   
  1276.   if      (sew>=35 && seh>=35) hs=7;
  1277.   else if (sew>=20 && seh>=20) hs=5;
  1278.   else if (sew>= 9 && seh>= 9) hs=3;
  1279.   else return 0;
  1280.  
  1281.   h2 = hs/2;
  1282.  
  1283.   istp = isbt = islf = isrt = isvm = ishm = 0;
  1284.  
  1285.   /* figure out which, if any, handle the mouse is in */
  1286.   if      (mex >= sex         && mex < sex+hs)      islf++;
  1287.   else if (mex >= sex+sew2-h2 && mex < sex+sew2+h2) ishm++;
  1288.   else if (mex >= sex+sew-hs  && mex < sex+sew)     isrt++;
  1289.   else return 0;
  1290.  
  1291.   if      (mey >= sey         && mey < sey+hs)      istp++;
  1292.   else if (mey >= sey+seh2-h2 && mey < sey+seh2+h2) isvm++;
  1293.   else if (mey >= sey+seh-hs  && mey < sey+seh)     isbt++;
  1294.   else return 0;
  1295.  
  1296.  
  1297.   offx = offy = 0;
  1298.   if (islf) offx = sex  - mex;
  1299.   if (isrt) offx = sex2+1 - mex;
  1300.   if (istp) offy = sey  - mey;
  1301.   if (isbt) offy = sey2+1 - mey;
  1302.  
  1303.   if (ishm && isvm) return 0;   /* clicked in middle.  doesn't count */
  1304.  
  1305.   if (selrh==0) orgaspect = 1.0;
  1306.   else orgaspect = (double) selrw / (double) selrh;
  1307.  
  1308.  
  1309.   /* it's definitely in a handle...  track 'til released */
  1310.   
  1311.   DrawSelection(0);
  1312.   selFilled   = 1;
  1313.   selTracking = 1;
  1314.   DrawSelection(0);
  1315.  
  1316.   CoordE2P(mex, mey, &mpx, &mpy);
  1317.  
  1318.   while (1) {
  1319.     if (!XQueryPointer(theDisp,mainW,&rW,&cW,&RX,&RY,&mx,&my,&mask)) continue;
  1320.     if (~mask & Button1Mask) break;
  1321.     cnstsq  = (mask & ShiftMask);
  1322.     cnstasp = (mask & ControlMask);
  1323.  
  1324.     CoordE2Prnd(mx+offx, my+offy, &mpx, &mpy);  /* mouse pos in PIC coords */
  1325.  
  1326.     sex = selrx;  sey = selry;  sew = selrw;  seh = selrh;
  1327.  
  1328.     /* compute new selection rectangle based on *what* handle is dragged */
  1329.     if (islf) {
  1330.       if (mpx>=selrx+selrw) mpx = selrx+selrw-1;
  1331.       sex = mpx;  sew = (selrx + selrw) - mpx;
  1332.     }
  1333.  
  1334.     if (isrt) {
  1335.       if (mpx<=selrx) mpx=selrx+1;
  1336.       sew = mpx - selrx;
  1337.     }
  1338.  
  1339.     if (istp) {
  1340.       if (mpy>=selry+selrh) mpy = selry+selrh-1;
  1341.       sey = mpy;  seh = (selry + selrh) - mpy;
  1342.     }
  1343.  
  1344.     if (isbt) {
  1345.       if (mpy<=selry) mpy=selry+1;
  1346.       seh = mpy - selry;
  1347.     }
  1348.  
  1349.  
  1350.  
  1351.     if (cnstsq || cnstasp) {
  1352.       int newwide, newhigh, chwide, chhigh;
  1353.  
  1354.       chwide = chhigh = newwide = newhigh = 0;
  1355.  
  1356.       if (cnstsq) { /* constrain to a square */
  1357.     if (islf || isrt) { chhigh=1;  newhigh = sew; }
  1358.                  else { chwide=1;  newwide = seh; }
  1359.       }
  1360.       else {         /* constrain to same aspect ratio */
  1361.     double asp;
  1362.     if (seh==0) { chwide=1; newwide=0; }
  1363.     else {
  1364.       asp = (double) sew / (double) seh;
  1365.       if (islf || isrt) { chhigh=1;  newhigh = (int) (sew/orgaspect); }
  1366.                        else { chwide=1;  newwide = (int) (seh*orgaspect); }
  1367.     }
  1368.       }
  1369.       
  1370.       if (chwide) {
  1371.     if (islf) { sex = (sex+sew) - newwide; }
  1372.     sew = newwide;
  1373.       }
  1374.       
  1375.       if (chhigh) {
  1376.     if (istp) { sey = (sey+seh) - newhigh; }
  1377.     seh = newhigh;
  1378.       }
  1379.     }
  1380.  
  1381.     if (sew<1) sew=1;
  1382.     if (seh<1) seh=1;
  1383.     
  1384.     if (sex!=selrx || sey!=selry || sew!=selrw || seh!=selrh) {
  1385.       DrawSelection(0);
  1386.       selrx = sex;  selry = sey;  selrw = sew;  selrh = seh;
  1387.       DrawSelection(1);
  1388.       if (infoUp) SetSelectionString();
  1389.       XSync(theDisp, False);
  1390.     }
  1391.     else {
  1392.       DrawSelection(0);
  1393.       DrawSelection(1);
  1394.       XSync(theDisp, False);
  1395.       Timer(100);
  1396.     }
  1397.   }
  1398.   
  1399.   EnableSelection(0);
  1400.  
  1401.   selFilled   = 0;
  1402.   selTracking = 0;
  1403.  
  1404.   /* only 'enable' the selection if it intersects CPIC */
  1405.   if (selrx < cXOFF+cWIDE && selrx+selrw > cXOFF && 
  1406.       selry < cYOFF+cHIGH && selry+selrh > cYOFF) EnableSelection(1);
  1407.  
  1408.   return 1;
  1409. }
  1410.  
  1411.  
  1412. /********************************************/
  1413. static void dragSelection(ev, bmask, dragndrop)
  1414.      XButtonEvent *ev;
  1415.      unsigned int bmask;
  1416.      int          dragndrop;
  1417. {
  1418.   /* called on a button press inside the selection area.  drags selection
  1419.    * around until button has been released.  Selection may be dragged outside
  1420.    * of 'pic' boundaries.  Holding SHIFT constrains movement to 90-degree
  1421.    * angles
  1422.    *
  1423.    * if 'dragndrop', changes cursor, monitors CTRL status
  1424.    */
  1425.   
  1426.   int          mpx, mpy, offx, offy;
  1427.   int          newsx, newsy, orgsx, orgsy, cnstrain, docopy, lastdocopy;
  1428.   Window       rW, cW;
  1429.   int          mx, my, RX, RY;
  1430.   unsigned int mask;
  1431.  
  1432.   if (!dragcurs) buildCursors();
  1433.  
  1434.   if (dragndrop) XDefineCursor(theDisp, mainW, cutcurs);
  1435.             else XDefineCursor(theDisp, mainW, dragcurs);
  1436.  
  1437.   CoordE2P(ev->x, ev->y, &mpx, &mpy);
  1438.   offx = mpx - selrx;  offy = mpy - selry;
  1439.   
  1440.   /* track rectangle until we get a release */
  1441.   
  1442.   DrawSelection(0);
  1443.   selFilled   = 1;
  1444.   selTracking = 1;
  1445.   DrawSelection(0);
  1446.  
  1447.   orgsx = selrx;  orgsy = selry;  docopy = lastdocopy = 0;
  1448.  
  1449.   while (1) {
  1450.     if (!XQueryPointer(theDisp,mainW,&rW,&cW,&RX,&RY,&mx,&my,&mask)) continue;
  1451.     if (~mask & bmask) break;
  1452.     cnstrain = (mask & ShiftMask);
  1453.     docopy   = (mask & ControlMask);
  1454.  
  1455.     if (dragndrop && docopy != lastdocopy) {
  1456.       XDefineCursor(theDisp, mainW, (docopy) ? copycurs : cutcurs);
  1457.       lastdocopy = docopy;
  1458.     }
  1459.  
  1460.     CoordE2P(mx, my, &mpx, &mpy);    /* mouse pos in PIC coord system */
  1461.  
  1462.     newsx = mpx - offx;
  1463.     newsy = mpy - offy;
  1464.  
  1465.     if (cnstrain) {
  1466.       int dx, dy;
  1467.       dx = newsx - orgsx;  dy = newsy - orgsy;
  1468.       if      (abs(dx) > abs(dy)) dy = 0;
  1469.       else if (abs(dy) > abs(dx)) dx = 0;
  1470.       
  1471.       newsx = orgsx + dx;  newsy = orgsy + dy;
  1472.     }
  1473.  
  1474.     if (newsx != selrx || newsy != selry) {    /* mouse moved */
  1475.       DrawSelection(0);
  1476.       selrx = newsx;  selry = newsy;
  1477.       DrawSelection(1);
  1478.       if (infoUp) SetSelectionString();
  1479.       XSync(theDisp, False);
  1480.     }
  1481.     else {
  1482.       DrawSelection(0);
  1483.       DrawSelection(1);
  1484.       XSync(theDisp, False);
  1485.       Timer(100);
  1486.     }
  1487.   }
  1488.   
  1489.   EnableSelection(0);
  1490.  
  1491.   selFilled   = 0;
  1492.   selTracking = 0;
  1493.  
  1494.   SetCursors(-1);
  1495.  
  1496.   /* only do <whatever> if the selection intersects CPIC */
  1497.  
  1498.  if (selrx < cXOFF+cWIDE && selrx+selrw > cXOFF && 
  1499.       selry < cYOFF+cHIGH && selry+selrh > cYOFF) {
  1500.  
  1501.     EnableSelection(1);
  1502.  
  1503.     if (dragndrop) {
  1504.       int   tmpsx, tmpsy;
  1505.       byte *data;
  1506.       
  1507.       tmpsx = selrx;  tmpsy = selry;
  1508.       selrx = orgsx;  selry = orgsy;
  1509.       
  1510.       data = getSelection();         /* copy old data */
  1511.       if (data) {
  1512.     if (!docopy) clearSelectedArea();
  1513.     selrx = tmpsx;  selry = tmpsy;
  1514.     doPaste(data);
  1515.     free(data);
  1516.       }
  1517.  
  1518.       EnableSelection(0);
  1519.       CropRect2Rect(&selrx, &selry, &selrw, &selrh, 0,0,pWIDE,pHIGH);
  1520.       EnableSelection(1);
  1521.     }
  1522.   }
  1523. }
  1524.  
  1525.  
  1526. /***********************************/
  1527. static void rectSelection(ev)
  1528.      XButtonEvent *ev;
  1529. {
  1530.   Window       rW,cW;
  1531.   int          rx,ry,ox,oy,x,y,active, x1, y1, x2, y2, cnstrain;
  1532.   int          i, px,py,px2,py2,pw,ph;
  1533.   unsigned int mask;
  1534.   
  1535.   /* called on a B1 press in mainW to draw a new rectangular selection.
  1536.    * any former selection has already been removed.  holding shift down
  1537.    * while tracking constrains selection to a square 
  1538.    */
  1539.   
  1540.   active = 0;
  1541.   
  1542.   x1 = ox = ev->x;  y1 = oy = ev->y;               /* nail down one corner */
  1543.   selrx = selry = selrw = selrh = 0;
  1544.   selTracking = 1;
  1545.   
  1546.   while (1) {
  1547.     if (!XQueryPointer(theDisp,mainW,&rW,&cW,&rx,&ry,&x,&y,&mask)) continue;
  1548.     if (!(mask & Button1Mask)) break;      /* button released */
  1549.     cnstrain = (mask & ShiftMask);
  1550.     
  1551.     if (x!=ox || y!=oy) {                  /* moved.  erase and redraw (?) */
  1552.       x2 = x;  y2 = y;
  1553.       
  1554.       /* x1,y1,x2,y2 are in epic coords.  sort, convert to pic coords,
  1555.      and if changed, erase+redraw */
  1556.       
  1557.       CoordE2P(x1, y1, &px,  &py);
  1558.       CoordE2P(x2, y2, &px2, &py2);
  1559.       if (px>px2) { i=px; px=px2; px2=i; }
  1560.       if (py>py2) { i=py; py=py2; py2=i; }
  1561.       pw = px2-px+1;  ph=py2-py+1;
  1562.  
  1563.       /* keep px,py,pw,ph inside 'pic' */
  1564.       
  1565.       if (px<0) { pw+=px;  px=0; }
  1566.       if (py<0) { ph+=py;  py=0; }
  1567.       if (px>pWIDE-1) px = pWIDE-1;
  1568.       if (py>pHIGH-1) py = pHIGH-1;
  1569.       
  1570.       if (pw<0) pw=0;
  1571.       if (ph<0) ph=0;
  1572.       if (px+pw>pWIDE) pw = pWIDE - px;
  1573.       if (py+ph>pHIGH) ph = pHIGH - py;
  1574.       
  1575.       if (cnstrain) {          /* make a square at smaller of w,h */
  1576.     if      (ph>pw) { if (y2<y1) py += (ph-pw);  ph=pw; }
  1577.     else if (pw>ph) { if (x2<x1) px += (pw-ph);  pw=ph; }
  1578.       }
  1579.  
  1580.       /* put x,y,w,h -> selr{x,y,w,h}
  1581.      if the rectangle has changed, erase old and draw new */
  1582.       
  1583.       if (px!=selrx || py!=selry || pw!=selrw || ph!=selrh) {
  1584.     DrawSelection(0);
  1585.     selrx = px;  selry = py;  selrw = pw;  selrh = ph;
  1586.     DrawSelection(1);
  1587.     
  1588.     haveSel = active = (pw>0 && ph>0);
  1589.     if (infoUp) SetSelectionString();
  1590.     XFlush(theDisp);
  1591.       }
  1592.       else {
  1593.     DrawSelection(0);
  1594.     DrawSelection(1);
  1595.     XFlush(theDisp);
  1596.     Timer(100);
  1597.       }
  1598.     }
  1599.   }
  1600.  
  1601.  
  1602.   DrawSelection(0);                 /* erase */
  1603.  
  1604.   selTracking = 0;
  1605.   if (active) EnableSelection(1);
  1606. }
  1607.  
  1608.  
  1609. /***********************************/
  1610. void DrawSelection(newcol)
  1611.      int newcol;
  1612. {
  1613.   /* doesn't affect 'haveSel', as when moving/resizing/tracking the
  1614.      selection we need to erase it and redraw it.  If 'chcol' is
  1615.      set, pick a new 'color' to invert the selection with */
  1616.  
  1617.   int   x,y,x1,y1,w,h;
  1618.   
  1619.   if (newcol) selColor = (selColor+1) & 0x7;
  1620.  
  1621.   /* convert selr{x,y,w,h} into epic coords */
  1622.   CoordP2E(selrx, selry, &x, &y);
  1623.   CoordP2E(selrx+selrw, selry+selrh, &x1, &y1);
  1624.  
  1625.   w = (x1-x)-1;
  1626.   h = (y1-y)-1;
  1627.   if (w<1 || h<1) return;
  1628.  
  1629.   XSetPlaneMask(theDisp, theGC, xorMasks[selColor]);
  1630.   XSetFunction(theDisp,theGC,GXinvert);
  1631.  
  1632.   if (w<=2 || h<=2) {
  1633.     XFillRectangle(theDisp, mainW, theGC, x,y,(u_int) w, (u_int) h);
  1634.  
  1635.     XSetFunction(theDisp,theGC,GXcopy);
  1636.     XSetPlaneMask(theDisp, theGC, AllPlanes);
  1637.     return;
  1638.   }
  1639.  
  1640.  
  1641.   /* if selection completely encloses the image (ie, the selection rect
  1642.      itself isn't visible) draw something that *is* visible */
  1643.  
  1644.   /* if only one (or zero) sides of the sel rect is visible, draw
  1645.      appropriate lines to indicate where the rect is */
  1646.  
  1647.   if (x<0 && x+w>eWIDE && selFilled!=1)
  1648.     XDrawLine(theDisp, mainW, theGC, eWIDE/2, y, eWIDE/2, y+h);
  1649.  
  1650.   if (y<0 && y+h>eHIGH && selFilled!=1)
  1651.     XDrawLine(theDisp, mainW, theGC, x, eHIGH/2, x+w, eHIGH/2);
  1652.  
  1653.   
  1654.   if (selFilled==0 || selFilled == 1) {
  1655.     /* one little kludge:  if w or h == eWIDE or eHIGH, make it one smaller */
  1656.     if (x+w == eWIDE) w--;
  1657.     if (y+h == eHIGH) h--;
  1658.     XDrawRectangle(theDisp,mainW,theGC, x,y, (u_int) w, (u_int) h);
  1659.  
  1660.     if (selFilled==0 && !selTracking) {  /* draw 'handles' */
  1661.       int hs, h1, h2;
  1662.  
  1663.       if      (w>=35 && h>=35) { hs=7;  h1=6; h2=3; }
  1664.       else if (w>=20 && h>=20) { hs=5;  h1=4; h2=2; }
  1665.       else if (w>= 9 && h>= 9) { hs=3;  h1=2; h2=1; }
  1666.       else hs=h1=h2=0;
  1667.       
  1668.       if (hs) {
  1669.     XFillRectangle(theDisp,mainW,theGC,x+1,     y+1,  (u_int)h1,(u_int)h1);
  1670.     XFillRectangle(theDisp,mainW,theGC,x+w/2-h2,y+1,  (u_int)hs,(u_int)h1);
  1671.     XFillRectangle(theDisp,mainW,theGC,x+w-h1,  y+1,  (u_int)h1,(u_int)h1);
  1672.     
  1673.     XFillRectangle(theDisp,mainW,theGC,x+1,   y+h/2-h2,
  1674.                (u_int)h1, (u_int)hs);
  1675.     XFillRectangle(theDisp,mainW,theGC,x+w-h1,y+h/2-h2,
  1676.                (u_int)h1, (u_int)hs);
  1677.     
  1678.     XFillRectangle(theDisp,mainW,theGC,x+1,     y+h-h1,
  1679.                (u_int)h1,(u_int)h1);
  1680.     XFillRectangle(theDisp,mainW,theGC,x+w/2-h2,y+h-h1,
  1681.                (u_int)hs,(u_int)h1);
  1682.     XFillRectangle(theDisp,mainW,theGC,x+w-h1,  y+h-h1,
  1683.                (u_int)h1,(u_int)h1);
  1684.       }
  1685.     }
  1686.     
  1687.     if (selFilled==1) {
  1688.       XDrawLine(theDisp, mainW, theGC, x+1, y+1,   x+w-1, y+h-1);
  1689.       XDrawLine(theDisp, mainW, theGC, x+1, y+h-1, x+w-1, y+1);
  1690.     }
  1691.   }
  1692.   else if (selFilled==2) {
  1693.     XFillRectangle(theDisp, mainW, theGC, x,y,(u_int) w, (u_int) h);
  1694.   }
  1695.   
  1696.   
  1697.   XSetFunction(theDisp,theGC,GXcopy);
  1698.   XSetPlaneMask(theDisp, theGC, AllPlanes);
  1699. }
  1700.  
  1701.  
  1702. /********************************************/
  1703. void MoveGrowSelection(dx,dy,dw,dh)
  1704.      int dx,dy,dw,dh;
  1705. {
  1706.   /* moves and/or grows the selection by the specified amount 
  1707.      (in pic coords).  keeps the selection entirely within 'pic'.
  1708.      (called by 'CropKey()') */
  1709.  
  1710.   int x,y,w,h;
  1711.  
  1712.   if (!HaveSelection()) return;
  1713.  
  1714.   GetSelRCoords(&x, &y, &w, &h);
  1715.  
  1716.   x += dx;  y += dy;  w += dw;  h += dh;
  1717.  
  1718.   CropRect2Rect(&x,&y,&w,&h, 0,0,pWIDE,pHIGH);
  1719.   if (w<1) w=1;
  1720.   if (h<1) h=1;
  1721.  
  1722.   /* put x,y,w,h -> selr{x,y,w,h}
  1723.      if the rectangle has changed, erase old and draw new */
  1724.  
  1725.   if (x!=selrx || y!=selry || w!=selrw || h!=selrh) {
  1726.     DrawSelection(0);
  1727.     selrx = x;  selry = y;  selrw = w;  selrh = h;
  1728.     EnableSelection(1);
  1729.   }
  1730. }
  1731.  
  1732.   
  1733. /***********************************/
  1734. void BlinkSelection(cnt)
  1735.      int cnt;
  1736. {
  1737.   if (!HaveSelection()) return;
  1738.  
  1739.   for ( ; cnt>0; cnt--) {
  1740.     DrawSelection(0);
  1741.     XFlush(theDisp);
  1742.     Timer(100);
  1743.   }
  1744. }
  1745.  
  1746.  
  1747. /***********************************/
  1748. void FlashSelection(cnt)
  1749.      int cnt;
  1750. {
  1751.   int i;
  1752.  
  1753.   if (!HaveSelection()) return;
  1754.   i = selFilled;
  1755.  
  1756.   for ( ; cnt>0; cnt--) {
  1757.     selFilled = 2;
  1758.     DrawSelection(0);
  1759.     XFlush(theDisp);
  1760.     Timer(100);
  1761.   }
  1762.  
  1763.   selFilled = i;
  1764. }
  1765.  
  1766.  
  1767. /********************************************/
  1768. static void makePasteSel(cimg)
  1769.      byte *cimg;
  1770. {
  1771.   /* makes a selection rectangle the size of the beastie on the clipboard,
  1772.    * centered on cpic.  selection is allowed to be bigger than pic
  1773.    */
  1774.  
  1775.   int          clipw, cliph;
  1776.  
  1777.   if (!cimg) return;
  1778.   clipw = cimg[CIMG_W] | ((int) (cimg[CIMG_W+1]<<8));
  1779.   cliph = cimg[CIMG_H] | ((int) (cimg[CIMG_H+1]<<8));
  1780.   if (clipw<1 || cliph<1) return;
  1781.  
  1782.   selrw = clipw;  selrh = cliph;
  1783.  
  1784.   selrx = (cXOFF + cWIDE/2) - selrw/2;
  1785.   selry = (cYOFF + cHIGH/2) - selrh/2;
  1786.  
  1787.   EnableSelection(1);
  1788. }
  1789.  
  1790.  
  1791.  
  1792.  
  1793. /********************************************/
  1794. /* COORDINATE SYSTEM TRANSLATION FUNCTIONS  */
  1795. /********************************************/
  1796.  
  1797.  
  1798. void CoordE2C(ex, ey, cx_ret, cy_ret)
  1799.      int ex, ey, *cx_ret, *cy_ret;
  1800. {
  1801.   /* the weirdness causes everything to round towards neg infinity */
  1802.   *cx_ret = ((ex*cWIDE) / eWIDE) + ((ex<0) ? -1 : 0);
  1803.   *cy_ret = ((ey*cHIGH) / eHIGH) + ((ey<0) ? -1 : 0);
  1804. }
  1805.  
  1806.  
  1807. void CoordC2E(cx, cy, ex_ret, ey_ret)
  1808.      int cx, cy, *ex_ret, *ey_ret;
  1809. {
  1810.   /* this makes positive #s round to +inf, and neg # round to -inf */
  1811.   if (cx>=0) *ex_ret = (cx*eWIDE + (cWIDE-1)) / cWIDE;
  1812.         else *ex_ret = (cx*eWIDE - (cWIDE-1)) / cWIDE;
  1813.   if (cy>=0) *ey_ret = (cy*eHIGH + (cHIGH-1)) / cHIGH;
  1814.         else *ey_ret = (cy*eHIGH - (cHIGH-1)) / cHIGH;
  1815. }
  1816.  
  1817.  
  1818. void CoordP2C(px, py, cx_ret, cy_ret)
  1819.      int px, py, *cx_ret, *cy_ret;
  1820. {
  1821.   *cx_ret = px - cXOFF;
  1822.   *cy_ret = py - cYOFF;
  1823. }
  1824.  
  1825.  
  1826. void CoordC2P(cx, cy, px_ret, py_ret)
  1827.      int cx, cy, *px_ret, *py_ret;
  1828. {
  1829.   *px_ret = cx + cXOFF;
  1830.   *py_ret = cy + cYOFF;
  1831. }
  1832.  
  1833.  
  1834. void CoordP2E(px, py, ex_ret, ey_ret)
  1835.      int px, py, *ex_ret, *ey_ret;
  1836. {
  1837.   int cx, cy;
  1838.   CoordP2C(px, py, &cx, &cy);
  1839.   CoordC2E(cx, cy, ex_ret, ey_ret);
  1840. }
  1841.  
  1842.  
  1843. void CoordE2P(ex, ey, px_ret, py_ret)
  1844.      int ex, ey, *px_ret, *py_ret;
  1845. {
  1846.   int cx, cy;
  1847.   CoordE2C(ex, ey, &cx, &cy);
  1848.   CoordC2P(cx, cy, px_ret, py_ret);
  1849. }
  1850.  
  1851.  
  1852. static void CoordE2Prnd(ex, ey, px_ret, py_ret)
  1853.      int ex, ey, *px_ret, *py_ret;
  1854. {
  1855.   int cx, cy;
  1856.   cx = ((ex*cWIDE + (eWIDE/2)) / eWIDE) + ((ex<0) ? -1 : 0);
  1857.   cy = ((ey*cHIGH + (eHIGH/2)) / eHIGH) + ((ey<0) ? -1 : 0);
  1858.  
  1859.   CoordC2P(cx, cy, px_ret, py_ret);
  1860. }
  1861.  
  1862.