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