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