home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
vsiftp.vmssoftware.com
/
VSIPUBLIC@vsiftp.vmssoftware.com.tar
/
FREEWARE
/
FREEWARE40.ZIP
/
xv310a
/
xvcut.c
< prev
next >
Wrap
Text File
|
1995-06-12
|
49KB
|
1,863 lines
/*
* xvcut.c - xv cut-n-paste (and eventually drag-n-drop) related functions
*
* Contains:
* int CutAllowed();
* int PasteAllowed();
* void DoImgCopy();
* void DoImgCut();
* void DoImgClear();
* void DoImgPaste();
*
* static byte *getSelection ();
* static byte *getFromClip ();
* void SaveToClip (data);
* static void clearSelectedArea();
* static void makeClipFName ();
* static int countcols24 (byte *, int,int, int,int,int,int));
* static int countNewCols (byte*, int, int, byte*, int,
* int, int, int, int);
*
* void InitSelection ();
* int HaveSelection ();
* int GetSelType ();
* void GetSelRCoords (&x, &y, &w, &h);
* void EnableSelection(onoff);
* void DrawSelection (onoff);
* int DoSelection (XButtonEvent *);
* static int dragHandle (XButtonEvent *);
* static void dragSelection (XButtonEvent *, u_int bmask, dndrop);
* static void rectSelection (XButtonEvent *);
* void MoveGrowSelection(dx,dy,dw,dh);
*
* void BlinkSelection (cnt);
* void FlashSelection (cnt);
*
* static void makePasteSel (data);
*
* void CropRect2Rect (int*,int*,int*,int*, int,int,int,int);
*
* coordinate system transforms between pic, cpic, and epic coords
* void CoordE2C(ex, ey, &cx, &cy);
* void CoordC2E(cx, cy, &ex, &ey);
* void CoordP2C(px, py, &cx, &cy);
* void CoordC2P(cx, cy, &px, &py);
* void CoordE2P(ex, ey, &px, &py);
* void CoordP2E(px, py, &ex, &ey);
*/
#define NEEDSDIR /* for stat() */
#include "copyright.h"
#include "xv.h"
#include "bits/cut"
#include "bits/cutm"
#include "bits/copy"
#include "bits/copym"
#define DBLCLKTIME 500
#define CLIPPROP "XV_CLIPBOARD"
/***
*** local functions
***/
static void doPaste PARM((byte *));
static void buildCursors PARM((void));
static byte *getSelection PARM((void));
static byte *getFromClip PARM((void));
static void clearSelectedArea PARM((void));
static void makeClipFName PARM((void));
static int countcols24 PARM((byte *, int, int, int, int, int, int));
static int countNewCols PARM((byte *, int, int, byte *, int,
int, int, int, int));
static int dragHandle PARM((XButtonEvent *));
static void dragSelection PARM((XButtonEvent *, u_int, int));
static void rectSelection PARM((XButtonEvent *));
static void makePasteSel PARM((byte *));
static void CoordE2Prnd PARM((int, int, int *, int *));
/***
*** local data
***/
static char *clipfname = (char *) NULL;
static char clipfnstr[MAXPATHLEN];
static int selrx, selry, selrw, selrh; /* PIC coords of sel. rect */
static int haveSel; /* have a selection? */
static int selType; /* type of sel. (RECT, etc) */
static int selFilled; /* if true flash whole sel */
static int selColor; /* selection 'mask' 0-7 */
static int selTracking; /* in a 'tracking' loop */
static Cursor dragcurs = (Cursor) 0;
static Cursor copycurs = (Cursor) 0;
static Cursor cutcurs = (Cursor) 0;
/********************************************/
int CutAllowed()
{
/* returns '1' if cut/copy/clear commands should be enabled (ie, if
there's a selection of some sort */
return (but[BCROP].active);
}
/********************************************/
int PasteAllowed()
{
Atom clipAtom;
struct stat st;
/* returns '1' if there's something on the clipboard to be pasted */
/* also, can't paste if we're in a root mode */
if (useroot) return 0;
/* see if there's a CLIPPROP associated with the root window,
if there is, we'll use that as the clipboard: return '1' */
clipAtom = XInternAtom(theDisp, CLIPPROP, True);
if (clipAtom != None) return 1; /* clipboard property exists: can paste */
/* barring that, see if the CLIPFILE exists. if so, we can paste */
if (!clipfname) makeClipFName();
if (stat(clipfname, &st)==0) return 1; /* file exists: can paste */
return 0;
}
/********************************************/
void DoImgCopy()
{
/*
* called when 'Copy' command is issued. does entire user interface,
* such as it is, and puts appropriate data onto the clipboard.
* Does all complaining on any errors
*/
byte *cimg;
if (!HaveSelection()) return;
cimg = getSelection();
if (!cimg) return;
SaveToClip(cimg);
free(cimg);
FlashSelection(2);
SetCursors(-1);
}
/********************************************/
void DoImgCut()
{
/*
* called when 'Cut' command is issued. does entire user interface,
* such as it is, and puts appropriate data onto the clipboard.
* Does all complaining on any errors
*/
byte *cimg;
if (!HaveSelection()) return;
FlashSelection(2);
cimg = getSelection();
if (!cimg) return;
SaveToClip(cimg);
free(cimg);
clearSelectedArea();
SetCursors(-1);
}
/********************************************/
void DoImgClear()
{
/* called when 'Clear' command is issued */
if (!HaveSelection()) return;
FlashSelection(2);
clearSelectedArea();
SetCursors(-1);
}
/********************************************/
void DoImgPaste()
{
byte *cimg;
if (!PasteAllowed()) { XBell(theDisp, 0); return; }
cimg = getFromClip();
if (!cimg) return;
/* if there's no selection, make one! */
if (!HaveSelection()) makePasteSel(cimg);
else doPaste(cimg);
free(cimg);
SetCursors(-1);
}
/********************************************/
static void doPaste(cimg)
byte *cimg;
{
/*
* This is fairly hairy.
*/
byte *dp, *dpic, *clippic, *clipcmap;
int clipw, cliph, clipis24, len, istran, trval;
int i, j, sx,sy,sw,sh, cx,cy,cw,ch, dx,dy,dw,dh,dx2,dy2;
/*
* verify clipboard data
*/
if (!cimg) return;
len = ((int) cimg[CIMG_LEN + 0]) |
((int) (cimg[CIMG_LEN + 1]<<8)) |
((int) (cimg[CIMG_LEN + 2]<<16)) |
((int) (cimg[CIMG_LEN + 3]<<24));
if (len < CIMG_PIC24) return;
istran = cimg[CIMG_TRANS];
trval = cimg[CIMG_TRVAL];
clipis24 = cimg[CIMG_24];
clipw = cimg[CIMG_W] | ((int) (cimg[CIMG_W+1]<<8));
cliph = cimg[CIMG_H] | ((int) (cimg[CIMG_H+1]<<8));
if (clipw<1 || cliph<1) return;
if (( clipis24 && len != CIMG_PIC24 + clipw * cliph * 3) ||
(!clipis24 && len != CIMG_PIC8 + clipw * cliph) ) {
ErrPopUp("The clipboard data is not in a recognized format!", "\nMoof!");
goto exit;
}
clippic = cimg + ((clipis24) ? CIMG_PIC24 : CIMG_PIC8);
clipcmap = cimg + CIMG_CMAP;
/* determine if we're going to promote 'pic' to 24-bits (if it isn't
* already, because if we *are*, we'd prefer to do any clipboard rescaling
* in 24-bit space for the obvious reasons.
*
* possibilities:
* PIC24 - easy, do clipboard rescale in 24-bit space
* PIC8, and clipboard is 8 bits, (or 24-bits, but with <=256 colors)
* and total unique colors < 256:
* do 8-bit rescale & paste, don't ask anything
* PIC8, and clipboard is 24 bits, or 8-bits but total # of cols >256:
* *ask* if they want to promote pic. if so, do it, otherwise
* do clipboard rescale in 8-bits, and do the best we can...
*/
GetSelRCoords(&sx, &sy, &sw, &sh); /* sel rect in pic coords */
/* dx,dy,dw,dh is the rectangle (in PIC coords) where the paste will occur
(cropped to be entirely within PIC */
dx = sx; dy = sy; dw = sw; dh = sh;
CropRect2Rect(&dx, &dy, &dw, &dh, 0, 0, pWIDE, pHIGH);
/* cx,cy,cw,ch is the rectangle of the clipboard data (in clipboard coords)
that will actually be used in the paste operation */
cx = (sx>=0) ? 0 : ((-sx) * clipw) / sw;
cy = (sy>=0) ? 0 : ((-sy) * cliph) / sh;
cw = (dw * clipw) / sw;
ch = (dh * cliph) / sh;
/* see if we have to ask any questions */
if (picType == PIC8) {
int ncc, keep8;
char buf[512];
if (clipis24) { /* pasting in a 24-bit image that *requires* promotion */
static char *bnames[] = { "\nOkay", "\033Cancel" };
strcpy(buf, "Warning: Pasting this 24-bit image will require ");
strcat(buf, "promoting the current image to 24 bits.");
if (PopUp(buf, bnames, 2)) goto exit; /* Cancelled */
else Change824Mode(PIC24); /* promote pic to 24 bits */
}
else { /* clip is 8 bits */
ncc = countNewCols(clippic,clipw,cliph,clipcmap,clipis24,cx,cy,cw,ch);
if (ncc + numcols > 256) {
static char *bnames[] = { "\nPromote", "8Keep 8-bit", "\033Cancel" };
strcpy(buf,"Warning: The image and the clipboard combine to have ");
strcat(buf,"more than 256 unique colors. Promoting the ");
strcat(buf,"image to 24 bits is recommended, otherwise the contents ");
strcat(buf,"of the clipboard will probably lose some colors.");
keep8 = PopUp(buf, bnames, 3);
if (keep8==2) goto exit; /* Cancel */
else if (keep8==0) Change824Mode(PIC24); /* promote pic to 24 bits */
}
}
}
/* legal possibilities at this point:
* pic is PIC24: clip is 8 or 24
* pic is PIC8: clip is 8, or clip is 24 but has 256 or fewer colors
*/
if (picType == PIC8) {
int clx, cly, r,g,b,k,mind,close,newcols;
byte *cp, *clp, *pp, *ccp, newr[256], newg[256], newb[256], remap[256];
byte order[256], trans[256];
int bperpix, dpncols;
dpic = (byte *) malloc((size_t) dw * dh);
if (!dpic) FatalError("Out of memory in DoImgPaste()\n");
bperpix = (clipis24) ? 3 : 1;
newcols = 0;
/* dpic = a scaled, 8-bit representation of clippic[cx,cy,cw,ch] */
if (!clipis24) { /* copy colormap from clip data into newr,g,b[] */
for (i=0; i<256; i++) {
newr[i] = clipcmap[i*3];
newg[i] = clipcmap[i*3 + 1];
newb[i] = clipcmap[i*3 + 2];
}
}
for (i=0; i<dh; i++) { /* un-smooth 8-bit resize */
dp = dpic + i*dw;
cly = cy + (i * ch) / dh;
clp = clippic + (cly*clipw * bperpix);
for (j=0; j<dw; j++, dp++) {
/* get appropriate pixel from clippic */
clx = cx + (j * cw) / dw;
cp = clp + (clx * bperpix);
if (!clipis24) *dp = *cp;
else { /* build colormap as we go... */
r = *cp++; g = *cp++; b = *cp++;
/* look it up in new colormap, add if not there */
for (k=0; k<newcols && (r!=newr[k] || g!=newg[k] ||b!=newb[k]); k++);
if (k==newcols && k<256) {
newr[k]=r; newg[k]=g; newb[k]=b; newcols++;
}
*dp = (byte) (k & 0xff);
}
}
}
SortColormap(dpic, dw, dh, &dpncols, newr,newg,newb, order, trans);
for (i=0, dp=dpic; i<dw*dh; i++, dp++) *dp = trans[*dp];
if (istran) {
if (!clipis24) trval = trans[trval];
else {
for (i=0; i<dpncols; i++) {
if (cimg[CIMG_TRR] == newr[i] &&
cimg[CIMG_TRG] == newg[i] &&
cimg[CIMG_TRB] == newb[i]) { trval = i; break; }
}
}
}
/* COLORMAP MERGING */
newcols = 0;
for (i=0; i<dpncols; i++) {
if (istran && i==trval) continue;
for (j=0; j<numcols; j++) { /* look for an exact match */
if (rMap[j]==newr[i] && gMap[j]==newg[i] && bMap[j]==newb[i]) break;
}
if (j<numcols) remap[i] = j;
else { /* no exact match */
newcols++;
if (numcols < 256) {
rMap[numcols] = newr[i];
gMap[numcols] = newg[i];
bMap[numcols] = newb[i];
remap[i] = numcols;
numcols++;
}
else { /* map to closest in image colormap */
r = newr[i]; g=newg[i]; b=newb[i];
mind = 256*256 + 256*256 + 256*256;
for (j=close=0; j<numcols; j++) {
k = ((rMap[j]-r) * (rMap[j]-r)) +
((gMap[j]-g) * (gMap[j]-g)) +
((bMap[j]-b) * (bMap[j]-b));
if (k<mind) { mind = k; close = j; }
}
remap[i] = (byte) close;
}
}
}
/* copy the data into PIC */
dp = dpic;
for (i=dy; i<dy+dh; i++) {
pp = pic + (i*pWIDE) + dx;
for (j=dx; j<dx+dw; j++) {
if (istran && *dp==trval) { pp++; dp++; }
else { *pp++ = remap[*dp++]; }
}
}
free(dpic);
if (newcols) InstallNewPic(); /* does color reallocation, etc. */
else {
GenerateCpic();
GenerateEpic(eWIDE, eHIGH);
DrawEpic();
}
}
/******************** PIC24 handling **********************/
else {
byte *tmppic, *cp, *pp, *clp;
int bperpix;
int trr, trg, trb, clx, cly;
trr = trg = trb = 0;
if (istran) {
if (clipis24) {
trr = cimg[CIMG_TRR];
trg = cimg[CIMG_TRG];
trb = cimg[CIMG_TRB];
}
else {
trr = clipcmap[trval*3];
trg = clipcmap[trval*3+1];
trb = clipcmap[trval*3+2];
}
}
bperpix = (clipis24) ? 3 : 1;
if (!istran && (cw != dw || ch != dh)) { /* need to resize, can smooth */
byte rmap[256], gmap[256], bmap[256];
tmppic = (byte *) malloc((size_t) cw * ch * bperpix);
if (!tmppic) FatalError("Out of memory in DoImgPaste()\n");
/* copy relevant hunk of clippic into tmppic (Smooth24 only works on
complete images */
for (i=0; i<ch; i++) {
dp = tmppic + i*cw*bperpix;
cp = clippic + ((i+cy)*clipw + cx) * bperpix;
for (j=0; j<cw*bperpix; j++) *dp++ = *cp++;
}
if (!clipis24) {
for (i=0; i<256; i++) {
rmap[i] = clipcmap[i*3];
gmap[i] = clipcmap[i*3+1];
bmap[i] = clipcmap[i*3+2];
}
}
dpic = Smooth24(tmppic, clipis24, cw,ch, dw,dh, rmap,gmap,bmap);
if (!dpic) FatalError("Out of memory (2) in DoImgPaste()\n");
free(tmppic);
/* copy the resized, smoothed, 24-bit data into 'pic' */
/* XXX: (deal with smooth-resized transparent imgs) */
dp = dpic;
for (i=dy; i<dy+dh; i++) {
pp = pic + (i*pWIDE + dx) * 3;
for (j=0; j<dw; j++) {
if (istran && dp[0]==trr && dp[1]==trg && dp[2]==trb) {
pp +=3; dp += 3;
} else {
*pp++ = *dp++; *pp++ = *dp++; *pp++ = *dp++;
}
}
}
free(dpic);
}
else { /* can't do smooth resize. Do non-smooth resize (if any!) */
for (i=0; i<dh; i++) {
pp = pic + ((i+dy)*pWIDE + dx) * 3;
cly = cy + (i * ch) / dh;
clp = clippic + (cly*clipw * bperpix);
for (j=0; j<dw; j++, pp+=3) {
clx = cx + (j * cw) / dw;
cp = clp + (clx * bperpix);
if (clipis24) {
if (!istran || cp[0]!=trr || cp[1]!=trg || cp[2]==trb) {
pp[0] = *cp++; pp[1] = *cp++; pp[2] = *cp++;
}
}
else { /* clip is 8 bit */
if (!istran || *cp != trval) {
pp[0] = clipcmap[*cp * 3];
pp[1] = clipcmap[*cp * 3 + 1];
pp[2] = clipcmap[*cp * 3 + 2];
}
}
}
}
}
GenerateCpic();
GenerateEpic(eWIDE, eHIGH);
DrawEpic();
}
exit:
SetCursors(-1);
}
/********************************************/
static void buildCursors()
{
Pixmap p1,p2,p3,p4;
XColor cfg, cbg;
dragcurs = XCreateFontCursor(theDisp, XC_fleur);
p1 = XCreatePixmapFromBitmapData(theDisp, rootW, (char *) cut_bits,
cut_width, cut_height, 1L, 0L, 1);
p2 = XCreatePixmapFromBitmapData(theDisp, rootW, (char *) cutm_bits,
cutm_width, cutm_height, 1L, 0L, 1);
p3 = XCreatePixmapFromBitmapData(theDisp, rootW, (char *) copy_bits,
copy_width, copy_height, 1L, 0L, 1);
p4 = XCreatePixmapFromBitmapData(theDisp, rootW, (char *) copym_bits,
copym_width, copym_height, 1L, 0L, 1);
if (p1 && p2 && p3 && p4) {
cfg.red = cfg.green = cfg.blue = 0;
cbg.red = cbg.green = cbg.blue = 0xffff;
cutcurs = XCreatePixmapCursor(theDisp, p1,p2, &cfg, &cbg,
cut_x_hot, cut_y_hot);
copycurs = XCreatePixmapCursor(theDisp, p3,p4, &cfg, &cbg,
copy_x_hot, copy_y_hot);
if (!cutcurs || !copycurs) FatalError("can't create cut/copy cursors...");
}
if (p1) XFreePixmap(theDisp, p1);
if (p2) XFreePixmap(theDisp, p2);
if (p3) XFreePixmap(theDisp, p3);
if (p4) XFreePixmap(theDisp, p4);
}
/********************************************/
static byte *getSelection()
{
/* alloc's and builds image with values based on currently selected
* portion of the image. Returns NULL on failure
*
* also note: getSelection will always fill trans,r,g,b with 0, for now
* as you can't 'select' transparent regions. Other code (TextPaste()),
* *can* generate semi-transparent objects to be pasted
*/
byte *pp, *dp, *cimg;
int i, j, k, x, y, w, h, do24, len;
if (!CutAllowed()) { XBell(theDisp, 0); return (byte *) NULL; }
if (!HaveSelection()) return (byte *) NULL;
GetSelRCoords(&x,&y,&w,&h);
CropRect2Rect(&x,&y,&w,&h, 0,0,pWIDE,pHIGH);
/* make selection be entirely within image */
EnableSelection(0);
selrx = x; selry = y; selrw = w; selrh = h;
EnableSelection(1);
if (picType == PIC24 && countcols24(pic,pWIDE,pHIGH, x,y,w,h)>256) {
do24=1;
len = CIMG_PIC24 + w*h*3;
}
else {
do24=0;
len = CIMG_PIC8 + w*h;
}
cimg = (byte *) malloc((size_t) len);
if (!cimg) {
ErrPopUp("Unable to malloc() temporary space for the selection.",
"\nByte Me!");
return (byte *) NULL;
}
cimg[CIMG_LEN ] = len & 0xff;
cimg[CIMG_LEN+1] = (len>> 8) & 0xff;
cimg[CIMG_LEN+2] = (len>>16) & 0xff;
cimg[CIMG_LEN+3] = (len>>24) & 0xff;
cimg[CIMG_W ] = w & 0xff;
cimg[CIMG_W+1] = (w>>8) & 0xff;
cimg[CIMG_H ] = h & 0xff;
cimg[CIMG_H+1] = (h>>8) & 0xff;
cimg[CIMG_24] = do24;
cimg[CIMG_TRANS] = 0;
if (picType == PIC24 && !do24) { /* 24-bit data as 8-bit */
int nc,pr,pg,pb;
byte *cm;
nc = 0;
dp = cimg + CIMG_PIC8;
for (i=y; i<y+h; i++) {
pp = pic + i*pWIDE*3 + x*3;
for (j=x; j<x+w; j++, pp+=3) {
pr = pp[0]; pg = pp[1]; pb = pp[2];
cm = cimg + CIMG_CMAP;
for (k=0; k<nc; k++,cm+=3) {
if (pr==cm[0] && pg==cm[1] && pb==cm[2]) break;
}
if (k==nc) {
nc++;
cimg[CIMG_CMAP + nc*3 ] = pr;
cimg[CIMG_CMAP + nc*3 + 1] = pg;
cimg[CIMG_CMAP + nc*3 + 2] = pb;
}
*dp++ = (byte) k;
}
}
}
else if (picType == PIC24) { /* 24-bit data as 24-bit */
dp = cimg + CIMG_PIC24;
for (i=y; i<y+h; i++) {
pp = pic + i*pWIDE*3 + x*3;
for (j=x; j<x+w; j++) {
*dp++ = *pp++;
*dp++ = *pp++;
*dp++ = *pp++;
}
}
}
else if (picType == PIC8) { /* 8-bit selection */
byte *cm = cimg + CIMG_CMAP;
for (i=0; i<256; i++) { /* copy colormap */
if (i<numcols) {
*cm++ = rMap[i];
*cm++ = gMap[i];
*cm++ = bMap[i];
}
}
dp = cimg + CIMG_PIC8;
for (i=y; i<y+h; i++) { /* copy image */
pp = pic + i*pWIDE + x;
for (j=x; j<x+w; j++) *dp++ = *pp++;
}
}
return cimg;
}
/********************************************/
static byte *getFromClip()
{
/* gets whatever data is on the clipboard, in CIMG_* format */
Atom clipAtom, actType;
int i, actFormat, len;
unsigned long nitems, nleft;
byte *data, *data1, lbuf[4];
FILE *fp;
char str[512];
if (forceClipFile) { /* remove property, if any */
clipAtom = XInternAtom(theDisp, CLIPPROP, True);
if (clipAtom != None) XDeleteProperty(theDisp, rootW, clipAtom);
}
clipAtom = XInternAtom(theDisp, CLIPPROP, True); /* find prop */
if (clipAtom != None) {
/* try to retrieve the length of the data in the property */
i = XGetWindowProperty(theDisp, rootW, clipAtom, 0L, 1L, False, XA_STRING,
&actType, &actFormat, &nitems, &nleft,
(unsigned char **) &data);
if (i==Success && actType==XA_STRING && actFormat==8 && nleft>0) {
/* got some useful data */
len = data[0];
len |= ((int) data[1])<<8;
len |= ((int) data[2])<<16;
len |= ((int) data[3])<<24;
XFree((void *) data);
/* read the rest of the data (len bytes) */
i = XGetWindowProperty(theDisp, rootW, clipAtom, 1L,
(long) ((len-4)+3)/4,
False, XA_STRING, &actType, &actFormat, &nitems,
&nleft, (unsigned char **) &data);
if (i==Success) {
/* copy data into regular 'malloc'd space, so we won't have to use
XFree() to get rid of it in calling procs */
data1 = (byte *) malloc((size_t) len);
if (!data1) {
XFree((void *) data);
ErrPopUp("Insufficient memory to retrieve clipboard!", "\nShucks!");
return (byte *) NULL;
}
data1[0] = len & 0xff;
data1[1] = (len>> 8) & 0xff;
data1[2] = (len>>16) & 0xff;
data1[3] = (len>>24) & 0xff;
xvbcopy((char *) data, (char *) data1+4, (size_t) len-4);
XFree((void *) data);
return data1;
}
}
}
/* if we're still here, then the prop method was less than successful.
use the file method, instead */
if (!clipfname) makeClipFName();
fp = fopen(clipfname, "r");
if (!fp) {
unlink(clipfname);
sprintf(str, "Can't read clipboard file '%s'\n\n %s.",
clipfname, ERRSTR(errno));
ErrPopUp(str,"\nBletch!");
return (byte *) NULL;
}
/* get data length */
if (fread((char *) lbuf, (size_t) 1, (size_t) 4, fp) != 4) {
fclose(fp);
unlink(clipfname);
sprintf(str, "Error occurred while reading clipboard file.\n\n %s.",
ERRSTR(errno));
ErrPopUp(str,"\nGlork!");
return (byte *) NULL;
}
len = lbuf[0];
len |= ((int) lbuf[1])<<8;
len |= ((int) lbuf[2])<<16;
len |= ((int) lbuf[3])<<24;
data = (byte *) malloc((size_t) len);
if (!data) {
ErrPopUp("Insufficient memory to retrieve clipboard!", "\nShucks!");
return (byte *) NULL;
}
data[0] = len & 0xff;
data[1] = (len>> 8) & 0xff;
data[2] = (len>>16) & 0xff;
data[3] = (len>>24) & 0xff;
/* get data */
if (fread((char *) data+4, (size_t) 1, (size_t) len-4, fp) != len-4) {
fclose(fp);
free(data);
unlink(clipfname);
sprintf(str, "Error occurred while reading clipboard file.\n\n %s.",
ERRSTR(errno));
ErrPopUp(str,"\nNertz!");
return (byte *) NULL;
}
fclose(fp);
return data;
}
/********************************************/
void SaveToClip(cimg)
byte *cimg;
{
/* takes the 'thing' pointed to by data and sticks it on the clipboard.
always tries to use the property method. If it gets a BadAlloc
error (the X server ran out of memory (ie, probably an X terminal)),
it deletes the property, and falls back to using the file method */
Atom clipAtom;
FILE *fp;
char str[512];
int len;
if (!cimg) return;
len = ((int) cimg[CIMG_LEN + 0]) |
((int) (cimg[CIMG_LEN + 1]<<8)) |
((int) (cimg[CIMG_LEN + 2]<<16)) |
((int) (cimg[CIMG_LEN + 3]<<24));
if (forceClipFile) { /* remove property, if any */
clipAtom = XInternAtom(theDisp, CLIPPROP, True);
if (clipAtom != None) XDeleteProperty(theDisp, rootW, clipAtom);
}
if (!forceClipFile) {
clipAtom = XInternAtom(theDisp, CLIPPROP, False); /* find or make prop */
if (clipAtom != None) {
/* try to store the data in the property */
xerrcode = 0;
XChangeProperty(theDisp, rootW, clipAtom, XA_STRING, 8, PropModeReplace,
cimg, len);
XSync(theDisp, False); /* make it happen *now* */
if (!xerrcode) return; /* success! */
/* failed, use file method */
XDeleteProperty(theDisp, rootW, clipAtom);
}
}
/* if we're still here, try the file method */
if (!clipfname) makeClipFName();
fp = fopen(clipfname, "w");
if (!fp) {
unlink(clipfname);
sprintf(str, "Can't write clipboard file '%s'\n\n %s.",
clipfname, ERRSTR(errno));
ErrPopUp(str,"\nBletch!");
return;
}
if (fwrite((char *) cimg, (size_t) 1, (size_t) len, fp) != len) {
fclose(fp);
unlink(clipfname);
sprintf(str, "Error occurred while writing to clipboard file.\n\n %s.",
ERRSTR(errno));
ErrPopUp(str,"\nGlork!");
return;
}
fclose(fp);
}
/********************************************/
static void clearSelectedArea()
{
/* called by 'Cut' or 'Clear' functions. fills the selected area of the
image with either clearR,clearG,clearB (in PIC24 mode), or editColor
(in PIC8 mode). Regens and redraws the image */
int i,j,x,y,w,h;
byte *pp;
if (!HaveSelection()) return;
GetSelRCoords(&x,&y,&w,&h);
CropRect2Rect(&x,&y,&w,&h, 0,0,pWIDE,pHIGH);
if (picType == PIC24) {
for (i=y; i<y+h && i<pHIGH; i++) {
pp = pic + i*pWIDE*3 + x*3;
for (j=x; j<x+w && j<pWIDE; j++) {
*pp++ = clearR;
*pp++ = clearG;
*pp++ = clearB;
}
}
}
else { /* PIC8 */
for (i=y; i<y+h && i<pHIGH; i++) {
pp = pic + i*pWIDE + x;
for (j=x; j<x+w && j<pWIDE; j++) *pp++ = editColor;
}
}
GenerateCpic();
GenerateEpic(eWIDE,eHIGH);
DrawEpic(); /* redraws selection, also */
}
/********************************************/
static void makeClipFName()
{
char *homedir;
if (clipfname) return;
#ifdef VMS
sprintf(clipfnstr, "SYS$LOGIN:%s", CLIPFILE);
#else
homedir = (char *) getenv("HOME");
if (!homedir) homedir = ".";
sprintf(clipfnstr, "%s/%s", homedir, CLIPFILE);
#endif
clipfname = clipfnstr;
}
/********************************************/
static int countcols24(pic, pwide, phigh, x, y, w, h)
byte *pic;
int pwide, phigh, x,y,w,h;
{
/* counts the # of unique colors in a selected rect of a 24-bit image
returns '0-256' or >256 */
int i, j, k, nc;
u_int rgb[257], col;
byte *pp;
nc = 0;
for (i=y; nc<257 && i<y+h; i++) {
pp = pic + i*pwide*3 + x*3;
for (j=x; nc<257 && j<x+w; j++, pp+=3) {
col = (((u_int) pp[0])<<16) + (((u_int) pp[1])<<8) + pp[2];
for (k=0; k<nc && col != rgb[k]; k++);
if (k==nc) rgb[nc++] = col; /* not found, add it */
}
}
return nc;
}
/********************************************/
static int countNewCols(newpic, w,h, newcmap, is24, cx,cy,cw,ch)
byte *newpic, *newcmap;
int w,h, is24, cx,cy,cw,ch;
{
/* computes the number of NEW colors in the specified region of the
* new pic, with respect to 'pic'. returns 0-257 (where 257 means
* 'some unknown # greater than 256')
*/
int i, j, k, nc, r,g,b;
byte *pp, *cp;
byte newr[257], newg[257], newb[257];
if (picType != PIC8) return 0; /* shouldn't happen */
nc = 0; /* # of new colors */
if (is24) {
for (i=cy; i<cy+ch; i++) {
pp = newpic + i*w*3 + cx*3;
for (j=cx; j<cx+cw; j++) {
r = *pp++; g = *pp++; b = *pp++;
/* lookup r,g,b in 'pic's colormap and the newcolors colormap */
for (k=0; k<nc && (r!=newr[k] || g!=newg[k] || b!=newb[k]); k++);
if (k==nc) {
for (k=0; k<numcols && (r!=rMap[k] || g!=gMap[k] || b!=bMap[k]);k++);
if (k==numcols) { /* it's a new color, alright */
newr[nc] = r; newg[nc] = g; newb[nc] = b;
nc++;
if (nc==257) return nc;
}
}
}
}
}
else { /* newpic is an 8-bit pic */
int coluse[256];
for (i=0; i<256; i++) coluse[i] = 0;
/* figure out which colors in newcmap are used */
for (i=cy; i<cy+ch; i++) {
pp = newpic + i*w + cx;
for (j=cx; j<cx+cw; j++, pp++) coluse[*pp] = 1;
}
/* now see which of the used colors are new */
for (i=0, nc=0; i<256; i++) {
if (!coluse[i]) continue;
r = newcmap[i*3];
g = newcmap[i*3+1];
b = newcmap[i*3+2];
/* lookup r,g,b in pic's colormap */
for (k=0; k<numcols && (r!=rMap[k] || g!=gMap[k] || b!=bMap[k]);k++);
if (k==numcols) { /* it's a new color, alright */
newr[nc] = r; newg[nc] = g; newb[nc] = b;
nc++;
}
}
}
return nc;
}
/********************************************/
void CropRect2Rect(xp,yp,wp,hp, cx,cy,cw,ch)
int *xp, *yp, *wp, *hp, cx, cy, cw, ch;
{
/* crops rect xp,yp,wp,hp to be entirely within bounds of cx,cy,cw,ch */
int x1,y1,x2,y2;
x1 = *xp; y1 = *yp;
x2 = *xp + *wp - 1; y2 = *yp + *hp - 1;
RANGE(x1, cx, cx+cw-1);
RANGE(y1, cy, cy+ch-1);
RANGE(x2, cx, cx+cw-1);
RANGE(y2, cy, cy+ch-1);
if (x2<x1) x2=x1;
if (y2<y1) y2=y1;
*xp = x1; *yp = y1;
*wp = (x2 - x1)+1; *hp = (y2 - y1)+1;
}
/********************************************/
/* SELECTION manipulation functions */
/********************************************/
/********************************************/
void InitSelection()
{
selrx = selry = selrw = selrh = 0;
haveSel = 0;
selType = SEL_RECT;
selFilled = 0;
selColor = 0;
selTracking = 0;
}
/********************************************/
int HaveSelection()
{
return haveSel;
}
/********************************************/
int GetSelType()
{
return selType;
}
/********************************************/
void GetSelRCoords(xp, yp, wp, hp)
int *xp, *yp, *wp, *hp;
{
/* returns selection rectangle x,y,w,h in pic coordinates */
/* NOTE: SELECTION IS *NOT* GUARANTEED to be within the bounds of 'pic'.
It is only guaranteed to *intersect* pic. */
*xp = selrx; *yp = selry;
*wp = selrw; *hp = selrh;
}
/********************************************/
void EnableSelection(enab)
int enab;
{
haveSel = enab;
BTSetActive(&but[BCROP], enab);
BTSetActive(&but[BCUT], enab);
BTSetActive(&but[BCOPY], enab);
BTSetActive(&but[BCLEAR],enab);
if (dirUp == BSAVE) CBSetActive(&saveselCB, enab);
SetSelectionString();
DrawSelection(0);
}
/***********************************/
int DoSelection(ev)
XButtonEvent *ev;
{
int px, py, rv;
static Time lastClickTime = 0;
static int lastClickButton = Button3;
/* actually, this handles all selection-oriented manipulation
* if B1 clicked outside sel (or if no sel) remove sel and draw new one
* if B1 clicked inside sel, drag sel around
* if B1 clicked in sel handles, resize sel
* if B1 dbl-clicked in sel, remove sel
* if B1 dbl-clicked ouside of sel, make a pic-sized sel
* if B2 clicked in sel, do a drag-n-drop operation
* B3 not used
*
* returns '1' if event was handled, '0' otherwise
*/
/* make sure it's even vaguely relevant */
if (ev->type != ButtonPress) return 0;
if (ev->window != mainW) return 0;
rv = 0;
CoordE2P(ev->x, ev->y, &px, &py);
if (ev->button == Button1) {
/* double clicked B1 ? */
if (lastClickButton==Button1 && (ev->time - lastClickTime) < DBLCLKTIME) {
lastClickButton=Button3;
if (HaveSelection() && PTINRECT(px, py, selrx, selry, selrw, selrh)) {
EnableSelection(0);
rv = 1;
}
else {
selrx = cXOFF; selry = cYOFF; selrw = cWIDE; selrh = cHIGH;
EnableSelection(1);
rv = 1;
}
}
else if (HaveSelection() && PTINRECT(px,py,selrx,selry,selrw,selrh)) {
if (dragHandle(ev)) {}
else dragSelection(ev, Button1Mask, 0);
rv = 1;
}
else if (!HaveSelection() || !PTINRECT(px,py,selrx,selry,selrw,selrh)) {
if (HaveSelection()) EnableSelection(0);
rectSelection(ev);
}
}
else if (ev->button == Button2) { /* do a drag & drop operation */
if (HaveSelection() && PTINRECT(px,py,selrx,selry,selrw,selrh)) {
/* clip selection rect to pic */
EnableSelection(0);
CropRect2Rect(&selrx, &selry, &selrw, &selrh, 0, 0, pWIDE, pHIGH);
if (selrw<1 || selrh<1) rv = 0;
else {
EnableSelection(1);
dragSelection(ev, Button2Mask, 1);
rv = 1;
}
}
}
lastClickTime = ev->time;
lastClickButton = ev->button;
return rv;
}
/********************************************/
static int dragHandle(ev)
XButtonEvent *ev;
{
/* called on a B1 press inside the selection area. if mouse clicked on
* one of the selection handles, drag the handle until released.
* Selection may be dragged outside of 'pic' boundaries
* holding SHIFT constrains selection to be square,
* holding CTRL constrains selection to keep original aspect ratio
*/
int i, mex, mey, mpx, mpy, offx,offy;
int sex, sey, sex2, sey2, sew, seh, sew2, seh2, hs, h2;
int istp, isbt, islf, isrt, isvm, ishm;
int cnstsq, cnstasp;
double orgaspect;
Window rW, cW;
int mx, my, RX, RY;
unsigned int mask;
mex = ev->x; mey = ev->y;
CoordP2E(selrx, selry, &sex, &sey);
CoordP2E(selrx+selrw, selry+selrh, &sex2, &sey2);
sew = sex2-sex;
seh = sey2-sey;
sew2 = sew/2;
seh2 = seh/2;
sex2--; sey2--;
if (sew>=35 && seh>=35) hs=7;
else if (sew>=20 && seh>=20) hs=5;
else if (sew>= 9 && seh>= 9) hs=3;
else return 0;
h2 = hs/2;
istp = isbt = islf = isrt = isvm = ishm = 0;
/* figure out which, if any, handle the mouse is in */
if (mex >= sex && mex < sex+hs) islf++;
else if (mex >= sex+sew2-h2 && mex < sex+sew2+h2) ishm++;
else if (mex >= sex+sew-hs && mex < sex+sew) isrt++;
else return 0;
if (mey >= sey && mey < sey+hs) istp++;
else if (mey >= sey+seh2-h2 && mey < sey+seh2+h2) isvm++;
else if (mey >= sey+seh-hs && mey < sey+seh) isbt++;
else return 0;
offx = offy = 0;
if (islf) offx = sex - mex;
if (isrt) offx = sex2+1 - mex;
if (istp) offy = sey - mey;
if (isbt) offy = sey2+1 - mey;
if (ishm && isvm) return 0; /* clicked in middle. doesn't count */
if (selrh==0) orgaspect = 1.0;
else orgaspect = (double) selrw / (double) selrh;
/* it's definitely in a handle... track 'til released */
DrawSelection(0);
selFilled = 1;
selTracking = 1;
DrawSelection(0);
CoordE2P(mex, mey, &mpx, &mpy);
while (1) {
if (!XQueryPointer(theDisp,mainW,&rW,&cW,&RX,&RY,&mx,&my,&mask)) continue;
if (~mask & Button1Mask) break;
cnstsq = (mask & ShiftMask);
cnstasp = (mask & ControlMask);
CoordE2Prnd(mx+offx, my+offy, &mpx, &mpy); /* mouse pos in PIC coords */
sex = selrx; sey = selry; sew = selrw; seh = selrh;
/* compute new selection rectangle based on *what* handle is dragged */
if (islf) {
if (mpx>=selrx+selrw) mpx = selrx+selrw-1;
sex = mpx; sew = (selrx + selrw) - mpx;
}
if (isrt) {
if (mpx<=selrx) mpx=selrx+1;
sew = mpx - selrx;
}
if (istp) {
if (mpy>=selry+selrh) mpy = selry+selrh-1;
sey = mpy; seh = (selry + selrh) - mpy;
}
if (isbt) {
if (mpy<=selry) mpy=selry+1;
seh = mpy - selry;
}
if (cnstsq || cnstasp) {
int newwide, newhigh, chwide, chhigh;
chwide = chhigh = newwide = newhigh = 0;
if (cnstsq) { /* constrain to a square */
if (islf || isrt) { chhigh=1; newhigh = sew; }
else { chwide=1; newwide = seh; }
}
else { /* constrain to same aspect ratio */
double asp;
if (seh==0) { chwide=1; newwide=0; }
else {
asp = (double) sew / (double) seh;
if (islf || isrt) { chhigh=1; newhigh = (int) (sew/orgaspect); }
else { chwide=1; newwide = (int) (seh*orgaspect); }
}
}
if (chwide) {
if (islf) { sex = (sex+sew) - newwide; }
sew = newwide;
}
if (chhigh) {
if (istp) { sey = (sey+seh) - newhigh; }
seh = newhigh;
}
}
if (sew<1) sew=1;
if (seh<1) seh=1;
if (sex!=selrx || sey!=selry || sew!=selrw || seh!=selrh) {
DrawSelection(0);
selrx = sex; selry = sey; selrw = sew; selrh = seh;
DrawSelection(1);
if (infoUp) SetSelectionString();
XSync(theDisp, False);
}
else {
DrawSelection(0);
DrawSelection(1);
XSync(theDisp, False);
Timer(100);
}
}
EnableSelection(0);
selFilled = 0;
selTracking = 0;
/* only 'enable' the selection if it intersects CPIC */
if (selrx < cXOFF+cWIDE && selrx+selrw > cXOFF &&
selry < cYOFF+cHIGH && selry+selrh > cYOFF) EnableSelection(1);
return 1;
}
/********************************************/
static void dragSelection(ev, bmask, dragndrop)
XButtonEvent *ev;
unsigned int bmask;
int dragndrop;
{
/* called on a button press inside the selection area. drags selection
* around until button has been released. Selection may be dragged outside
* of 'pic' boundaries. Holding SHIFT constrains movement to 90-degree
* angles
*
* if 'dragndrop', changes cursor, monitors CTRL status
*/
int mpx, mpy, offx, offy;
int newsx, newsy, orgsx, orgsy, cnstrain, docopy, lastdocopy;
Window rW, cW;
int mx, my, RX, RY;
unsigned int mask;
if (!dragcurs) buildCursors();
if (dragndrop) XDefineCursor(theDisp, mainW, cutcurs);
else XDefineCursor(theDisp, mainW, dragcurs);
CoordE2P(ev->x, ev->y, &mpx, &mpy);
offx = mpx - selrx; offy = mpy - selry;
/* track rectangle until we get a release */
DrawSelection(0);
selFilled = 1;
selTracking = 1;
DrawSelection(0);
orgsx = selrx; orgsy = selry; docopy = lastdocopy = 0;
while (1) {
if (!XQueryPointer(theDisp,mainW,&rW,&cW,&RX,&RY,&mx,&my,&mask)) continue;
if (~mask & bmask) break;
cnstrain = (mask & ShiftMask);
docopy = (mask & ControlMask);
if (dragndrop && docopy != lastdocopy) {
XDefineCursor(theDisp, mainW, (docopy) ? copycurs : cutcurs);
lastdocopy = docopy;
}
CoordE2P(mx, my, &mpx, &mpy); /* mouse pos in PIC coord system */
newsx = mpx - offx;
newsy = mpy - offy;
if (cnstrain) {
int dx, dy;
dx = newsx - orgsx; dy = newsy - orgsy;
if (abs(dx) > abs(dy)) dy = 0;
else if (abs(dy) > abs(dx)) dx = 0;
newsx = orgsx + dx; newsy = orgsy + dy;
}
if (newsx != selrx || newsy != selry) { /* mouse moved */
DrawSelection(0);
selrx = newsx; selry = newsy;
DrawSelection(1);
if (infoUp) SetSelectionString();
XSync(theDisp, False);
}
else {
DrawSelection(0);
DrawSelection(1);
XSync(theDisp, False);
Timer(100);
}
}
EnableSelection(0);
selFilled = 0;
selTracking = 0;
SetCursors(-1);
/* only do <whatever> if the selection intersects CPIC */
if (selrx < cXOFF+cWIDE && selrx+selrw > cXOFF &&
selry < cYOFF+cHIGH && selry+selrh > cYOFF) {
EnableSelection(1);
if (dragndrop) {
int tmpsx, tmpsy;
byte *data;
tmpsx = selrx; tmpsy = selry;
selrx = orgsx; selry = orgsy;
data = getSelection(); /* copy old data */
if (data) {
if (!docopy) clearSelectedArea();
selrx = tmpsx; selry = tmpsy;
doPaste(data);
free(data);
}
EnableSelection(0);
CropRect2Rect(&selrx, &selry, &selrw, &selrh, 0,0,pWIDE,pHIGH);
EnableSelection(1);
}
}
}
/***********************************/
static void rectSelection(ev)
XButtonEvent *ev;
{
Window rW,cW;
int rx,ry,ox,oy,x,y,active, x1, y1, x2, y2, cnstrain;
int i, px,py,px2,py2,pw,ph;
unsigned int mask;
/* called on a B1 press in mainW to draw a new rectangular selection.
* any former selection has already been removed. holding shift down
* while tracking constrains selection to a square
*/
active = 0;
x1 = ox = ev->x; y1 = oy = ev->y; /* nail down one corner */
selrx = selry = selrw = selrh = 0;
selTracking = 1;
while (1) {
if (!XQueryPointer(theDisp,mainW,&rW,&cW,&rx,&ry,&x,&y,&mask)) continue;
if (!(mask & Button1Mask)) break; /* button released */
cnstrain = (mask & ShiftMask);
if (x!=ox || y!=oy) { /* moved. erase and redraw (?) */
x2 = x; y2 = y;
/* x1,y1,x2,y2 are in epic coords. sort, convert to pic coords,
and if changed, erase+redraw */
CoordE2P(x1, y1, &px, &py);
CoordE2P(x2, y2, &px2, &py2);
if (px>px2) { i=px; px=px2; px2=i; }
if (py>py2) { i=py; py=py2; py2=i; }
pw = px2-px+1; ph=py2-py+1;
/* keep px,py,pw,ph inside 'pic' */
if (px<0) { pw+=px; px=0; }
if (py<0) { ph+=py; py=0; }
if (px>pWIDE-1) px = pWIDE-1;
if (py>pHIGH-1) py = pHIGH-1;
if (pw<0) pw=0;
if (ph<0) ph=0;
if (px+pw>pWIDE) pw = pWIDE - px;
if (py+ph>pHIGH) ph = pHIGH - py;
if (cnstrain) { /* make a square at smaller of w,h */
if (ph>pw) { if (y2<y1) py += (ph-pw); ph=pw; }
else if (pw>ph) { if (x2<x1) px += (pw-ph); pw=ph; }
}
/* put x,y,w,h -> selr{x,y,w,h}
if the rectangle has changed, erase old and draw new */
if (px!=selrx || py!=selry || pw!=selrw || ph!=selrh) {
DrawSelection(0);
selrx = px; selry = py; selrw = pw; selrh = ph;
DrawSelection(1);
haveSel = active = (pw>0 && ph>0);
if (infoUp) SetSelectionString();
XFlush(theDisp);
}
else {
DrawSelection(0);
DrawSelection(1);
XFlush(theDisp);
Timer(100);
}
}
}
DrawSelection(0); /* erase */
selTracking = 0;
if (active) EnableSelection(1);
}
/***********************************/
void DrawSelection(newcol)
int newcol;
{
/* doesn't affect 'haveSel', as when moving/resizing/tracking the
selection we need to erase it and redraw it. If 'chcol' is
set, pick a new 'color' to invert the selection with */
int x,y,x1,y1,w,h;
if (newcol) selColor = (selColor+1) & 0x7;
/* convert selr{x,y,w,h} into epic coords */
CoordP2E(selrx, selry, &x, &y);
CoordP2E(selrx+selrw, selry+selrh, &x1, &y1);
w = (x1-x)-1;
h = (y1-y)-1;
if (w<1 || h<1) return;
XSetPlaneMask(theDisp, theGC, xorMasks[selColor]);
XSetFunction(theDisp,theGC,GXinvert);
if (w<=2 || h<=2) {
XFillRectangle(theDisp, mainW, theGC, x,y,(u_int) w, (u_int) h);
XSetFunction(theDisp,theGC,GXcopy);
XSetPlaneMask(theDisp, theGC, AllPlanes);
return;
}
/* if selection completely encloses the image (ie, the selection rect
itself isn't visible) draw something that *is* visible */
/* if only one (or zero) sides of the sel rect is visible, draw
appropriate lines to indicate where the rect is */
if (x<0 && x+w>eWIDE && selFilled!=1)
XDrawLine(theDisp, mainW, theGC, eWIDE/2, y, eWIDE/2, y+h);
if (y<0 && y+h>eHIGH && selFilled!=1)
XDrawLine(theDisp, mainW, theGC, x, eHIGH/2, x+w, eHIGH/2);
if (selFilled==0 || selFilled == 1) {
/* one little kludge: if w or h == eWIDE or eHIGH, make it one smaller */
if (x+w == eWIDE) w--;
if (y+h == eHIGH) h--;
XDrawRectangle(theDisp,mainW,theGC, x,y, (u_int) w, (u_int) h);
if (selFilled==0 && !selTracking) { /* draw 'handles' */
int hs, h1, h2;
if (w>=35 && h>=35) { hs=7; h1=6; h2=3; }
else if (w>=20 && h>=20) { hs=5; h1=4; h2=2; }
else if (w>= 9 && h>= 9) { hs=3; h1=2; h2=1; }
else hs=h1=h2=0;
if (hs) {
XFillRectangle(theDisp,mainW,theGC,x+1, y+1, (u_int)h1,(u_int)h1);
XFillRectangle(theDisp,mainW,theGC,x+w/2-h2,y+1, (u_int)hs,(u_int)h1);
XFillRectangle(theDisp,mainW,theGC,x+w-h1, y+1, (u_int)h1,(u_int)h1);
XFillRectangle(theDisp,mainW,theGC,x+1, y+h/2-h2,
(u_int)h1, (u_int)hs);
XFillRectangle(theDisp,mainW,theGC,x+w-h1,y+h/2-h2,
(u_int)h1, (u_int)hs);
XFillRectangle(theDisp,mainW,theGC,x+1, y+h-h1,
(u_int)h1,(u_int)h1);
XFillRectangle(theDisp,mainW,theGC,x+w/2-h2,y+h-h1,
(u_int)hs,(u_int)h1);
XFillRectangle(theDisp,mainW,theGC,x+w-h1, y+h-h1,
(u_int)h1,(u_int)h1);
}
}
if (selFilled==1) {
XDrawLine(theDisp, mainW, theGC, x+1, y+1, x+w-1, y+h-1);
XDrawLine(theDisp, mainW, theGC, x+1, y+h-1, x+w-1, y+1);
}
}
else if (selFilled==2) {
XFillRectangle(theDisp, mainW, theGC, x,y,(u_int) w, (u_int) h);
}
XSetFunction(theDisp,theGC,GXcopy);
XSetPlaneMask(theDisp, theGC, AllPlanes);
}
/********************************************/
void MoveGrowSelection(dx,dy,dw,dh)
int dx,dy,dw,dh;
{
/* moves and/or grows the selection by the specified amount
(in pic coords). keeps the selection entirely within 'pic'.
(called by 'CropKey()') */
int x,y,w,h;
if (!HaveSelection()) return;
GetSelRCoords(&x, &y, &w, &h);
x += dx; y += dy; w += dw; h += dh;
CropRect2Rect(&x,&y,&w,&h, 0,0,pWIDE,pHIGH);
if (w<1) w=1;
if (h<1) h=1;
/* put x,y,w,h -> selr{x,y,w,h}
if the rectangle has changed, erase old and draw new */
if (x!=selrx || y!=selry || w!=selrw || h!=selrh) {
DrawSelection(0);
selrx = x; selry = y; selrw = w; selrh = h;
EnableSelection(1);
}
}
/***********************************/
void BlinkSelection(cnt)
int cnt;
{
if (!HaveSelection()) return;
for ( ; cnt>0; cnt--) {
DrawSelection(0);
XFlush(theDisp);
Timer(100);
}
}
/***********************************/
void FlashSelection(cnt)
int cnt;
{
int i;
if (!HaveSelection()) return;
i = selFilled;
for ( ; cnt>0; cnt--) {
selFilled = 2;
DrawSelection(0);
XFlush(theDisp);
Timer(100);
}
selFilled = i;
}
/********************************************/
static void makePasteSel(cimg)
byte *cimg;
{
/* makes a selection rectangle the size of the beastie on the clipboard,
* centered on cpic. selection is allowed to be bigger than pic
*/
int clipw, cliph;
if (!cimg) return;
clipw = cimg[CIMG_W] | ((int) (cimg[CIMG_W+1]<<8));
cliph = cimg[CIMG_H] | ((int) (cimg[CIMG_H+1]<<8));
if (clipw<1 || cliph<1) return;
selrw = clipw; selrh = cliph;
selrx = (cXOFF + cWIDE/2) - selrw/2;
selry = (cYOFF + cHIGH/2) - selrh/2;
EnableSelection(1);
}
/********************************************/
/* COORDINATE SYSTEM TRANSLATION FUNCTIONS */
/********************************************/
void CoordE2C(ex, ey, cx_ret, cy_ret)
int ex, ey, *cx_ret, *cy_ret;
{
/* the weirdness causes everything to round towards neg infinity */
*cx_ret = ((ex*cWIDE) / eWIDE) + ((ex<0) ? -1 : 0);
*cy_ret = ((ey*cHIGH) / eHIGH) + ((ey<0) ? -1 : 0);
}
void CoordC2E(cx, cy, ex_ret, ey_ret)
int cx, cy, *ex_ret, *ey_ret;
{
/* this makes positive #s round to +inf, and neg # round to -inf */
if (cx>=0) *ex_ret = (cx*eWIDE + (cWIDE-1)) / cWIDE;
else *ex_ret = (cx*eWIDE - (cWIDE-1)) / cWIDE;
if (cy>=0) *ey_ret = (cy*eHIGH + (cHIGH-1)) / cHIGH;
else *ey_ret = (cy*eHIGH - (cHIGH-1)) / cHIGH;
}
void CoordP2C(px, py, cx_ret, cy_ret)
int px, py, *cx_ret, *cy_ret;
{
*cx_ret = px - cXOFF;
*cy_ret = py - cYOFF;
}
void CoordC2P(cx, cy, px_ret, py_ret)
int cx, cy, *px_ret, *py_ret;
{
*px_ret = cx + cXOFF;
*py_ret = cy + cYOFF;
}
void CoordP2E(px, py, ex_ret, ey_ret)
int px, py, *ex_ret, *ey_ret;
{
int cx, cy;
CoordP2C(px, py, &cx, &cy);
CoordC2E(cx, cy, ex_ret, ey_ret);
}
void CoordE2P(ex, ey, px_ret, py_ret)
int ex, ey, *px_ret, *py_ret;
{
int cx, cy;
CoordE2C(ex, ey, &cx, &cy);
CoordC2P(cx, cy, px_ret, py_ret);
}
static void CoordE2Prnd(ex, ey, px_ret, py_ret)
int ex, ey, *px_ret, *py_ret;
{
int cx, cy;
cx = ((ex*cWIDE + (eWIDE/2)) / eWIDE) + ((ex<0) ? -1 : 0);
cy = ((ey*cHIGH + (eHIGH/2)) / eHIGH) + ((ey<0) ? -1 : 0);
CoordC2P(cx, cy, px_ret, py_ret);
}