qxw-20140331/0000750000175000017500000000000012317044644010400 5ustar momoqxw-20140331/draw.c0000640000175000017500000013474012272734365011521 0ustar momo// $Id: draw.c 519 2014-01-31 14:53:41Z mo $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant http://www.quinapalus.com E-mail: qxw@quinapalus.com This file is part of Qxw. Qxw is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Qxw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Qxw. If not, see or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "common.h" #include "qxw.h" #include "draw.h" #include "gui.h" #include "dicts.h" int bawdpx,hbawdpx; static int sfcxoff=0,sfcyoff=0; static double sdirdx[NGTYPE][MAXNDIR]={{1,0},{ 1.2,1.2,0 },{1.4,0.7,-0.7},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0},{1,0}}; static double sdirdy[NGTYPE][MAXNDIR]={{0,1},{-0.7,0.7,1.4},{0 ,1.2, 1.2},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1},{0,1}}; char*dname[NGTYPE][MAXNDIR]={{"Across","Down"},{"Northeast","Southeast","South"},{"East","Southeast","Southwest"},{"Ring","Radial"},{"Ring","Radial"}, {"Across","Down"},{"Across","Down"},{"Across","Down"},{"Across","Down"},{"Across","Down"}}; // GERNERAL DRAWING void moveto(cairo_t*cc,double x,double y) {cairo_move_to(cc,x,y);} void lineto(cairo_t*cc,double x,double y) {cairo_line_to(cc,x,y);} void rmoveto(cairo_t*cc,double x,double y) {cairo_rel_move_to(cc,x,y);} void rlineto(cairo_t*cc,double x,double y) {cairo_rel_line_to(cc,x,y);} void setlinewidth(cairo_t*cc,double w) {cairo_set_line_width(cc,w);} void setlinecap(cairo_t*cc,int c) {cairo_set_line_cap(cc,c);} void closepath(cairo_t*cc) {cairo_close_path(cc);} void fill(cairo_t*cc) {cairo_fill(cc);} void stroke(cairo_t*cc) {cairo_stroke(cc);} void strokepreserve(cairo_t*cc) {cairo_stroke_preserve(cc);} void clip(cairo_t*cc) {cairo_clip(cc);} void gsave(cairo_t*cc) {cairo_save(cc);} void grestore(cairo_t*cc) {cairo_restore(cc);} void setrgbcolor(cairo_t*cc,double r,double g, double b) {cairo_set_source_rgb(cc,r,g,b);} void setrgbacolor(cairo_t*cc,double r,double g, double b,double a) {cairo_set_source_rgba(cc,r,g,b,a);} void setfontstyle(cairo_t*cc,int fs) {cairo_select_font_face(cc,"sans", (fs&2)?CAIRO_FONT_SLANT_ITALIC:CAIRO_FONT_SLANT_NORMAL, (fs&1)?CAIRO_FONT_WEIGHT_BOLD:CAIRO_FONT_WEIGHT_NORMAL );} void setfontsize(cairo_t*cc,double h) {cairo_set_font_size(cc,h);} void showtext(cairo_t*cc,char*s) {cairo_show_text(cc,s);} double textwidth(cairo_t*cc,char*s,double h) { cairo_text_extents_t te; cairo_set_font_size(cc,h); cairo_text_extents(cc,s,&te); return te.x_advance; } // Cairo's arc drawing does not seem to work well as part of a path, so we do our own void arc(cairo_t*cc,double x,double y,double r,double t0,double t1) {double u; if(t11.0/pxsq) { // error of approx 1/4 px? u=(t0+t1)/2; arc(cc,x,y,r,t0,u); arc(cc,x,y,r, u,t1); } else lineto(cc,x+r*cos(t1),y+r*sin(t1)); } void arcn(cairo_t*cc,double x,double y,double r,double t0,double t1) {double u; if(t01.0/pxsq) { // error of approx 1/4 px? u=(t0+t1)/2; arcn(cc,x,y,r,t0,u); arcn(cc,x,y,r, u,t1); } else lineto(cc,x+r*cos(t1),y+r*sin(t1)); } void setrgbcolor24(cairo_t*cc,int c) {setrgbcolor(cc,((c>>16)&255)/255.0,((c>>8)&255)/255.0,(c&255)/255.0);} void ltext(cairo_t*cc,char*s,double h,int fs) {setfontstyle(cc,fs); setfontsize(cc,h); showtext(cc,s);} // Centered text. If ocm==0, print normally spaced; if ocm==1, print one character at a time, regularly spaced void ctext(cairo_t*cc,char*s,double x,double y,double h,int fs,int ocm) {int i,m; char t[2]; double u; setfontstyle(cc,fs); setfontsize(cc,h); if(!ocm) { u=x-textwidth(cc,s,h)/2.0; moveto(cc,u,y); showtext(cc,s); return; } m=strlen(s); t[1]=0; for(i=0;i4) { // we are on a square-format grid but there is potential for wrap-around while(l>2) stepforw(&x,&y,d),l-=2; if((d==0&&x==width -1)||(d==1&&y==height-1)) l=1; } vxcoords(&x0,&y0,(double)x,(double)y,0); vxcoords(&x1,&y1,(double)x,(double)y,ndir[gtype]); *u=(x0+x1)/2+sdirdx[gtype][d]*(l-1)/2.0; *v=(y0+y1)/2+sdirdy[gtype][d]*(l-1)/2.0; } static void addvxcoordsbbox(double*x0,double*y0,double*x1,double*y1,double x,double y,int d) {double vx=0,vy=0; vxcoords(&vx,&vy,x,y,d); if(vx<*x0) *x0=vx; if(vx>*x1) *x1=vx; if(vy<*y0) *y0=vy; if(vy>*y1) *y1=vy; } static void addsqbbox(double*x0,double*y0,double*x1,double*y1,int x,int y) {int d; double vx=0,vy=0; mgcentre(&vx,&vy,x,y,0,1); if(vx-0.5<*x0) *x0=vx-0.5; // include a 1x1 square centred in the middle of the merge group for the letter if(vx+0.5>*x1) *x1=vx+0.5; if(vy-0.5<*y0) *y0=vy-0.5; if(vy+0.5>*y1) *y1=vy+0.5; for(d=0;d1) s=1; return s; } static void movetoorig(cairo_t*cc,int x,int y) {double x0=0,y0=0; switch(gshape[gtype]) { case 0: vxcoords(&x0,&y0,(double)x,(double)y,3);break; case 1: vxcoords(&x0,&y0,(double)x,(double)y,5);break; case 2: vxcoords(&x0,&y0,(double)x,(double)y,4);break; case 3:case 4: vxcoords(&x0,&y0,(double)x,(double)y,1); break; } moveto(cc,x0,y0); } static void movetomgcentre(cairo_t*cc,int x,int y,int d,int l) {double u=0,v=0; mgcentre(&u,&v,x,y,d,l); moveto(cc,u,v); } // draw path for edges with bits set in mask b static void edges(cairo_t*cc,int x,int y,int b) {int f,g,i,j,n,o;double x0=0,y0=0,x1=0,y1=0,t; n=ndir[gtype]*2; b&=(1<>o)&1)==0) break; // find a place to start drawing f=0;g=0; for(i=0;i>j)&1) { edgecoords(&x0,&y0,&x1,&y1,x,y,j); if(f==0) moveto(cc,x0,y0); if((gshape[gtype]==3||gshape[gtype]==4)) { if(j&1) { t=2*PI*((gshape[gtype]==4)?x-0.5:x)/width-PI/2; if(j==1) arcn(cc,(double)height,(double)height,height-y-.999, t+2*PI/width,t); else arc (cc,(double)height,(double)height,height-y ,t,t+2*PI/width); } else lineto(cc,x1,y1); } else { // the following is to defeat this bug: // https://bugs.freedesktop.org/show_bug.cgi?id=39551 lineto(cc,(x0+x1)/2,(y0+y1)/2); moveto(cc,(x0+x1)/2,(y0+y1)/2); lineto(cc,x1,y1); } f=1; } else f=0,g=1; } if(g==0) closepath(cc); } // draw path for outline of a `square' static void sqpath(cairo_t*cc,int x,int y) {int i;double x0=0,y0=0,t; vxcoords(&x0,&y0,(double)x,(double)y,0); moveto(cc,x0,y0); switch(gshape[gtype]) { case 0: case 1: case 2: for(i=1;igx1) gx1=sx1; if(sy1>gy1) gy1=sy1; sfq[x][y]=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,sx1-sx0,sy1-sy0); } DEB4 printf("Overall bbox = (%d,%d)-(%d,%d)\n",gx0,gy0,gx1,gy1); sfb=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,gx1,gy1); // take top left as (0,0) sfc=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,pxsq,pxsq); sfs=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,gx1+hbawdpx-1,gy1+hbawdpx-1); sfh=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,gx1,gy1); sfn=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,gx1,gy1); } // Refresh drawing area: called here with clip already set on cr, so we can // just repaint everything. void repaint(cairo_t*cc) {int i,j; assert(sfb); cairo_set_source_surface(cc,sfb,bawdpx,bawdpx); // paint background cairo_paint(cc); cairo_set_source_surface(cc,sfc,sfcxoff,sfcyoff); // paint cursor cairo_paint(cc); cairo_set_source_surface(cc,sfh,bawdpx,bawdpx); // paint hints cairo_paint(cc); for(j=0;jctlen[i]>l) l=sq->ctlen[i]; h=0.7/nd; if(h>1.0/l) h=1.0/l; for(i=0;ictbm[i],sq->ctlen[i]); if(dots) for(j=0;s[j];j++) if(s[j]==' ') s[j]='.'; ctext(cc,s,u,v+((i-(nd-1.0)/2.0)*h+0.42*h)*sc,h*sc,getfstyle(xr,yr),ocm); } } else { s[0]=0; for(i=0;ictbm[i],sq->ctlen[i]); l=strlen(s); if(l<1) l=1; if(dots&&l>1) for(i=0;i1) h=1.0/l; ctext(cc,s,u,v+0.42*h*sc,h*sc,getfstyle(xr,yr),ocm); } } // draw bars and edges // lf=1 draw entered letter also // ocm flag passed on to ctext static void drawsqfg(cairo_t*cc,int x,int y,int sq,int ba,int lf,int ocm,double textscale) { int b,d,f,m,xr,yr; // edges and bars b=0; for(d=0;d=2); tl=getmergegroupd(0,0,lx[0],ly[0],d); movetomgcentre(cc,lx[0],ly[0],d,tl); mgcentre(&u,&v,lx[0],ly[0],d,tl); for(k=1;k=5) { // potential for wrap-around? if(d==0&&(lx[k]=100 int i,j,l,md,nc,pd[MXSZ][MXSZ],vis[MXSZ][MXSZ],x,y,x0,y0; double sc[MXCL],u,v,xc[MXCL],yc[MXCL]; setlinecap(cc,1); memset(vis,0,sizeof(vis)); for(i=0;i=ndir[gtype]) refreshsel2(cc); cairo_destroy(cc); invaldaall(); } void refreshcur() { double sc,c=0,s=0,u,v; int x,y; static int ocx=0,ocy=0; cairo_t*cc; cc=cairo_create(sfc); setrgbacolor(cc,0.0,0.0,0.0,0.0); cairo_set_operator(cc,CAIRO_OPERATOR_SOURCE); cairo_paint(cc); cairo_set_operator(cc,CAIRO_OPERATOR_OVER); sc=getscale(curx,cury,0,1)*pxsq; setrgbacolor(cc,0.6,0.6,0.6,0.8); setlinewidth(cc,2); if(dirflbm,e++; setrgbcolor(cc,0.75,0.75,0.75); drawct(cc,x,y,&sq,1,1,1.0); } else if(c==' ') { m=e->flbm;k=cbits(m); // any info from filler? DEB4 printf("%16llx ",m); if(k==0) c='?'; // no feasible letters else if(k==1) c=ltochar[logbase2(m)]; // unique feasible letter else c=' '; s[0]=c;s[1]=0; setrgbcolor(cc,0.75,0.75,0.75); mgcentre(&u,&v,x,y,md,l); ctext(cc,s,u,v+0.3*sc,.7*sc,getfstyle(x,y),1); if(k>1&&k123) fprintf(fp,"&#%d;",u); else switch(u) { case'&': fprintf(fp,"&");break; case'\"':fprintf(fp,""");break; case'<': fprintf(fp,"<");break; case'>': fprintf(fp,">");break; default: fputc(u,fp);break; } } } // convert light/answer to string form for display void ansform(char*t0,int t0l,int ln,int wlen,unsigned int dmask) { struct answer*a; int em; em=lts[ln].em; if(em>0&&em<4) { // special entry method (but not jumble)? give grid form too strncpy(t0,lts[ln].s,wlen); strcpy(t0+wlen,": "); } else t0[0]=0; a=ansp[lts[ln].ans]; // negative ans values canno occur here for(;;) { if(a->cfdmask&dmask) { if((int)strlen(t0)+(int)strlen(a->cf)+2>t0l-LEMDESCLEN-10) {strcat(t0,"...");break;} strcat(t0,a->cf); if(a->acf) strcat(t0,", "); } if(!a->acf) break; a=a->acf; } if(em>0) strcat(t0,lemdesc[lts[ln].em]); } // Output answers in direction dir; if dir==-1 do VL:s // html=0: plain text // html=1: block HTML with enumeration // html=2: block HTML // html=3: table HTML // cf=1: word lists are valid, can be used for citation forms static void panswers(FILE*fp,int dir,int html,int cf) { int d,f,i,j,k,n,vlf; char s[SLEN]; char t[MXLE+1]; char t0[MXFL*3+100]; struct word*w; vlf=(dir==-1); // doing virtual lights? for(j=0;j<(vlf?1:height);j++) for(i=0;i<(vlf?1:width);i++) for(d=vlf?100:dir;d<(vlf?100+nvl:(dir+1));d++) { // loop over all possible answers if(!vlf&&!isstartoflight(i,j,d)) continue; f=getword(i,j,d,t); if(f<2) { if(f==-2) fprintf(fp,"(entry too long)\n"); continue; // silently skip other errors } f=cf; for(k=0;t[k];k++) if(t[k]==' ') t[k]='.',f=0; // f=1 now if we are to print citation forms n=vlf?d-100+1:gsq[i][j].number; // answer number if(n>0) sprintf(s,"%d",n); else strcpy(s,"-"); // unnumbered light if (html==0) fprintf(fp,"%s ",s); else if(html==1) fprintf(fp,"%s ",s); else if(html==2) fprintf(fp,"%s ",s); else if(html==3) fprintf(fp,"%s\n",s); if(f) { if(vlf) w=vls[d-100].w; else w=gsq[i][j].w[d]; } else w=0; if(w&&w->flistlen>0) { for(k=0;kflistlen;k++) { if(k>0) fprintf(fp,"; "); ansform(t0,sizeof(t0),w->flist[k],w->wlen,w->lp->dmask); if(html==0) fprintf(fp,"%s",t0); else escstr(fp,t0); } } else fprintf(fp,"%s",t); if (html==0) fprintf(fp," (%d)\n",(int)strlen(t)); else if(html==1) fprintf(fp," (%d)
\n",(int)strlen(t)); else if(html==2) fprintf(fp,"\n"); else if(html==3) fprintf(fp," (%d)\n",(int)strlen(t)); // answer and length } } // HTML export // f b0: include grid // f b1: include answers in grid // f b2: include numbers and marks in grid // f b3: include answers in table (to be edited into clues) // f b4: include answers in compact format // f b5: include link to grid image (given URL) // Thanks to Paul Boorer for CSS suggestions void a_exportgh(int f,char*url) { int c,cf,d,fl,i,j,i0,j0,k,l,md,n,u,v,x,y,bg,fg,mk,fs,fh; int de,nd; int hbawd; double textscale; FILE*fp; textscale=((f&6)==6)?0.8:1; // smaller text if we are doing numbers and entries hbawd=hpxsq/8; // a suitable bar width if(hbawd<3) hbawd=3; hbawd|=1; // ensure it is odd DEB1 printf("export HTML %s\n",filename); fp=fopen(filename,"w"); if(!fp) {fserror();return;} cf=!preexport(); // emit header and CSS fprintf(fp,"\ \n\ \n\ \n\ \n\ ",RELEASE); escstr(fp,titlebyauthor()); fprintf(fp,"\n"); if(f&1) { // need the styles for the grid fprintf(fp,"\n"); } fprintf(fp,"\n\n\n"); if(f&1) { fprintf(fp,"
\n"); fprintf(fp,"
\n",width*hpxsq+1,height*hpxsq+1); fprintf(fp,"
\n",width*hpxsq+1,height*hpxsq+1); for(j=0;j
\n",i*hpxsq+1,j*hpxsq+1);continue;} fprintf(fp,"
\n",i*hpxsq+1,j*hpxsq+1,bg,hpxsq); } for(j=0;j
\n",(i+1)*hpxsq-hbawd/2,j*hpxsq); else fprintf(fp,"
\n",(i+1)*hpxsq-hbawd/2,j*hpxsq); } if(isbar(i,j,1)) { if(isbar(i,j,2)) fprintf(fp,"
\n",i*hpxsq-hbawd/2,(j+1)*hpxsq-hbawd/2); else fprintf(fp,"
\n",i*hpxsq,(j+1)*hpxsq-hbawd/2); } if(isbar(i,j,2)) { if(isbar(i,j,3)) fprintf(fp,"
\n",i*hpxsq-hbawd/2,j*hpxsq-hbawd/2); else fprintf(fp,"
\n",i*hpxsq-hbawd/2,j*hpxsq); } if(isbar(i,j,3)) { if(isbar(i,j,0)) fprintf(fp,"
\n",i*hpxsq,j*hpxsq-hbawd/2); else fprintf(fp,"
\n",i*hpxsq,j*hpxsq-hbawd/2); } if(f&4) { char s[MXMK+1]; for(c=0;c<4;c++) { getmk(s,i,j,c); mk=getmkcol(i,j); if(!strcmp(s,"")) continue; if(!strcmp(s,"\\#")) { n=getnumber(i,j); if(n<=0) continue; sprintf(s,"%d",n); } switch(c) { case 0: fprintf(fp,"
%s
\n" ,i*hpxsq+4,j*hpxsq+1,mk,s); break; case 1: fprintf(fp,"
%s
\n",i*hpxsq+1,j*hpxsq+1,mk,s); break; case 2: fprintf(fp,"
%s
\n",i*hpxsq+1,j*hpxsq+hpxsq*2/3-4,mk,s); break; case 3: fprintf(fp,"
%s
\n" ,i*hpxsq+4,j*hpxsq+hpxsq*2/3-4,mk,s); break; } } } } for(j=0;j2) stepforw(&i0,&j0,md),l-=2; if((md==0&&i0==width-1)||(md==1&&j0==height-1)) l=1; x=((i0*2+(md==0)*(l-1))*hpxsq)/2+1; y=((j0*2+(md==1)*(l-1))*hpxsq)/2+hpxsq/2; de=getdech(i,j); nd=ndir[gtype]; if(de==0) nd=1,de=1; if(de==2) { l=3; for(k=0;k<2;k++) if(gsq[i][j].ctlen[k]>l) l=gsq[i][j].ctlen[k]; fh=(int)floor(hpxsq*textscale/l+.5); abmstodispstr(s,gsq[i][j].ctbm[0],gsq[i][j].ctlen[0]); fprintf(fp,"
%s%s%s%s%s
\n", x,y-fh,fg,fh, (fs&1)?"":"",(fs&2)?"":"",s,(fs&2)?"":"",(fs&1)?"":""); abmstodispstr(s,gsq[i][j].ctbm[1],gsq[i][j].ctlen[1]); fprintf(fp,"
%s%s%s%s%s
\n", x,y,fg,fh, (fs&1)?"":"",(fs&2)?"":"",s,(fs&2)?"":"",(fs&1)?"":""); } else { s[0]=0; for(k=0;k%s%s%s%s%s\n", x,y-fh/2,fg,fh, (fs&1)?"":"",(fs&2)?"":"",s,(fs&2)?"":"",(fs&1)?"":""); } } } for(j=0;j<=height;j++) for(i=0,v=0;i<=width;i++) { if(j==0) u=!ismerge(i,j ,3); else u=!ismerge(i,j-1,1); u&=sqexists(i,j)|sqexists(i,j-1); if(u==0&&v>0) fprintf(fp,"
\n",(i-v)*hpxsq,j*hpxsq,v*hpxsq+1); if(u==0) v=0; else v++; } for(i=0;i<=width;i++) for(j=0,v=0;j<=height;j++) { if(i==0) u=!ismerge(i ,j,2); else u=!ismerge(i-1,j,0); u&=sqexists(i,j)|sqexists(i-1,j); if(u==0&&v>0) fprintf(fp,"
\n",i*hpxsq,(j-v)*hpxsq,v*hpxsq+1); if(u==0) v=0; else v++; } fprintf(fp,"
\n"); } if(f&0x20) { fprintf(fp,"
\n"); fprintf(fp,"\"grid\n",url); fprintf(fp,"
\n"); } if(f&8) { // print table of `clues' fprintf(fp,"
\n"); for(d=0;d%s\n",96/ndir[gtype],dname[gtype][d]); if(d \n"); } fprintf(fp,"\n"); for(d=0;d
\n"); panswers(fp,d,3,cf); fprintf(fp,"
\n"); if(d \n"); } fprintf(fp,"\n"); if(nvl>0) { fprintf(fp,"Other\n",ndir[gtype]*2-1); fprintf(fp,"\n",ndir[gtype]*2-1); panswers(fp,-1,2,cf); fprintf(fp,"
\n"); } fprintf(fp,"
\n"); } if(f&16) { // print answers in condensed form for(d=0;d%s:\n",dname[gtype][d]); panswers(fp,d,2,cf); fprintf(fp,"
\n"); } if(nvl>0) { fprintf(fp,"Other:\n"); panswers(fp,-1,2,cf); fprintf(fp,"
\n"); } } fprintf(fp,"\n"); if(ferror(fp)) fserror(); if(fclose(fp)) fserror(); postexport(); } // GRID AS EPS OR PNG static cairo_status_t filewrite(FILE*fp,unsigned char*p,unsigned int n) { return (fwrite(p,1,n,fp)==n)?CAIRO_STATUS_SUCCESS:CAIRO_STATUS_WRITE_ERROR; } // f0 b1: include answers in grid // f0 b2: include numbers and marks in grid // f1: 0=EPS 1=SVG 2=PNG void a_exportg(char*fn,int f0,int f1) { cairo_surface_t*sf=0; cairo_t*cc; int i,j,sq=0,ba,px,py; char s[1000]; double textscale; FILE*fp=0; DEB1 printf("a_exportg(\"%s\",%d,%d)\n",fn,f0,f1); switch(f1) { case 0: sq=eptsq; break; case 1: sq=eptsq; break; case 2: sq=hpxsq; break; } textscale=((f0&6)==6)?0.8:1; // smaller text if we are doing numbers and entries ba=sq/8; // a suitable bar width if(ba<2) ba=2; px=gwidth(sq,ba); py=gheight(sq,ba); switch(f1) { case 0: fp=fopen(fn,"w"); if(!fp) {fserror();return;} sf=cairo_ps_surface_create_for_stream((cairo_write_func_t)filewrite,fp,px,py); cairo_ps_surface_restrict_to_level(sf,CAIRO_PS_LEVEL_2); cairo_ps_surface_set_eps(sf,1); sprintf(s,"%%%%Title: %s generated by Qxw",fn); cairo_ps_surface_dsc_comment(sf,s); break; case 1: fp=fopen(fn,"w"); if(!fp) {fserror();return;} sf=cairo_svg_surface_create_for_stream((cairo_write_func_t)filewrite,fp,px,py); cairo_svg_surface_restrict_to_version(sf,CAIRO_SVG_VERSION_1_1); break; case 2: sf=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,px,py); break; } cc=cairo_create(sf); if(f1==2) { cairo_set_source_rgb(cc,1,1,1); // flood PNG to white to avoid nasty transparency effect in merged cells cairo_paint(cc); } cairo_translate(cc,ba-.5,ba-.5); cairo_scale(cc,sq,sq); for(j=0;j\n":"\n"); for(d=0;d%s
\n\n",dname[gtype][d]); else fprintf(fp,"\n%s\n\n",dname[gtype][d]); panswers(fp,d,f,cf); } if(nvl>0) { if(f) fprintf(fp,"\nOther
\n\n"); else fprintf(fp,"\nOther\n\n"); panswers(fp,-1,f,cf); } if(f) fprintf(fp,"\n"); if(ferror(fp)) fserror(); if(fclose(fp)) fserror(); postexport(); } // COMBINED HTML AND PNG // f: 0=puzzle, 1=solution // f0: f0 flags for exportg // f1: 1=SVG, 2=PNG void a_exporthp(int f,int f0,int f1) {char url[SLEN+200]; strcpy(url,filename); strcat(url,"_files"); #ifdef _WIN32 // Windows version of mkdir() _mkdir(url); #else mkdir(url,0777); #endif strcat(url,f?"/solution":"/grid"); strcat(url,f1==1?".svg":".png"); a_exportgh(f?0x30:0x28,url); a_exportg(url,f0,f1); } qxw-20140331/qxw.c0000640000175000017500000017057312305367347011406 0ustar momo// $Id: qxw.c 553 2014-03-04 15:19:02Z mo $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant http://www.quinapalus.com E-mail: qxw@quinapalus.com This file is part of Qxw. Qxw is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Qxw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Qxw. If not, see or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #ifdef _WIN32 #include #include "pfgetopt.h" #else #include #include #endif #include #include #include #include #include #include #include "common.h" #include "qxw.h" #include "filler.h" #include "dicts.h" #include "gui.h" #include "draw.h" // GLOBAL PARAMETERS // these are saved: int width=12,height=12; // grid size int gtype=0; // grid type: 0=square, 1=hexH, 2=hexV, 3=circleA (edge at 12 o'clock), 4=circleB (cell at 12 o'clock) // 5=cylinder L-R, 6=cylinder T-B, 7=Moebius L-R, 8=Moebius T-B, 9=torus int symmr=2,symmm=0,symmd=0; // symmetry mode flags (rotation, mirror, up-and-down/left-and-right) int nvl=0; // number of virtual lights struct sprop dsp; // default square properties struct lprop dlp; // default light properties char gtitle[SLEN]=""; char gauthor[SLEN]=""; // these are not saved: int debug=0; // debug level int curx=0,cury=0,dir=0; // cursor position, and direction: 0=to right, 1=down int unsaved=0; // edited-since-save flag int cwperm[NL-1]; int ndir[NGTYPE]={2,3,3,2,2, 2,2,2,2,2}; // number of directions per grid type int gshape[NGTYPE]={0,1,2,3,4,0,0,0,0,0}; // basic shape of grid int dhflip[NGTYPE][MAXNDIR*2]={ {2,1,0,3}, {4,3,2,1,0,5}, {3,2,1,0,5,4}, {2,1,0,3}, {2,1,0,3}, {2,1,0,3}, {2,1,0,3}, {2,1,0,3}, {2,1,0,3}, {2,1,0,3} }; int dvflip[NGTYPE][MAXNDIR*2]={ {0,3,2,1}, {1,0,5,4,3,2}, {0,5,4,3,2,1}, {2,1,0,3}, {2,1,0,3}, {0,3,2,1}, {0,3,2,1}, {0,3,2,1}, {0,3,2,1}, {0,3,2,1} }; static unsigned char log2lut[65536]; int cbits(ABM x) {ABM i; int j; for(i=1,j=0;i>16)&0xffff; if(u) return log2lut[u]+16; u=(x>>32)&0xffff; if(u) return log2lut[u]+32; u=(x>>48)&0xffff; if(u) return log2lut[u]+48; return -1; } int*llistp=0; // ptr to matching lights int llistn=0; // number of matching lights unsigned int llistdm=0; // dictionary mask applicable to matching lights int llistwlen=0; // word length (less tags if any) applicable to matching lights int llistem=0; // entry method mask applicable to matching lights int*llist=0; // buffer for light index list // GRID struct square gsq[MXSZ][MXSZ]; struct vl vls[NVL]; int getnumber(int x,int y) { return gsq[x][y].number; } int getflags(int x,int y) {int xr,yr; getmergerep(&xr,&yr,x,y); return gsq[xr][yr].fl; } int getbgcol(int x,int y) {int xr,yr; getmergerep(&xr,&yr,x,y); return (gsq[xr][yr].sp.spor?gsq[xr][yr].sp.bgcol:dsp.bgcol)&0xffffff; } int getfgcol(int x,int y) {int xr,yr; getmergerep(&xr,&yr,x,y); return (gsq[xr][yr].sp.spor?gsq[xr][yr].sp.fgcol:dsp.fgcol)&0xffffff; } int getmkcol(int x,int y) {int xr,yr; getmergerep(&xr,&yr,x,y); return (gsq[xr][yr].sp.spor?gsq[xr][yr].sp.mkcol:dsp.mkcol)&0xffffff; } int getfstyle(int x,int y) {int xr,yr; getmergerep(&xr,&yr,x,y); return gsq[xr][yr].sp.spor?gsq[xr][yr].sp.fstyle:dsp.fstyle; } int getdech(int x,int y) {int xr,yr; getmergerep(&xr,&yr,x,y); return gsq[xr][yr].sp.spor?gsq[xr][yr].sp.dech:dsp.dech; } void getmk(char*s,int x,int y,int c) {int d,nd,nd2,xr,yr; char*t; strcpy(s,""); getmergerep(&xr,&yr,x,y); t=gsq[xr][yr].sp.spor?gsq[xr][yr].sp.mk[c]:dsp.mk[c]; if(!strcmp(t,"\\#")) goto ew0; // if number, put it wherever necessary d=getmergedir(x,y); // otherwise only in extreme corners of merge group nd=ndir[gtype]; nd2=nd+nd; if(d>=0) { if(!ismerge(x,y,d )&&(c-1-d+nd2)%nd2< nd) goto ew0; // end of group d=0:1,2/d=1:2,3 if(!ismerge(x,y,d+nd)&&(c-1-d+nd2)%nd2>=nd) goto ew0; else return; } ew0: strcpy(s,t); } // move backwards one cell in direction d void stepback(int*x,int*y,int d) { // printf("stepback(%d,%d,%d)\n",*x,*y,d); if(d>=ndir[gtype]) {stepforw(x,y,d-ndir[gtype]); return;} if(gtype==1) { switch(d) { case 0: if(((*x)&1)==1) (*y)++; (*x)--; break; case 1: if(((*x)&1)==0) (*y)--; (*x)--; break; case 2: (*y)--;break; } return; } if(gtype==2) { switch(d) { case 2: if(((*y)&1)==1) (*x)++; (*y)--; break; case 1: if(((*y)&1)==0) (*x)--; (*y)--; break; case 0: (*x)--;break; } return; } // rectangular grid cases if(d) (*y)--; else (*x)--; if(gtype==3||gtype==4||gtype==5||gtype==7||gtype==9) { // loop in x direction if(*x<0) { *x+=width; if(gtype==7) *y=height-1-*y; } } if(gtype==6||gtype==8||gtype==9) { // loop in y direction if(*y<0) { *y+=height; if(gtype==8) *x=width-1-*x; } } // printf("=(%d,%d)\n",*x,*y); } // move forwards one cell in direction d void stepforw(int*x,int*y,int d) { // printf("stepforw(%d,%d,%d)\n",*x,*y,d); if(d>=ndir[gtype]) {stepback(x,y,d-ndir[gtype]); return;} if(gtype==1) { switch(d) { case 0: (*x)++; if(((*x)&1)==1) (*y)--; break; case 1: (*x)++; if(((*x)&1)==0) (*y)++; break; case 2: (*y)++;break; } return; } if(gtype==2) { switch(d) { case 2: (*y)++; if(((*y)&1)==1) (*x)--; break; case 1: (*y)++; if(((*y)&1)==0) (*x)++; break; case 0: (*x)++;break; } return; } // rectangular grid cases if(d) (*y)++; else (*x)++; if(gtype==3||gtype==4||gtype==5||gtype==7||gtype==9) { // loop in x direction if(*x>=width) { *x-=width; if(gtype==7) *y=height-1-*y; } } if(gtype==6||gtype==8||gtype==9) { // loop in y direction if(*y>=height) { *y-=height; if(gtype==8) *x=width-1-*x; } } // printf("=(%d,%d)\n",*x,*y); } int isingrid(int x,int y) { if(x<0||y<0||x>=width||y>=height) return 0; if(gshape[gtype]==1&&ODD(width )&&ODD(x)&&y==height-1) return 0; if(gshape[gtype]==2&&ODD(height)&&ODD(y)&&x==width -1) return 0; return 1; } int isclear(int x,int y) { if(!isingrid(x,y)) return 0; if(gsq[x][y].fl&0x09) return 0; return 1; } int isbar(int x,int y,int d) {int u; if(d>=ndir[gtype]) {d-=ndir[gtype];stepback(&x,&y,d);} if(!isingrid(x,y)) return 0; u=(gsq[x][y].bars>>d)&1; // DEB2 printf(" x=%d y=%d d=%d u=%d g[x,y].bars=%08x\n",x,y,d,u,gsq[x][y].bars); stepforw(&x,&y,d); if(!isingrid(x,y)) return 0; return u; } int ismerge(int x,int y,int d) {int u; if(d>=ndir[gtype]) {d-=ndir[gtype];stepback(&x,&y,d);} if(!isingrid(x,y)) return 0; u=(gsq[x][y].merge>>d)&1; stepforw(&x,&y,d); if(!isingrid(x,y)) return 0; return u; } int sqexists(int i,int j) { // is a square within the grid (and not cut out)? if(!isingrid(i,j)) return 0; return !(gsq[i][j].fl&8); } // is a step backwards clear? (not obstructed by a bar, block, cutout or edge of grid) int clearbefore(int x,int y,int d) {int tx,ty; tx=x;ty=y;stepback(&tx,&ty,d); if(!isingrid(tx,ty)) return 0; if(!isclear(tx,ty)) return 0; if(isbar(tx,ty,d)) return 0; return 1; } // is a step forwards clear? int clearafter(int x,int y,int d) {int tx,ty; tx=x;ty=y;stepforw(&tx,&ty,d); if(!isingrid(tx,ty)) return 0; if(!isclear(tx,ty)) return 0; if(isbar(x,y,d)) return 0; return 1; } // move forwards one (merged) cell in direction d static void stepforwm(int*x,int*y,int d) {int x0,y0; x0=*x;y0=*y; while(ismerge(*x,*y,d)) {stepforw(x,y,d);if(*x==x0&&*y==y0) return;} stepforw(x,y,d); } // is a step forwards clear? static int clearafterm(int x,int y,int d) {int tx,ty; tx=x;ty=y;stepforwm(&tx,&ty,d); if(x==tx&&y==ty) return 0; if(!isingrid(tx,ty)) return 0; if(!isclear(tx,ty)) return 0; stepback(&tx,&ty,d); if(isbar(tx,ty,d)) return 0; return 1; } int stepbackifingrid (int*x,int*y,int d) {int tx,ty; tx=*x;ty=*y; stepback (&tx,&ty,d);if(!isingrid(tx,ty)) return 0; *x=tx;*y=ty; return 1;} //static int stepforwifingrid (int*x,int*y,int d) {int tx,ty; tx=*x;ty=*y; stepforw (&tx,&ty,d);if(!isingrid(tx,ty)) return 0; *x=tx;*y=ty; return 1;} // not currently used int stepforwmifingrid(int*x,int*y,int d) {int tx,ty; tx=*x;ty=*y; stepforwm(&tx,&ty,d);if(!isingrid(tx,ty)) return 0; *x=tx;*y=ty; return 1;} void getmergerepd(int*mx,int*my,int x,int y,int d) {int x0,y0; // find merge representative, direction d (0<=d=0); return getmergegroupd(gx,gy,x,y,d); } int isstartoflight(int x,int y,int d) { if(!isclear(x,y)) return 0; if(clearbefore(x,y,d)) return 0; if(clearafterm(x,y,d)) return 1; return 0; } // returns: // -1: looped, no light found // 0: blocked, no light found // 1: light found, start (not mergerep'ed) stored in *lx,*ly static int getstartoflight(int*lx,int*ly,int x,int y,int d) {int x0,y0; if(!isclear(x,y)) return 0; x0=x;y0=y; while(clearbefore(x,y,d)) { stepback(&x,&y,d); // to start of light if(x==x0&&y==y0) return -1; // loop found } *lx=x;*ly=y; return 1; } // is light selected? int issellight(int x,int y,int d) {int l,lx,ly; l=getstartoflight(&lx,&ly,x,y,d); if(l<1) return 0; if(!isstartoflight(lx,ly,d)) return 0; // not actually a light return (gsq[lx][ly].dsel>>d)&1; } // set selected flag on a light to k void sellight(int x,int y,int d,int k) {int l,lx,ly; DEB1 printf("sellight(%d,%d,%d,%d)\n",x,y,d,k); l=getstartoflight(&lx,&ly,x,y,d); if(l<1) return; if(!isstartoflight(lx,ly,d)) return; // not actually a light gsq[lx][ly].dsel=(gsq[lx][ly].dsel&(~(1<=2: length of light (lx[0..n-1], ly[0..n-1] set), can be up to MXCL for VL, up to MXSZ*2 in Mobius case // if d>=100 returns data for VL #d-100 int getlightd(int*lx,int*ly,int x,int y,int d) {int i,j; if(d>=100) { d-=100; for(i=0,j=0;i=100 returns data for VL #d-100 // errors as for getlightd() int getlight(int*lx,int*ly,int x,int y,int d) {int i,l; l=getlightd(lx,ly,x,y,d); for(i=0;i=100 returns data for VL #d-100 // returns -2 if light overflows plus errors as for getlightd() // lp: bitmap ptrs (<=MXLE) // lx: mergerep square x (<=MXLE) // ly: mergerep square y (<=MXLE) // ls: index of contributing string (<=MXLE) // lo: offset in contributing string (<=MXLE) // le: entry number (<=MXLE) static int getlightdat(ABM**lp,int*lx,int*ly,int*ls,int*lo,int*le,int x0,int y0,int d) {int c,e,i,j,k,l,m,tx[MXCL],ty[MXCL],x,y; l=getlight(tx,ty,x0,y0,d); if(l<1) return l; for(i=0,m=0;i=ndir[gtype]) {d-=ndir[gtype];stepback(&x,&y,d);} if(!isingrid(x,y)) return; if(k) k=1; gsq[x][y].merge&=~(1<=ndir[gtype]) {d-=ndir[gtype];stepback(&x,&y,d);} if(!isingrid(x,y)) return; if(k) k=1; gsq[x][y].bars &=~(1<=width) i=(i-width)|1;} // special case for hexH grid else i=i0; if(isclear(i,j)) { if(isownmergerep(i,j)&&(gsq[i][j].sp.spor?gsq[i][j].sp.ten:dsp.ten)) gsq[i][j].goi=goi++; for(d=0;ddnran) {gsq[i][j].number=num++; break;} } } } } // add word to filler data, starting at (i,j) in direction d // if d>=100, add virtual light #d-100 // return: // 0: OK // 1: too long // 2: ignored as dmask is zero static int addwordfd(int i,int j,int d) { int f,k,l,le[MXLE],lx[MXLE],ly[MXLE]; ABM*lcp[MXLE]; struct lprop*lp; if(d<100) lp=gsq[i][j].lp[d].lpor?&gsq[i][j].lp[d]:&dlp; else lp=&vls[d-100].lp; if(lp->dmask==0) return 2; // skip if dictionary mask empty l=getlightdat(lcp,lx,ly,0,0,le,i,j,d); if(l<1) return 1; // too long: ignore DEB8 printf("new word %d: l=%d\n",nw,l); f=0; for(k=0;kten) ntw++; return 0; } static void initstructs() {int i,j,k,d; nw=0; ntw=0; nc=0; ne=0; ne0=0; for(i=0;ichecking++; st_ce=0; for(i=0;i1) st_ce++; // number of checked squares st_sc=0;st_2u=0;st_3u=0; for(i=0;ichecking<=1); if(!unch[j]) k++; // count checked cells in word if(j>0&&unch[j]&&unch[j-1] ) v|=1; // double unch? if(j>1&&unch[j]&&unch[j-1]&&unch[j-2]) v|=2; // triple+ unch? } if(v&1) st_2u++; // double+ unch? if(v&2) st_3u++; // triple+ unch? // k is now number of checked cells (out of l total) if( k *100l*maxcheck) st_locc[l]++,v|=8; st_lsc[l]+=k; if(kst_lmxc[l]) st_lmxc[l]=k; st_sc+=k; if(words[i].ldirten) { if(treatorder[i]>0) entries[ne].flbm=treatcstr[i][l]; else entries[ne].flbm=ABM_ALL; entries[ne].sel=1; // make sure we fill even if only in "fill selection" mode entries[ne].checking=2; if(l==MXFL) st_tmtlf=1; // too many treated lights for filler to use discretion if(l0) nw++; // don't create an empty word, and don't create one for first come first served entry } } donumbers(); stats_upd(); // refresh statistics window if it is in view DEB8 printf("nw=%d ntw=%d nc=%d ne=%d\n",nw,ntw,nc,ne); return 0; } // symmetry functions // call f on (x,y) if in range static void symmdo5(void f(int,int,int,int),int k,int x,int y,int d) { if(isingrid(x,y)) f(k,x,y,d); } // call symmdo5 on (x,y) and other square implied by up-and-down symmetry flag (if any) static void symmdo4(void f(int,int,int,int),int k,int x,int y,int d) { if(symmd&2) switch(gshape[gtype]) { case 0: case 1: case 2: symmdo5(f,k,x,(y+(height+1)/2)%((height+1)&~1),d);break; case 3: case 4: break; // not for circular grids } symmdo5(f,k,x,y,d); } // call symmdo4 on (x,y) and other square implied by left-and-right symmetry flag (if any) static void symmdo3(void f(int,int,int,int),int k,int x,int y,int d) { if(symmd&1) switch(gshape[gtype]) { case 0: case 1: case 2: symmdo4(f,k,(x+(width+1)/2)%((width+1)&~1),y,d);break; case 3: case 4: break; // not for circular grids } symmdo4(f,k,x,y,d); } // call symmdo3 on (x,y) and other square implied by vertical mirror flag (if any) static void symmdo2(void f(int,int,int,int),int k,int x,int y,int d) {int h; h=height; if(gshape[gtype]==1&&ODD(width)&&ODD(x)) h--; if(symmm&2) switch(gshape[gtype]) { case 0: case 1: case 2: symmdo3(f,k,x,h-y-1,dvflip[gtype][d]);break; case 3: if(width%2) break; // not for odd-size circular grids symmdo3(f,k,(width*3/2-x-1)%width,y,dvflip[gtype][d]); break; case 4: if(width%2) break; // not for odd-size circular grids symmdo3(f,k,(width*3/2-x)%width,y,dvflip[gtype][d]); break; } symmdo3(f,k,x,y,d); } // call symmdo2 on (x,y) and other square implied by horizontal mirror flag (if any) static void symmdo1(void f(int,int,int,int),int k,int x,int y,int d) {int w; w=width; if(gshape[gtype]==2&&ODD(height)&&ODD(y)) w--; if(symmm&1) switch(gtype) { case 0: case 1: case 2: case 3: symmdo2(f,k,w-x-1,y,dhflip[gtype][d]);break; case 4: symmdo2(f,k,(w-x)%w,y,dhflip[gtype][d]);break; break; } symmdo2(f,k,x,y,d); } // get centre of rotation in 6x h-units, 4x v-units (gshape[gtype]=1; mutatis mutandis for gshape[gtype]=2) static void getcrot(int*cx,int*cy) {int w,h; w=width;h=height; if(gshape[gtype]==1) { *cx=(w-1)*3; if(EVEN(w)) *cy=h*2-1; else *cy=h*2-2; } else { *cy=(h-1)*3; if(EVEN(h)) *cx=w*2-1; else *cx=w*2-2; } } // rotate by d/6 of a revolution in 6x h-units, 4x v-units (gshape[gtype]=1; mutatis mutandis for gshape[gtype]=2) static void rot6(int*x0,int*y0,int x,int y,int d) {int u,v; u=1;v=1; switch(d) { case 0:u= x*2 ;v= 2*y;break; case 1:u= x -3*y;v= x+ y;break; case 2:u=-x -3*y;v= x- y;break; case 3:u=-x*2 ;v= -2*y;break; case 4:u=-x +3*y;v=-x- y;break; case 5:u= x +3*y;v=-x+ y;break; } assert(EVEN(u));assert(EVEN(v)); *x0=u/2;*y0=v/2; } // call f on (x,y) and any other squares implied by symmetry flags by // calling symmdo1 on (x,y) and any other squares implied by rotational symmetry flags void symmdo(void f(int,int,int,int),int k,int x,int y,int d) {int i,mx,my,x0,y0; switch(gshape[gtype]) { case 0: switch(symmr) { case 4:symmdo1(f,k,width-y-1,x, (d+1)%4); symmdo1(f,k,y, height-x-1,(d+3)%4); case 2:symmdo1(f,k,width-x-1,height-y-1,(d+2)%4); case 1:symmdo1(f,k,x, y, d); } break; case 1: getcrot(&mx,&my); y=y*4+(x&1)*2-my;x=x*6-mx; for(i=5;i>=0;i--) { if((i*symmr)%6==0) { rot6(&x0,&y0,x,y,i); x0+=mx;y0+=my; assert(x0%6==0); x0/=6; y0-=(x0&1)*2; assert(y0%4==0); y0/=4; symmdo1(f,k,x0,y0,(d+i)%6); } } break; case 2: getcrot(&mx,&my); x=x*4+(y&1)*2-mx;y=y*6-my; for(i=5;i>=0;i--) { if((i*symmr)%6==0) { rot6(&y0,&x0,y,x,i); x0+=mx;y0+=my; assert(y0%6==0); y0/=6; x0-=(y0&1)*2; assert(x0%4==0); x0/=4; symmdo1(f,k,x0,y0,(d-i+6)%6); } } break; case 3: case 4: if(symmr==0) break; // assertion for(i=symmr-1;i>=0;i--) symmdo1(f,k,(x+i*width/symmr)%width,y,d); break; } } // basic grid editing commands (candidates for f() in symmdo above) void a_editblock (int k,int x,int y,int d) {int l,gx[MXCL],gy[MXCL]; l=getmergegroup(gx,gy,x,y); gsq[x][y].fl=(gsq[x][y].fl&0x06)|1; demerge(x,y); refreshsqlist(l,gx,gy); } void a_editempty (int k,int x,int y,int d) { gsq[x][y].fl= gsq[x][y].fl&0x16; refreshsqmg(x,y); } void a_editcutout(int k,int x,int y,int d) {int l,gx[MXCL],gy[MXCL]; l=getmergegroup(gx,gy,x,y); gsq[x][y].fl=(gsq[x][y].fl&0x06)|8; demerge(x,y); refreshsqlist(l,gx,gy); } // set bar state in direction d to k void a_editbar(int k,int x,int y,int d) {int tx,ty; if(!isingrid(x,y)) return; tx=x;ty=y; stepforw(&tx,&ty,d); // printf("<%d,%d %d,%d %d>\n",x,y,tx,ty,d); if(!isingrid(tx,ty)) return; setbars(x,y,d,k); refreshsqmg(x,y); refreshsqmg(tx,ty); donumbers(); } // set merge state in direction d to k, deal with consequences void a_editmerge(int k,int x,int y,int d) {int f,i,l,tl,tx,ty,gx[MXCL],gy[MXCL],tgx[MXCL],tgy[MXCL]; if(!isclear(x,y)) return; tx=x;ty=y; stepforw(&tx,&ty,d); if(!isclear(tx,ty)) return; l=getmergegroup(gx,gy,x,y); tl=getmergegroup(tgx,tgy,tx,ty); if(k) for(i=0;ipw_dir)>SLEN-20) return; strcpy(s,p->pw_dir); strcat(s,"/.qxw/preferences"); #endif DEB1 printf("loadprefs %s\n",s); fp=fopen(s,"r");if(!fp) return; while(fgets(s,SLEN,fp)) { if(sscanf(s,"%s %d",t,&u)==2) { for(i=0;iprefmaxv[i]) u=prefmaxv[i]; prefdata[i]=u; } } } fclose(fp); } // write preferences to file // fail silently void saveprefs(void) {FILE*fp; int i; char s[SLEN]; #ifdef _WIN32 // Preferences in app data folder if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, s))) { strcat(s,"\\Qxw"); _mkdir(s); strcat(s,"\\Qxw.ini"); } else return; #else struct passwd*p; p=getpwuid(getuid()); if(!p) return; if(strlen(p->pw_dir)>SLEN-20) return; strcpy(s,p->pw_dir); strcat(s,"/.qxw"); mkdir(s,0777); strcat(s,"/preferences"); #endif DEB1 printf("saveprefs %s\n",s); fp=fopen(s,"w");if(!fp) return; for(i=0;i=0x20&&*s<0x7f) *t++=*s; *t=0; } // as abmtocs below, but just do letters or digits char*abmtocs0(char*s,ABM b) { ABM b0,b1; char c0,c1; while(b) { b0=b&~(b-1); // bottom 1 b1=b&~(b+b0); // bottom run of 1:s c0=ltochar[logbase2(b0)]; c1=ltochar[logbase2(b0+b1)-1]; *s++=c0; if(c0!=c1) { if(c1-c0>1) *s++='-'; *s++=c1; } b&=~b1; } return s; } // convert ABM to choice string of the form [a-eghq-z0-39], using inverting notation if inv==1 and // including "-" if dash==1 void abmtocs(char*s,ABM b,int inv,int dash) { *s++='['; if(inv) *s++='^',b^=dash?ABM_ALL:ABM_ALNUM; s=abmtocs0(s,b&ABM_LET); s=abmtocs0(s,b&ABM_NUM); if(dash&&(b&ABM_DASH)) *s++='-'; *s++=']'; *s++=0; } // convert ABM to string of the form "s" or "[aeiou]" or whatever // NL+4 is a very safe upper bound on the resulting length void abmtostr(char*s,ABM b,int dash) { char s0[NL+4],s1[NL+4]; b&=dash?ABM_ALL:ABM_ALNUM; if(b==ABM_ALL) {strcpy(s,"?"); return;} if(b==ABM_ALNUM) {strcpy(s,"."); return;} if(b==ABM_VOW) {strcpy(s,"@"); return;} if(b==ABM_CON) {strcpy(s,"#"); return;} if(onebit(b)) {s[0]=ltochar[logbase2(b)]; s[1]=0; return;} abmtocs(s0,b,0,dash); abmtocs(s1,b,1,dash); if(strlen(s0)<=strlen(s1)) strcpy(s,s0); // choose shorter of the two representations else strcpy(s,s1); } // convert sequence of ABMs to string in compact format suitable for display: concantenates results of abmtoechar() void abmstodispstr(char*s,ABM*b,int l) {int i; for(i=0;ic0) c1=c; else c0=c; s+=2; } for(c=c0;c<=c1;c++) b|=1ULL<bgcol=0xffffff; p->fgcol=0x000000; p->mkcol=0x000000; p->fstyle=0; p->dech=0; p->ten=0; // for(k=0;kmk[k],"%c",k+'a'); // for testing for(k=0;kmk[k],defaultmk(k)); p->spor=0; } void resetlp(struct lprop*p) { p->dmask=1; p->emask=EM_FWD; p->ten=0; p->dnran=0; p->lpor=0; } static void resetstate(void) {int i,j,k,u0,u1; for(i=0;ibgcol&=0xffffff; sp->fgcol&=0xffffff; sp->mkcol&=0xffffff; // if(sp->fstyle<0) sp->fstyle=0; if(sp->fstyle>3) sp->fstyle=3; sp->ten=!!sp->ten; // if(sp->dech<0) sp->dech=0; if(sp->dech>2) sp->dech=2; sp->spor=!!sp->spor; for(k=0;kmk[k]); } // tidy up light properties structure static void fixlp(struct lprop*lp) { lp->dmask&=(1<emask&=(1<emask==0) lp->emask=EM_FWD; lp->ten=!!lp->ten; lp->lpor=!!lp->lpor; lp->dnran=!!lp->dnran; } // FILE SAVE/LOAD // flags b23..16: grid width // flags b15..8: grid height // flags b7..0: grain void a_filenew(int flags) { char*p; int u,v,x,y; filler_stop(); resetstate(); u=(flags>>16)&0xff; v=(flags>>8)&0xff; if(u>0&&u<=MXSZ&&v>0&&vpw_dir)>SLEN-20) strcpy(filenamebase,""); else strcpy(filenamebase,pw->pw_dir),strcat(filenamebase,"/");; #endif } else { // we have a path to start from p=strrchr(filenamebase,DIR_SEP_CHAR); if(p) strcpy(p,DIR_SEP_STR); else strcpy(filenamebase,""); } strcat(filenamebase,"untitled"); havesavefn=0; donumbers(); undo_push(); unsaved=0; } #define SAVE_VERSION 3 #define NEXTL {if(!fgets(s,SLEN*4-1,fp)) goto ew3; l=strlen(s); while(l>0&&!isprint(s[l-1])) s[--l]='\0';} // load state from file void a_load(void) { int d,i,j,k,l,n,u,t0,t1,t2,t3,b,m,f,wf; char *p,s[SLEN*4],s0[SLEN*4],*t,c; struct sprop sp; struct lprop lp; FILE*fp; filler_stop(); DEB1 printf("filler stopped: loading\n"); setfilenamebase(filename); fp=fopen(filename,"r"); if(!fp) {fserror();return;} resetstate(); *gtitle=0; *gauthor=0; NEXTL; wf=0; if(strncmp(s,"#QXW2",5)) { // LEGACY LOAD gtype=0; if(sscanf(s,"%d %d %d %d %d\n",&width,&height,&symmr,&symmm,&symmd)!=5) goto ew1; if(width<1||width>MXSZ|| // validate basic parameters height<1||height>MXSZ|| symmr<0||symmr>2|| symmm<0||symmm>3|| symmd<0||symmd>3) goto ew1; if (symmr==1) symmr=2; else if(symmr==2) symmr=4; else symmr=1; draw_init(); for(j=0;j31) goto ew1; gsq[i][j].fl=u&0x09; gsq[i][j].bars=(u>>1)&3; gsq[i][j].merge=0; } } for(j=0;j'Z')&&(c<'0'||c>'9')&&c!=' ') goto ew1; gsq[i][j].ctbm[0][0]=chartoabm[(int)c]; } if(fgetc(fp)!='\n') goto ew1; } } else { // #QXW2 load if(s[5]=='v'&&isdigit(s[6])&&atoi(s+6)>SAVE_VERSION) wf=1; // check if the file was saved using a newer version NEXTL; if(sscanf(s,"GP %d %d %d %d %d %d\n",>ype,&width,&height,&symmr,&symmm,&symmd)!=6) goto ew1; if(gtype<0||gtype>=NGTYPE|| // validate basic parameters width<1||width>MXSZ|| height<1||height>MXSZ) goto ew1; if(symmr<1||symmr>12) symmr=1; if(symmm<0||symmm>3) symmm=0; if(symmd<0||symmd>3) symmd=0; if((symmr&symmrmask())==0) symmr=1; draw_init(); NEXTL; if(strcmp(s,"TTL")) goto ew1; NEXTL; if(s[0]!='+') goto ew1; strncpy(gtitle,s+1,SLEN-1); gtitle[SLEN-1]=0; make7bitclean(gtitle); NEXTL; if(strcmp(s,"AUT")) goto ew1; NEXTL; if(s[0]!='+') goto ew1; strncpy(gauthor,s+1,SLEN-1); gauthor[SLEN-1]=0; make7bitclean(gauthor); DEB1 printf("L0\n"); NEXTL; if(!strncmp(s,"GLP ",4)) { int ten,lpor,dnran=0; resetlp(&dlp); if(sscanf(s,"GLP %d %d %d %d %d\n",&dlp.dmask,&dlp.emask,&ten,&lpor,&dnran)<4) goto ew1; dlp.ten=ten; // using %hhd above fails in Visual C dlp.lpor=0; dlp.dnran=dnran; fixlp(&dlp); NEXTL; } while(!strncmp(s,"GSP ",4)) { int ten,spor,fstyle=0,dech=0; resetsp(&dsp); if(sscanf(s,"GSP %x %x %d %d %d %d %x\n",&dsp.bgcol,&dsp.fgcol,&ten,&spor,&fstyle,&dech,&dsp.mkcol)<4) goto ew1; dsp.ten=ten; dsp.spor=0; dsp.fstyle=fstyle; dsp.dech=dech; fixsp(&dsp); NEXTL; } while(!strncmp(s,"GSPMK ",6)) { if(sscanf(s,"GSPMK %d\n",&k)<1) goto ew1; if(k<0||k>=MAXNMK) continue; NEXTL; if(s[0]=='+') { strncpy(dsp.mk[k],s+1,MXMK); dsp.mk[k][MXMK]='\0'; } NEXTL; } while(!strncmp(s,"TM ",3)) { t2=0; t3=0; if(sscanf(s,"TM %d %d %d %d %d\n",&j,&t0,&t1,&t2,&t3)<3) goto ew1; NEXTL; if(j==0&&s[0]=='+') { if(t0<0||t0>=NATREAT) continue; treatmode=t0,tambaw=!!t1; if(t2<0||t2>2) t2=0; treatorder[0]=t2; if(t3<0||t3>2) t3=0; treatorder[1]=t3; if(treatmode==TREAT_PLUGIN) { strncpy(tpifname,s+1,SLEN-1); tpifname[SLEN-1]=0; } NEXTL; } } while(!strncmp(s,"TMSG ",5)) { if(sscanf(s,"TMSG %d %d\n",&j,&t0)!=2) goto ew1; NEXTL; if(j==0&&s[0]=='+') { if(t0<0||t0>=NMSG) continue; strncpy(treatmsg[t0],s+1,SLEN-1); treatmsg[t0][SLEN-1]=0; NEXTL; } } while(!strncmp(s,"TCST ",5)) { if(sscanf(s,"TCST %d %d %s\n",&i,&j,s0)!=3) goto ew1; if(i>=0&&i=0&&j=MAXNDICTS) continue; if(s[0]=='+') { strncpy(dfnames[j],s+1,SLEN-1); dfnames[j][SLEN-1]=0; NEXTL; } } while(!strncmp(s,"DSF ",4)) { if(sscanf(s,"DSF %d\n",&j)!=1) goto ew1; NEXTL; if(j<0||j>=MAXNDICTS) continue; if(s[0]=='+') { strncpy(dsfilters[j],s+1,SLEN-1); dsfilters[j][SLEN-1]=0; NEXTL; } } while(!strncmp(s,"DAF ",4)) { if(sscanf(s,"DAF %d\n",&j)!=1) goto ew1; NEXTL; if(j<0||j>=MAXNDICTS) continue; if(s[0]=='+') { strncpy(dafilters[j],s+1,SLEN-1); dafilters[j][SLEN-1]=0; NEXTL; } } DEB1 printf("L1: %s\n",s); while(!strncmp(s,"SQ ",3)) { c=' '; DEB1 printf("SQ: "); if(sscanf(s,"SQ %d %d %d %d %d %c\n",&i,&j,&b,&m,&f,&c)<5) goto ew1; DEB1 printf("%d,%d %d\n",i,j,b); if(i<0||i>=MXSZ||j<0||j>=MXSZ) continue; gsq[i][j].bars =b&((1<=MXSZ||j<0||j>=MXSZ) continue; sp.ten=ten; sp.spor=spor; sp.fstyle=fstyle; sp.dech=dech; fixsp(&sp); gsq[i][j].sp=sp; NEXTL; } while(!strncmp(s,"SQSPMK ",7)) { if(sscanf(s,"SQSPMK %d %d %d\n",&i,&j,&k)<3) goto ew1; if(i<0||i>=MXSZ||j<0||j>=MXSZ||k<0||k>=MAXNMK) continue; NEXTL; if(s[0]=='+') { strncpy(gsq[i][j].sp.mk[k],s+1,MXMK); gsq[i][j].sp.mk[k][MXMK]='\0'; } NEXTL; } while(!strncmp(s,"SQLP ",5)) { int ten,lpor,dnran=0; resetlp(&lp); if(sscanf(s,"SQLP %d %d %d %d %d %d %d %d\n",&i,&j,&d,&lp.dmask,&lp.emask,&ten,&lpor,&dnran)<7) goto ew1; if(i<0||i>=MXSZ||j<0||j>=MXSZ||d<0||d>=ndir[gtype]) continue; lp.ten=ten; lp.lpor=lpor; lp.dnran=dnran; fixlp(&lp); gsq[i][j].lp[d]=lp; NEXTL; } while(!strncmp(s,"VL ",3)) { if(sscanf(s,"VL %d %d %d %d\n",&d,&n,&i,&j)!=4) goto ew1; if(i<0||i>=MXSZ||j<0||j>=MXSZ||n<0||n>=MXCL||d<0||d>=NVL) continue; vls[d].x[n]=i; vls[d].y[n]=j; if(n>=vls[d].l) vls[d].l=n+1; if(d>=nvl) nvl=d+1; NEXTL; } while(!strncmp(s,"VLP ",4)) { int ten,lpor,dnran=0; resetlp(&lp); if(sscanf(s,"VLP %d %d %d %d %d %d\n",&d,&lp.dmask,&lp.emask,&ten,&lpor,&dnran)<5) goto ew1; if(d<0||d>=NVL) continue; lp.ten=ten; lp.lpor=lpor; lp.dnran=dnran; fixlp(&lp); vls[d].lp=lp; NEXTL; } while(!strncmp(s,"SQCT ",5)) { if(sscanf(s,"SQCT %d %d %d %s\n",&i,&j,&d,s0)!=4) goto ew1; if(i<0||i>=MXSZ||j<0||j>=MXSZ||d<0||d>=MAXNDIR) continue; gsq[i][j].ctlen[d]=strtoabms(gsq[i][j].ctbm[d],MXCT,s0,0); NEXTL; } } if(fclose(fp)) goto ew3; if(treatmode==TREAT_PLUGIN) { if((p=loadtpi())) { sprintf(s,"Error loading custom plug-in\n%.100s",p); reperr(s); } } else unloadtpi(); donumbers(); loaddicts(0); undo_push();unsaved=0; if(wf) reperr("File was saved using\na newer version of Qxw.\nSome features may be lost."); havesavefn=1; return; // no errors ew1:fclose(fp);syncgui();reperr("File format error");goto ew2; ew3:fserror(); ew2: a_filenew(0); loaddefdicts(); } // write state to file void a_save(void) { int d,i,j,k,l; char c,s0[MXCT*(NL+4)]; ABM b; FILE*fp; setfilenamebase(filename); fp=fopen(filename,"w"); if(!fp) {fserror();return;} if(fprintf(fp,"#QXW2v%d http://www.quinapalus.com\n",SAVE_VERSION)<0) goto ew0; if(fprintf(fp,"GP %d %d %d %d %d %d\n",gtype,width,height,symmr,symmm,symmd)<0) goto ew0; if(fprintf(fp,"TTL\n+%s\n",gtitle)<0) goto ew0; if(fprintf(fp,"AUT\n+%s\n",gauthor)<0) goto ew0; if(fprintf(fp,"GLP %d %d %d %d %d\n",dlp.dmask,dlp.emask,dlp.ten,dlp.lpor,dlp.dnran)<0) goto ew0; if(fprintf(fp,"GSP %06x %06x %d %d %d %d %06x\n",dsp.bgcol,dsp.fgcol,dsp.ten,dsp.spor,dsp.fstyle,dsp.dech,dsp.mkcol)<0) goto ew0; for(k=0;k0) b=gsq[i][j].ctbm[0][0]&ABM_ALNUM; else b=0; if(onebit(b)) c=ltochar[logbase2(b)]; else c=' '; if(fprintf(fp,"SQ %d %d %d %d %d %c\n",i,j,gsq[i][j].bars,gsq[i][j].merge,gsq[i][j].fl,c)<0) goto ew0; } for(j=0;j0&&!isprint(s[l-1])) s[--l]='\0'; if(l==0) break; // end of this vl if(s[0]=='#') continue; // comment line? if(i>=MXCL) goto ew1; if(sscanf(s,"%d %d",tvls[d].x+i,tvls[d].y+i)<2) goto ew1; if(tvls[d].x[i]<0||tvls[d].x[i]>=MXSZ) goto ew1; if(tvls[d].y[i]<0||tvls[d].y[i]>=MXSZ) goto ew1; i++; tvls[d].l=i; } if(tvls[d].l>0) d++; } ew0: if(fclose(fp)) goto ew3; if(tvls[d].l>0) d++; memcpy(vls,tvls,d*sizeof(struct vl)); nvl=d; undo_push();unsaved=0;syncgui();compute(0); return; ew3:fserror();return; ew1:fclose(fp);syncgui();reperr("File format error"); } void a_exportvls(char*fn) { int d,i; FILE*fp; fp=fopen(fn,"w"); if(!fp) {fserror();return;} if(fprintf(fp,"# Free light export file created by Qxw %s http://www.quinapalus.com\n",RELEASE)<0) goto ew0; for(d=0;d]* [qxw_file]\n",argv[0]); printf("This is Qxw, release %s.\n\n\ Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant\n\ \n\ This program is free software; you can redistribute it and/or modify\n\ it under the terms of version 2 of the GNU General Public License as\n\ published by the Free Software Foundation.\n\ \n\ This program is distributed in the hope that it will be useful,\n\ but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ GNU General Public License for more details.\n\ \n\ You should have received a copy of the GNU General Public License along\n\ with this program; if not, write to the Free Software Foundation, Inc.,\n\ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\ \n\ For more information visit http://www.quinapalus.com or\n\ e-mail qxw@quinapalus.com\n\n\ ",RELEASE); return 0; } g_thread_init(0); gdk_threads_init(); gdk_threads_enter(); filler_init(); gtk_init(&argc,&argv); startgtk(); a_filenew(0); // reset grid loadprefs(); // load preferences file (silently failing to defaults) draw_init(); if(optindscore; // negative ans values cannot occur here g=ansp[lts[*(int*)q].ans]->score; if(fg) return -1; return (char*)p-(char*)q; // stabilise sort } // called by filler when a list of feasible words through the cursor has been found void mkfeas(void) {int l,x,y; int*p; struct word*w=0; llistp=NULL;llistn=0; // default answer: no list if(dir0) w=gsq[x][y].w[dir];} else if(dir>=100&&dir<100+NVL) w=vls[dir-100].w; DEB2 printf("mkfeas: %d,%d (d=%d) ->%d,%d, w=%p\n",curx,cury,dir,x,y,w); if(w==0||w->flist==0) {llistp=NULL;llistn=0;return;} // no list p=w->flist; l=w->flistlen; if(llist) free(llist); llist=(int*)malloc(l*sizeof(int)); // space for feasible word list if(llist==NULL) return; memcpy(llist,p,l*sizeof(int)); llistp=llist; llistn=l; llistwlen=w->wlen; llistdm=w->lp->dmask; // copy list across llistem=w->lp->emask; DEB1 printf("llist=%p p=%p l=%d dm=%08x llistwlen=%d llistdm=%08x\n",llist,p,l,w->lp->dmask,llistwlen,llistdm); qsort(llistp,llistn,sizeof(int),&cmpscores); // DEB1 printf("mkfeas: %d matches; dm=%08x\n",llistn,llistdm); } // provide progress info to display void updategrid(void) {int i; for(i=0;i g #FAFAFA", ", g #F8F8F8", "' g #F9F9F9", ") g #707070", "! g #D0D0D0", "~ g #FDFDFD", "{ g #DEDEDE", "] g #E0E0E0", "^ g #747474", "/ g #D6D6D6", "( g #D8D8D8", "_ g #B6B6B6", ": g #FEFEFE", "< g #FBFBFB", "[ g #ECECEC", "} g #9B9B9B", "| g #5E5E5E", "1 g #737373", "2 g #D7D7D7", "3 g #F0F0F0", "4 g #B8B8B8", "5 g #676767", "6 g #525252", "7 g #D5D5D5", "8 g #575757", "9 g #797979", "0 g #464646", "a g #E5E5E5", "b g #7C7C7C", "c g #959595", "d g #AFAFAF", "e g #C3C3C3", "f g #F6F6F6", "g g #A7A7A7", "h g #B1B1B1", "i g #888888", "j g #3D3D3D", "k g #303030", "l g #404040", "m g #8E8E8E", "n g #484848", "o g #1D1D1D", "p g #999999", "q g #C4C4C4", "r g #939393", "s g #202020", "t g #585858", "u g #7A7A7A", "v g #0C0C0C", "w g #BBBBBB", "x g #0D0D0D", "y g #A3A3A3", "z g #EDEDED", "A g #474747", "B g #333333", "C g #CECECE", "D g #171717", "E g #818181", "F g #030303", "G g #C7C7C7", "H g #121212", "I g #BCBCBC", "J g #8D8D8D", "K g #000000", "L g #A2A2A2", "M g #717171", "N g #0E0E0E", "O g #494949", "P g #313131", "Q g #555555", "R g #ABABAB", "S g #060606", "T g #808080", "U g #D4D4D4", "V g #929292", "W g #050505", "X g #2C2C2C", "Y g #5C5C5C", "Z g #2A2A2A", "` g #0A0A0A", " . g #E8E8E8", ".. g #2B2B2B", "+. g #606060", "@. g #EEEEEE", "#. g #3B3B3B", "$. g #989898", "%. g #E6E6E6", "&. g #757575", "*. g #B2B2B2", "=. g #D9D9D9", "-. g #F7F7F7", ";. g #9A9A9A", ">. g #848484", ",. g #9C9C9C", "'. g #E9E9E9", "). g #4F4F4F", "!. g #979797", "~. g #BABABA", "{. g #BDBDBD", "]. g #A6A6A6", "^. g #DFDFDF", "/. g #8C8C8C", " ", " . + @ # # # # # # # # # # # # # # # # # # # # # # # # # # # # ", " + $ % & & & & & & & & & & & & & & & & & & & & & & * = - & & & ", " @ % ; > . . , ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' > ) ! ~ . > ", " # & > { ] ^ / ( _ : ", " # & < [ } | 1 2 3 4 _ 5 ", " # & ' 6 1 7 8 ", " # & ' 6 : 1 7 > 9 ( ", " # & ' ~ 2 0 a : : : 1 / b c 7 ", " # & ' < d } e : f : 1 / ; d g h ", " # & ' : : < i j k l m > 1 7 ", " # & ' : , n o p q r s t < : 1 7 ", " # & ' : u v ] : : w x y 1 7 ", " # & ' z k 1 ~ : A B : 1 7 ", " # & ' C D d : : E F 1 7 ", " # & ' G H I : J K 1 7 ", " # & ' 7 o L ~ : M N 1 7 ", " # & ' ~ l O : : P Q : 1 7 ", " # & ' R S r T F U 1 7 ", " # & ' : V W X Y Z ` d ~ : 1 7 ", " # & ' : : .r M ..+.@. 1 7 ", " # & ' : U #.$.< 1 7 ", " # & ' : %.! . 1 7 ", " # & ' : 1 7 ", " # & ' 1 7 ", " # & > ^ / ", " # * ) ^ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ^ B +.&.1 1 1 ", " # = ! / / / 7 7 / / 7 7 7 7 7 7 7 7 7 7 7 7 7 7 / +.*.=.7 7 7 ", " # - ~ ~ , -. &.=. ", " # & < 3 ;.>. # ,.>.@ : 1 7 ", " # & ' < '.). q !.~.L . 1 7 ", " # & ' 6 {.].^./.< 1 7 "}; qxw-20140331/qxw.desktop0000644000175000017500000000035712305362141012614 0ustar momo[Desktop Entry] Version=1.0 Type=Application Name=Qxw Icon=qxw GenericName=Crossword editor Comment=Construct crossword puzzles Exec=/usr/games/qxw Terminal=false Categories=Game;LogicGame; Keywords=puzzle;grid;construction;wordgame;word; qxw-20140331/draw.h0000640000175000017500000000611412272730646011515 0ustar momo// $Id: draw.h 517 2014-01-31 14:22:28Z mo $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant http://www.quinapalus.com E-mail: qxw@quinapalus.com This file is part of Qxw. Qxw is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Qxw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Qxw. If not, see or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __DRAW_H__ #define __DRAW_H__ #include extern void moveto(cairo_t*cc,double x,double y); extern void lineto(cairo_t*cc,double x,double y); extern void rmoveto(cairo_t*cc,double x,double y); extern void rlineto(cairo_t*cc,double x,double y); extern void setlinewidth(cairo_t*cc,double w); extern void setlinecap(cairo_t*cc,int c); extern void closepath(cairo_t*cc); extern void fill(cairo_t*cc); extern void stroke(cairo_t*cc); extern void strokepreserve(cairo_t*cc); extern void clip(cairo_t*cc); extern void gsave(cairo_t*cc); extern void grestore(cairo_t*cc); extern void setrgbcolor(cairo_t*cc,double r,double g, double b); extern void setrgbacolor(cairo_t*cc,double r,double g, double b,double a); extern void setfontstyle(cairo_t*cc,int fs); extern void setfontsize(cairo_t*cc,double h); extern void showtext(cairo_t*cc,char*s); extern double textwidth(cairo_t*cc,char*s,double h); extern void arc(cairo_t*cc,double x,double y,double r,double t0,double t1); extern void arcn(cairo_t*cc,double x,double y,double r,double t0,double t1); extern void setrgbcolor24(cairo_t*cc,int c); extern void ltext(cairo_t*cc,char*s,double h,int fs); extern void ctext(cairo_t*cc,char*s,double x,double y,double h,int fs,int ocm); extern void ansform(char*t0,int t0l,int ln,int wlen,unsigned int dmask); extern int bawdpx; extern int hbawdpx; extern void draw_init(); extern void draw_finit(); extern void repaint(cairo_t*cr); extern void refreshsq(int x,int y); extern void refreshsqlist(int l,int*gx,int*gy); extern void refreshsqmg(int x,int y); extern void refreshsel(); extern void refreshcur(); extern void refreshnum(); extern void refreshhin(); extern void refreshall(); extern int dawidth(void); extern int daheight(void); extern void a_exportg(char*fn,int f0,int f1); extern void a_exportgh(int f,char*url); extern void a_exporta(int f); extern void a_exporthp(int f,int f0,int f1); extern void edgecoords(double*x0,double*y0,double*x1,double*y1,int x,int y,int d); extern void getsqbbox(double*x0,double*y0,double*x1,double*y1,int x,int y); extern void getmgbbox(double*x0,double*y0,double*x1,double*y1,int x,int y); extern void mgcentre(double*u,double*v,int x,int y,int d,int l); extern char*dname[NGTYPE][MAXNDIR]; #endif qxw-20140331/filler.c0000640000175000017500000010362612274432736012037 0ustar momo// $Id: filler.c 534 2014-02-05 13:00:14Z mo $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant http://www.quinapalus.com E-mail: qxw@quinapalus.com This file is part of Qxw. Qxw is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Qxw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Qxw. If not, see or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Interface to dicts.c comprises: pregetinitflist(); getinitflist(); postgetinitflist(); Interface to qxw.c / gui.c comprises: Calls out: mkfeas(); updatefeas(); updategrid(); Calls in: filler_init(); filler_start(); filler_stop(); filler_finit(); getposs(); */ #include #include #include #include "common.h" #include "filler.h" #include "dicts.h" #include "qxw.h" #include "gui.h" static GThread*fth; static int ct_malloc=0,ct_free=0; // counters for debugging volatile int abort_flag=0; static int fillmode=0; // 0=stopped, 1=filling all, 2=filling selection, 3=word lists only (for preexport) static clock_t ct0; int filler_status=0; // return code: -5: aborted; -3, -4: initflist errors; -2: out of stack; -1: out of memory; 0: stopped; 1: no fill found; 2: fill found; 3: running // the following stacks keep track of the filler state as it recursively tries to fill the grid static int sdep=-1; // stack pointer static char**sposs=0; // possibilities for this entry, 0-terminated static int*spossp=0; // which possibility we are currently trying (index into sposs) static int***sflist; // pointers to restore feasible word list flist static struct jdata***sjdata=0; // jumble data static ABM***sjflbm=0; // jumble data feasible list bitmaps static struct sdata***ssdata=0; // spread data static int**sflistlen=0; // pointers to restore flistlen static ABM**sentryfl=0; // feasible letter bitmap for this entry static int*sentry=0; // entry considered at this depth static unsigned char*aused=0; // answer already used while filling static unsigned char*lused=0; // light already used while filling #define isused(l) (lused[lts[l].uniq]|aused[lts[l].ans+NMSG]) #define setused(l,v) lused[lts[l].uniq]=v,aused[lts[l].ans+NMSG]=v // ,printf("setused(%d,%d)->%d\n",l,v,lts[l].uniq) static void pstate(int f) { int i,j,jmode; struct word*w; struct entry*e; char s[MXFL+1]; for(i=0;ilp->emask&EM_JUM) jmode=1; else if(w->lp->emask&EM_SPR) jmode=2; else jmode=0; printf("W%d: fe=%d jmode=%d nent=%d wlen=%d jlen=%d ",i,w->fe,jmode,w->nent,w->wlen,w->jlen); for(j=0;jnent;j++) { e=w->e[j]; s[j]=abmtoechar(e->flbm); if(s[j]==' ') s[j]='.'; printf(" E%d:%016llx",(int)(e-entries),e->flbm); } s[j]=0; printf("\n %s\n",s); if(f) { printf(" "); if(w->flistlen<8) j=0; else { for(j=0;j<4;j++) printf(" %s[%d]",lts[w->flist[j]].s,lts[w->flist[j]].uniq); printf(" ..."); j=w->flistlen-4; } for(;jflistlen;j++) printf(" %s[%d]",lts[w->flist[j]].s,lts[w->flist[j]].uniq); printf(" (%d)\n",w->flistlen); } } } // transfer progress info to display static void progress(void) { DEB1 printf("ct_malloc=%d ct_free=%d diff=%d\n",ct_malloc,ct_free,ct_malloc-ct_free); gdk_threads_enter(); updategrid(); gdk_threads_leave(); } static int initjdata(int j) {struct word*w; int i; w=words+j; if(!(w->lp->emask&EM_JUM)) return 0; if(w->fe) return 0; w->jdata=malloc(w->flistlen*sizeof(struct jdata)); if(!w->jdata) return -1; w->jflbm=malloc(w->flistlen*w->jlen*sizeof(ABM)); if(!w->jflbm) return -1; for(i=0;iflistlen*w->jlen;i++) w->jflbm[i]=ABM_ALL; return 0; } static int initsdata(int j) {struct word*w; int i,k; w=words+j; if(!(w->lp->emask&EM_SPR)) return 0; if(w->fe) return 0; w->sdata=malloc(w->flistlen*sizeof(struct sdata)); if(!w->sdata) return -1; for(i=0;iflistlen;i++) for(k=0;knent;k++) w->sdata[i].flbm[k]=ABM_ALL; return 0; } // Determine if one string is a non-trivial cyclic permutation of another static int strcyccmp(char*s,char*t,int l) { int i,j,k; for(i=1;ilp->emask; m=w->jlen; for(k=0;kjflbm[j*m+k]:w->e[k]->flbm)]; f[k]=0; r[k]=0; t=lts[w->flist[j]].s; DEB16 printf("checkperm(%s : %s em=%d mode=%d)\n",f,t,em,mode); if((em&EM_FWD)==0) if(!strncmp(f,t,m)) return 0; if((em&EM_REV)==0) if(!strncmp(r,t,m)) return 0; if((em&EM_CYC)==0) if(strcyccmp(f,t,m)) return 0; if((em&EM_RCY)==0) if(strcyccmp(r,t,m)) return 0; return 1; } // Approximate test to see if a jumbled string can fit in a given word. Writes deductions to flbm etc. in jdata. static int checkjword(struct word*w,int j) { unsigned char hi[NL]; ABM bm[MXFL],u,v,*jbm; struct light*l; int c,c0,c1,f,i,k,m,n,nuf; l=lts+w->flist[j]; m=w->jlen; jbm=w->jflbm+j*m; // if(w-words==0) debug|=16; else debug&=~16; DEB16 { printf("checkjword(w=%ld,\"%s\") m=%d lbm=%016llx\n",(long int)(w-words),l->s,m,l->lbm); printf("hist:"); for(i=0;ihist[i]); printf("\n"); printf("order:"); for(i=0;inhistorder;i++) printf(" %d",l->historder[i]); printf("\n"); } for(k=0;ke[k]->flbm&l->lbm; do { DEB16 { printf("** "); for(k=0;khist,sizeof(hi)); for(k=0;knhistorder;i++) { // work from biggest histogram entry down, greedily looking for contradictions c=l->historder[i]; n+=hi[c]; // accumulate histogram total of letters considered so far in this group nuf+=hi[c]; v=1ULL<jdata[j].nuf=nuf; w->jdata[j].ufhist[i]=hi[c]; w->jdata[j].poscnt[i]=c0; } } while(f); memcpy(jbm,bm,m*sizeof(ABM)); DEB16 { printf("checkjword returning: w=%ld \"%s\" nuf=%d hist(poscnt)=",(long int)(w-words),l->s,nuf); for(i=0;inhistorder;i++) printf("%d(%d) ",w->jdata[j].ufhist[i],w->jdata[j].poscnt[i]); printf(" flbm:"); for(i=0;iflist[wn]; m=w->nent; n=w->wlen; sd=w->sdata+wn; DEB16 printf("scounts: w=%ld \"%s\"\n",(long int)(w-words),l->s); memset(ctl,0,sizeof(ctl)); ctl[0][0]=1; for(j=1;j<=m;j++) { u=bm[j-1]; for(i=0;i<=n;i++) { if(u&ABM_DASH) ctl[i][j]=ctl[i][j-1]; if(i>0&&(u&chartoabm[(int)l->s[i-1]])) ctl[i][j]+=ctl[i-1][j-1]; } } memset(ctr,0,sizeof(ctr)); ctr[n][m]=1; for(j=m-1;j>=0;j--) { u=bm[j]; for(i=n;i>=0;i--) { if(u&ABM_DASH) ctr[i][j]=ctr[i][j+1]; if(is[i]])) ctr[i][j]+=ctr[i+1][j+1]; } } DEB16 { printf("CTL: # of ways to put chars [0,i) in slots [0,j)\n"); for(j=0;j<=m;j++) { for(i=0;i<=n;i++) printf("%5.1f ",ctl[i][j]); printf("\n"); } printf("CTR: # of ways to put chars [i,n) in slots [j,m)\n"); for(j=0;j<=m;j++) { for(i=0;i<=n;i++) printf("%5.1f ",ctr[i][j]); printf("\n"); } } memset(sd->ct,0,sizeof(sd->ct)); memset(sd->ctd,0,sizeof(sd->ctd)); for(i=0;i< n;i++) for(j=0;jct[i][j]=ctl[i][j]*ctr[i+1][j+1]; for(j=0;jctd[j] +=ctl[i][j]*ctr[i][j+1]; // do the spreading character as a special case DEB16 { printf(" " ); for(i=0;is[i]); printf(" -\n"); for(j=0;jct[i][j]); printf(" %5.1f",sd->ctd[j]); printf("\n");} } memset(sd->flbm,0,sizeof(sd->flbm)); for(i=0;ict[i][j]) sd->flbm[j]|=chartoabm[(int)l->s[i]]; for(j=0;jctd[j]) sd->flbm[j]|=ABM_DASH; } // Test to see if a spread string can fit in a given word. Writes deductions to sdata. static int checksword(struct word*w,int j) { ABM bm[MXFL]; struct light*l; struct sdata*sd; int k,m; //,n; l=lts+w->flist[j]; sd=w->sdata+j; m=w->nent; // n=w->wlen; for(k=0;ke[k]->flbm; DEB16 { printf("checksword(w=%ld,\"%s\") bm=",(long int)(w-words),l->s); for(k=0;kflbm[k]); printf("\n"); } return 1; } // intersect light list q length l with letter position wp masked by bitmap m: result is stored in p and new length is returned static int listisect(int*p,int*q,int l,int wp,ABM m) {int i,j; for(i=0,j=0;i%d\n",wp,m,l,j); return j; } // find the entry to expand next, or -1 if all done static int findcritent(void) {int i,j,m;double k,l; m=-1; for(i=0;im) m=entries[i].checking; // find highest checking level // find highest checking level } j=-1; for(;m>0;m--) { // m>=2: loop over checked entries; then m=1: loop over unchecked entries j=-1;l=DBL_MAX; for(i=0;i=m) { // not already fixed? k=entries[i].crux; // get the priority for this entry if(k=1 otherwise static int settleents(void) { struct entry*e; struct word*w; struct jdata*jd; struct sdata*sd; ABM*jdf; int aed,f,i,j,k,l,m,mj,jmode; int*p; // DEB1 printf("settleents() sdep=%d\n",sdep); f=0; for(j=0;jlp->emask&EM_JUM) jmode=1; else if(w->lp->emask&EM_SPR) jmode=2; else jmode=0; // printf("j=%d jmode=%d emask=%d\n",j,jmode,w->lp->emask); m=w->nent; mj=w->jlen; for(k=0;ke[k]->upd) break; if(k==m) continue; // no update flags set on any entries for this word for(k=0;ke[k]->flbm)) break; aed=(k==m); // all entries determined? p=w->flist; l=w->flistlen; jd=w->jdata; sd=w->sdata; if(sflistlen[sdep][j]==-1) { // then we mustn't trash words[].flist sflist [sdep][j]=p; sflistlen[sdep][j]=l; sjdata [sdep][j]=jd; sjflbm [sdep][j]=w->jflbm; ssdata [sdep][j]=sd; w->jdata=0; w->jflbm=0; w->sdata=0; w->flist=(int*)malloc(l*sizeof(int)); // new list can be at most as long as old one if(!w->flist) return -1; // out of memory ct_malloc++; if(jmode==1) { w->jdata=(struct jdata*)malloc(l*sizeof(struct jdata)); if(!w->jdata) return -1; // out of memory ct_malloc++; w->jflbm=(ABM*)malloc(l*mj*sizeof(ABM)); if(!w->jflbm) return -1; // out of memory ct_malloc++; } if(jmode==2) { w->sdata=(struct sdata*)malloc(l*sizeof(struct sdata)); if(!w->sdata) return -1; // out of memory ct_malloc++; } } if(afunique) { // the following test makes things quite a lot slower: consider optimising by keeping track of when an update might be needed for(i=0,k=0;iflist[k++]=p[i]; p=w->flist; l=k; } if(jmode==0) { // normal case for(k=0;ke[k]; if(!e->upd) continue; l=listisect(w->flist,p,l,k,e->flbm); // generate new feasible word list p=w->flist; if(l==0) break; } } else if(jmode==1) { // jumble case for(k=mj;ke[k]; if(!e->upd) continue; l=listisect(w->flist,p,l,k,e->flbm); // generate new feasible word list p=w->flist; } for(i=0,k=0;iflist[k]=p[i]; if(checkjword(w,k)) k++;} l=k; w->upd=1; f++; // need to do settlents() anyway in this case } else { // spread case for(i=0,k=0;iflist[k]=p[i]; if(checksword(w,k)) k++;} l=k; w->upd=1; f++; // need to do settlents() anyway in this case } if(l!=w->flistlen) { w->upd=1;f++; // word list has changed: feasible letter lists will need updating if(l) { p=realloc(w->flist,l*sizeof(int)); if(p) w->flist=p; if(w->jdata) { jd=realloc(w->jdata,l*sizeof(struct jdata)); if(jd) w->jdata=jd; jdf=realloc(w->jflbm,l*mj*sizeof(ABM)); if(jdf) w->jflbm=jdf; } if(w->sdata) { sd=realloc(w->sdata,l*sizeof(struct sdata)); if(sd) w->sdata=sd; } } } w->flistlen=l; if(l==0&&!w->fe) return -2; // no options left and was not fully entered by user if(!aed) continue; // not all entries determined yet, so don't commit if(jmode==1) { // final check that the "jumble" is not actually a cyclic permutation etc. for(i=0,k=0;iflist[k]=w->flist[i]; if(checkperm(w,k,0)) k++;} l=k; } w->flistlen=l; if(l==0&&!w->fe) return -2; // no options left and was not fully entered by user assert(w->commitdep==-1); for(k=0;kflist[k],1); // flag as used (can be more than one in jumble case) w->commitdep=sdep; } for(i=0;i0 otherwise static int settlewds(void) { int f,i,j,k,l,m,mj,jmode; int*p; struct entry*e; struct word*w; ABM entfl[MXFL]; // DEB1 printf("settlewds()\n"); f=0; for(i=0;iupd) continue; // loop over updated word lists if(w->fe) continue; if(w->lp->emask&EM_JUM) jmode=1; else if(w->lp->emask&EM_SPR) jmode=2; else jmode=0; m=w->nent; mj=w->jlen; p=w->flist; l=w->flistlen; for(k=0;kjflbm[j*mj+k]; // main work has been done in settleents() for( ;ksdata[j].flbm[k]; // main work has been done in settleents() DEB16 { printf("w=%d entfl: ",i); for(k=0;ke[j]; // propagate from word to entry if(e->flbm&~entfl[j]) { // has this entry been changed by the additional constraint? e->flbm&=entfl[j]; e->upd=1;f++; // flag that it will need updating // printf("E%d %16llx\n",k,entries[k].flbm);fflush(stdout); } } } for(i=0;iflist[j]; m=w->jlen; jd=w->jdata+j; jbm=w->jflbm+j*m; nuf=jd->nuf; DEB16 { printf("jscores: w=%ld \"%s\" nuf=%d hist(poscnt)=",(long int)(w-words),l->s,nuf); for(i=0;inhistorder;i++) printf("%d(%d) ",jd->ufhist[i],jd->poscnt[i]); } // first make a very poor man's estimate of total feasible permutations given jdata information for word t=nuf; tp=1; for(i=0;inhistorder;i++) { h=jd->ufhist[i]; p=jd->poscnt[i]; if(!h||h==nuf) continue; r=(double)(p-h)/(nuf-h)*(t-h)+h; // if p==h, r=h; if p==nuf, r=t; and linearly (!) in between for(k=1;k<=h;k++) tp*=r,tp/=k,r-=1; t-=h; } DEB16 { printf(" tp==%g\n",tp); printf(" flbm:"); for(k=0;knhistorder;i++) { c=l->historder[i]; if(jd->poscnt[i]==0) continue; if(u&(1ULL<ufhist[i]/jd->poscnt[i]; // proportion of fills that will have letter c in each position r+=sc[k][c]=s; } } if(r>0) r=tp/r; for(i=0;iflist[wn]; m=w->nent; n=w->wlen; sd=w->sdata+wn; memset(sc,0,m*NL*sizeof(double)); for(i=0;is[i]]; for(j=0;jct[i][j]; } // now do the spreading character as a special case: i=#chars to left of candidate '-' for(j=0;jctd[j]; DEB16 for(k=0;kfe) continue; if(w->lp->emask&EM_JUM) jmode=1; else if(w->lp->emask&EM_SPR) jmode=2; else jmode=0; m=w->nent; mj=w->jlen; p=w->flist; l=w->flistlen; for(k=0;kcommitdep>=0) { // avoid zero score if we've committed if(l==1) for(k=0;kscore; for(k=0;k=0) f*=(double)ansp[k]->score; // score once for each feasible permutation; assume score=1 if a treatment light for(k=mj;ke[k]->score[j]*=sc[k][j]; } for(i=0;iscore[i]>k) k=e->score[i]; // find highest score k*=2; for(;;) { for(i=0,j=-DBL_MAX;iscore[i]>j&&e->score[i]score[i]; // peel off scores from top down // DEB2 printf("getposs(%d): j=%g\n",(int)(e-entries),j); if(j<=0) break; for(i=0;iscore[i]==j) s[l++]=ltochar[i]; // add to output string k=j;} // get next highest set of equal scores s[l]='\0'; if(r==0) return; for(i=0;i=0&&mcommitdep>=sdep) { // word to uncommit? l=w->flistlen; DEB16 { printf("sdep=%d flistlen=%d uncommitting word %d commitdep=%d:",sdep,w->flistlen,i,w->commitdep); for(j=0;jflist[j]].s); printf("\n"); } for(j=0;jflist[j],0); w->commitdep=-1; } if(sflistlen[sdep][i]!=-1&&w->flist!=0) { // word feasible list to free? free(w->flist); ct_free++; w->flist=sflist[sdep][i]; w->flistlen=sflistlen[sdep][i]; if(w->jdata) { free(w->jdata); ct_free++; w->jdata=sjdata[sdep][i]; } if(w->jflbm) { free(w->jflbm); ct_free++; w->jflbm=sjflbm[sdep][i]; } if(w->sdata) { free(w->sdata); ct_free++; w->sdata=ssdata[sdep][i]; } } } for(i=0;i=0);state_restore();sdep--;} // clear state stacks and free allocated memory static void state_finit(void) { while(sdep>=0) state_pop(); freestack(); } // build initial feasible lists, calling plug-in as necessary static int buildlists(void) {int u,i,j; for(i=0;ichecking; } u=getinitflist(&words[i].flist,&words[i].flistlen,words[i].lp,words[i].wlen); if(abort_flag) { DEB1 printf("aborted while building word lists\n"); filler_status=-5; return 1; } if(u) {filler_status=-3;return 0;} if(words[i].lp->ten) clueorderindex++; if(initjdata(i)) {filler_status=-3;return 0;} if(initsdata(i)) {filler_status=-3;return 0;} } if(postgetinitflist()) {filler_status=-4;return 1;} FREEX(aused); FREEX(lused); aused=(unsigned char*)calloc(atotal+NMSG,sizeof(unsigned char)); // enough for "msgword" answers too if(aused==NULL) {filler_status=-3;return 0;} lused=(unsigned char*)calloc(ultotal,sizeof(unsigned char)); if(lused==NULL) {filler_status=-3;return 0;} return 0; } // Main search routine. Returns // -5: told to abort // -1: out of memory // -2: out of stack // 1: all done, no result found // 2: all done, result found or only doing BG fill anyway static int search() { int e,f; char c; clock_t ct1; // Initially entry flbms are not consistent with word lists or vice versa. So we // need to make sure we call both settlewds() and settleents() before proceeding. if(settlewds()==-3) {DEB1 printf("aborting...\n"); return -5;}; resettle: // "unit propagation" do { if(abort_flag) {DEB1 printf("aborting...\n"); return -5;} f=settleents(); // rescan entries if(f==-3) {DEB1 printf("aborting...\n"); return -5;} if(f==0) break; if(f==-1) return -1; // out of memory: abort if(f==-2) goto backtrack; // proved impossible f=settlewds(); // rescan words if(f==-3) {DEB1 printf("aborting...\n"); return -5;} } while(f); // need to iterate until everything settles down f=mkscores(); if(f==-3) {DEB1 printf("aborting...\n"); return -5;} if(fillmode==0||fillmode==3) return 2; // only doing BG/preexport fill? stop after first settle DEB16 pstate(1); // go one level deeper in search tree DEB1 { int w; for(w=0;wCLOCKS_PER_SEC*3||ct1-ct0<0) {progress();ct0=clock();} // update display every three seconds or so goto resettle; // update internal data from new entry backtrack: state_pop(); if(sdep!=-1) goto nextposs; return 1; // all done, no solution found } static void searchdone() { int i; DEB1 printf("searchdone: A\n"); gdk_threads_enter(); DEB1 printf("searchdone: B\n"); if(abort_flag==0) { // finishing gracefully? if(filler_status==2) { mkfeas(); // construct feasible word list DEB1 pstate(1); } else { for(i=0;i=0) printf("assertion failing i=%d nw=%d words[i].commitdep=%d\n",i,nw,words[i].commitdep); assert(words[i].commitdep==-1); // ... and uncommitted } DEB1 printf("search done\n"); DEB1 printf(">> ct_malloc=%d ct_free=%d diff=%d\n",ct_malloc,ct_free,ct_malloc-ct_free);fflush(stdout); // j=0; for(i=0;i or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __GUI_H__ #define __GUI_H__ #define PXEDGE .2 // size of edge of box for clicking bar #define BAWD .139 // width of bar extern int selmode; extern int nsel; extern int pxsq; // pixels per square on display extern char filename[SLEN+50]; extern char filenamebase[SLEN]; extern int havesavefn; extern void setfilenamebase(char*s); extern void startgtk(void); extern void stopgtk(void); extern void invaldarect(int x0,int y0,int x1,int y1); extern void invaldaall(void); extern void stats_upd(void); extern void syncgui(void); extern void fserror(void); extern void fsgerr(void); extern void reperr(const char*s); extern void killcurrdia(void); extern void setposslabel(char*s); #endif qxw-20140331/gui.c0000640000175000017500000034037012305367062011337 0ustar momo// $Id: gui.c 552 2014-03-04 15:16:01Z mo $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant http://www.quinapalus.com E-mail: qxw@quinapalus.com This file is part of Qxw. Qxw is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Qxw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Qxw. If not, see or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // GTK #include #include #include #include #include "common.h" #include "qxw.h" #include "filler.h" #include "dicts.h" #include "gui.h" #include "draw.h" int selmode=0; int nsel=0; // number of things selected int pxsq; int zoomf=2; int zoompx[]={18,26,36,52,72}; int curmf=1; // cursor has moved since last redraw char filename[SLEN+50]; // result from filedia() char filenamebase[SLEN]; // base to use for constructing default filenames int havesavefn; // do we have a validated filename to save to? static char*gtypedesc[NGTYPE]={ "Plain rectangular", "Hex with vertical lights", "Hex with horizontal lights", "Circular", "Circular with half-cell offset", "Join left and right edges", "Join top and bottom edges", "Join left and right edges with flip", "Join top and bottom edges with flip", "Torus", }; static int spropdia(int g); static int lpropdia(int g); static int mvldia(int v); static int dictldia(void); static int treatdia(void); static int gpropdia(void); static int ccontdia(void); static int prefsdia(void); static int statsdia(void); static int filedia(char*res,char*but,char*ext,char*filetype,int write); static void gridchangen(void); static void syncselmenu(void); static GtkWidget *grid_da; static GtkItemFactory *item_factory; // for menus static GtkWidget *mainw,*grid_sw,*poss_label,*clist; // main window and content static GtkWidget*stats=NULL; // statistics window (if visible) static GtkWidget *hist_da; static GtkWidget*(st_te[MXLE+2][5]),*(st_r[10]); // statistics table static GtkWidget*currentdia=0; // for `Filling' dialogue box (so that we can close it automatically when filling completes) static char*treat_lab[NATREAT][NMSG]={ {0, 0, }, {"Keyword: ", 0, }, {"Encode ABC...Z as: ", 0, }, {"Key letter/word: ", 0, }, {"Encodings of A: ", 0, }, {"Correct letters: ", "Incorrect letters: " }, {"Letters to delete: ", 0, }, {"Letters to delete: ", 0, }, {"Letters to insert: ", 0, }, {"Message 1: ", "Message 2: " }, {"Correct letters: ", 0, }, {"Incorrect letters: ", 0 }, }; static int treatpermok[NATREAT][NMSG]={ {0,0}, {0,0}, {0,0}, {0,0}, {1,0}, {0,0}, {1,0}, {1,0}, {1,0}, {1,1}, {1,0}, {1,0}, }; static char*tnames[NATREAT]={ " None", " Playfair cipher", " Substitution cipher", " Fixed Caesar/Vigenère cipher", " Variable Caesar cipher", " Misprint (general, clue order)", " Delete single occurrence of character", " Letters latent: delete all occurrences of character", " Insert single character", " Custom plug-in", " Misprint (correct letters specified)", " Misprint (incorrect letters specified)", }; static char*mklabel[NGTYPE][MAXNDIR*2]={ {"NW mark: ","NE mark: ","SE mark: ","SW mark: "}, {"NW mark: ","NE mark: ","E mark: ", "SE mark: ","SW mark: ","W mark: "}, {"NW mark: ","N mark: ","NE mark: ", "SE mark: ","S mark: ","SW mark: "}, {"First mark: ","Second mark: ","Third mark: ","Fourth mark: "}, {"First mark: ","Second mark: ","Third mark: ","Fourth mark: "}, {"NW mark: ","NE mark: ","SE mark: ","SW mark: "}, {"NW mark: ","NE mark: ","SE mark: ","SW mark: "}, {"NW mark: ","NE mark: ","SE mark: ","SW mark: "}, {"NW mark: ","NE mark: ","SE mark: ","SW mark: "}, {"NW mark: ","NE mark: ","SE mark: ","SW mark: "}, }; // set main window title static void setwintitle() {char t[SLEN*3]; sprintf(t,"Qxw: %s",titlebyauthor()); gtk_window_set_title(GTK_WINDOW(mainw),t); } // set filenamebase from given string, which conventionally ends ".qxw" void setfilenamebase(char*s) { strncpy(filenamebase,s,SLEN-1);filenamebase[SLEN-1]='\0'; if(strlen(filenamebase)>=4&&!strcmp(filenamebase+strlen(filenamebase)-4,".qxw")) filenamebase[strlen(filenamebase)-4]='\0'; } // general question-and-answer box: title, question, yes-text, no-text static int box(int type,const char*t,const char*u,const char*v) { GtkWidget*dia; int i; dia=gtk_message_dialog_new( GTK_WINDOW(mainw), GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_NONE, "%s",t ); gtk_dialog_add_buttons(GTK_DIALOG(dia),u,GTK_RESPONSE_ACCEPT,v,GTK_RESPONSE_REJECT,NULL); currentdia=dia; i=gtk_dialog_run(GTK_DIALOG(dia)); currentdia=0; gtk_widget_destroy(dia); return i==GTK_RESPONSE_ACCEPT; } // box for information purposes only static void okbox(const char*t) {box(GTK_MESSAGE_INFO,t,GTK_STOCK_CLOSE,0);} // box for errors, with `details' option void reperr(const char*s) {box(GTK_MESSAGE_ERROR,s,GTK_STOCK_CLOSE,0);} void fsgerr() {box(GTK_MESSAGE_ERROR,"Filing system error",GTK_STOCK_CLOSE,0);} void fserror() {char s[SLEN],t[SLEN*2]; #ifdef _WIN32 if(strerror_s(s,SLEN,errno)) strcpy(s,"general error"); // Windows version of threadsafe strerror() #else if(strerror_r(errno,s,SLEN)) strcpy(s,"general error"); #endif sprintf(t,"Filing system error: %s",s); reperr(t); } static int areyousure(char*action) { // general-purpose are-you-sure dialogue char s[1000]; sprintf(s,"\n Your work is not saved. \n Are you sure you want to %s? \n",action); return box(GTK_MESSAGE_QUESTION,s," Proceed ",GTK_STOCK_CANCEL); } // GTK MENU HANDLERS // simple menu handlers static int checkoverwrite() {FILE*fp; fp=fopen(filename,"r"); if(!fp) return 1; fclose(fp); return box(GTK_MESSAGE_QUESTION,"\n That file already exists. \n Overwrite it? \n"," Overwrite ",GTK_STOCK_CANCEL); } static void m_filenew(GtkWidget*w,gpointer data) { if(!unsaved||areyousure("start again")) { a_filenew((intptr_t)data); strcpy(filenamebase,""); setwintitle(); syncgui(); compute(0); } } static void m_fileopen(GtkWidget*w,gpointer data) { if(!unsaved||areyousure("proceed")) if(filedia(0,"Open",".qxw","Qxw",0)) { a_load(); setwintitle(); syncgui(); compute(0); } } static void m_filesaveas(GtkWidget*w,gpointer data) { if(filedia(0,"Save",".qxw","Qxw",1)&&checkoverwrite()) { a_save(); setwintitle(); } } static void m_filesave(GtkWidget*w,gpointer data) { // printf("m_filesave: filenamebase=%s havesavefn=%d\n",filenamebase,havesavefn); if(havesavefn&&strcmp(filenamebase,"")) { strcpy(filename,filenamebase); strcat(filename,".qxw"); a_save(); } else m_filesaveas(w,data); } static void m_exportvls(GtkWidget*w,gpointer data) {char t[SLEN+50]; if(filedia(t,"Export free light paths",".fl.txt","Plain text",1)) a_exportvls(t);} static void m_importvls(GtkWidget*w,gpointer data) {char t[SLEN+50]; if(filedia(t,"Import free light paths",".fl.txt","Plain text",0)) a_importvls(t);} static void m_filequit(void) {if(!unsaved||areyousure("quit")) gtk_main_quit();} static void m_undo(GtkWidget*w,gpointer data) {if((uhead+UNDOS-1)%UNDOS==utail) return;undo_pop();gridchangen();syncgui();} static void m_redo(GtkWidget*w,gpointer data) {if(uhead==uhwm) return;uhead=(uhead+2)%UNDOS;undo_pop();gridchangen();syncgui();} static void m_editgprop(GtkWidget*w,gpointer data) {gpropdia();} static void m_dsprop(GtkWidget*w,gpointer data) {spropdia(1);} static void m_sprop(GtkWidget*w,gpointer data) {spropdia(0);} static void m_dlprop(GtkWidget*w,gpointer data) {lpropdia(1);} static void m_lprop(GtkWidget*w,gpointer data) {lpropdia(0);} static void m_cellcont(GtkWidget*w,gpointer data) {ccontdia();} static void m_afctreat(GtkWidget*w,gpointer data) {treatdia();} static void m_editprefs(GtkWidget*w,gpointer data) {prefsdia();} static void m_showstats(GtkWidget*w,gpointer data) {statsdia();} static void m_symm0(GtkWidget*w,gpointer data) {symmr=(intptr_t)data&0xff;} static void m_symm1(GtkWidget*w,gpointer data) {symmm=(intptr_t)data&0xff;} static void m_symm2(GtkWidget*w,gpointer data) {symmd=(intptr_t)data&0xff;} static void m_zoom(GtkWidget*w,gpointer data) {int u; u=(intptr_t)data; if(u==-1) zoomf++; else if(u==-2) zoomf--; else zoomf=u; if(zoomf<0) zoomf=0; if(zoomf>4) zoomf=4; pxsq=zoompx[zoomf]; syncgui(); } static void m_fileexport(GtkWidget*w,gpointer data) { switch((intptr_t)data) { case 0x401: if(filedia(0,"Export blank grid as EPS",".blank.eps","Encapsulated PostScript",1)) a_exportg(filename,4,0);break; case 0x402: if(filedia(0,"Export blank grid as SVG",".blank.svg","Scalable Vector Graphics",1)) a_exportg(filename,4,1);break; case 0x403: if(filedia(0,"Export blank grid as PNG",".blank.png","Portable Network Graphics",1)) a_exportg(filename,4,2);break; case 0x404: if(gshape[gtype]!=0) break; if(filedia(0,"Export blank grid as HTML",".blank.html","HyperText Markup Language",1)) a_exportgh(0x05,""); break; case 0x411: if(filedia(0,"Export filled grid as EPS",".eps","Encapsulated PostScript",1)) a_exportg(filename,lnis?6:2,0);break; case 0x412: if(filedia(0,"Export filled grid as SVG",".svg","Scalable Vector Graphics",1)) a_exportg(filename,lnis?6:2,1);break; case 0x413: if(filedia(0,"Export filled grid as PNG",".png","Portable Network Graphics",1)) a_exportg(filename,lnis?6:2,2);break; case 0x414: if(gshape[gtype]!=0) break; if(filedia(0,"Export filled grid as HTML",".html","HyperText Markup Language",1)) a_exportgh(lnis?7:3,""); break; case 0x420: if(filedia(0,"Export answers as plain text",".ans.txt","Plain text",1)) a_exporta(0);break; case 0x423: if(filedia(0,"Export answers as HTML",".ans.html","HyperText Markup Language",1)) a_exporta(1);break; case 0x433: if(gshape[gtype]!=0) break; if(filedia(0,"Export puzzle as HTML",".html","HyperText Markup Language",1)) a_exportgh(0x0d,""); break; case 0x434:if(filedia(0,"Export puzzle as HTML+SVG",".html","HyperText Markup Language",1)) a_exporthp(0,4,1);break; case 0x435:if(filedia(0,"Export puzzle as HTML+PNG",".html","HyperText Markup Language",1)) a_exporthp(0,4,2);break; case 0x443: if(gshape[gtype]!=0) break; if(filedia(0,"Export solution as HTML",".html","HyperText Markup Language",1)) a_exportgh(0x13,""); break; case 0x444:if(filedia(0,"Export solution as HTML+SVG",".html","HyperText Markup Language",1)) a_exporthp(1,lnis?6:2,1);break; case 0x445:if(filedia(0,"Export solution as HTML+PNG",".html","HyperText Markup Language",1)) a_exporthp(1,lnis?6:2,2);break; default: assert(0); } } static void selchange(void) {int i,j,d; nsel=0; if (selmode==0) for(i=0;i=100) { if(dir>=100+nvl) return; if(selmode!=2) m_selnone(w,data); selmode=2; vls[dir-100].sel=!vls[dir-100].sel; } else { if(selmode!=1) m_selnone(w,data); selmode=1; sellight(curx,cury,dir,!issellight(curx,cury,dir)); } selchange(); } // all lights parallel static void m_sellpar(GtkWidget*w,gpointer data) {int f,i,j; if(dir>=100) return; if(selmode!=1) m_selnone(w,data); selmode=1; f=issellight(curx,cury,dir); for(i=0;ichecking==1); } selchange(); } static void m_selctreat(GtkWidget*w,gpointer data) {int i,j; if(selmode!=0) m_selnone(w,data); selmode=0; for(i=0;i=100||dir>=100) refreshsel(); odir=dir; curmf=1; } static void gridchangen(void) { int d,i,j; // only squares at start of lights can have dsel information for(i=0;i0&&treat_lab[treatmode][i]) for(j=0;j0&&treat_lab[treatmode][i]) for(j=0;jflbmh; // get hints bitmap // printf("%d %d %16llx\n",x,y,m); if(onebit(m)) gsq[x][y].ctbm[i][j]=m; // could consider removing the onebit() test but potentially confusing? e++; } } refreshsqmg(x,y); } } f=0; for(i=0;i0&&treat_lab[treatmode][i]) for(j=0;jflbmh; if(b!=treatcstr[i][j]) treatcstr[i][j]=b,f=1; } } if(f) {okbox("\n Answer treatment \n constraints updated \n");} gridchange(); } // run filler static void m_autofill(GtkWidget*w,gpointer data) { int mode; if(filler_status==0) return; // already running? mode=(intptr_t)data; // selected mode (1=all, 2=selection) if(mode==2&&selmode!=0) { sel_toc(); selchange(); } if(compute(mode)) { reperr("Could not start filler"); return; } box(GTK_MESSAGE_INFO,"\n Filling in progress \n"," Stop ",0); // this box is closed manually or by the filler when it completes DEB1 printf("stopping filler...\n"); filler_stop(); DEB1 printf("stopped filler, status=%d\n",filler_status); setposslabel(""); switch(filler_status) { case -4: case -3: reperr("Error generating lists of feasible lights");break; case -2: reperr("Out of stack space");break; case -1: reperr("Out of memory");break; case 1: reperr("No fill found");break; } } // grid edit operations static void editblock (int x,int y) { symmdo(a_editblock ,0,x,y,0);gridchange();} static void editempty (int x,int y) {clrcont(x,y);symmdo(a_editempty ,0,x,y,0);gridchange();} static void editcutout(int x,int y) { symmdo(a_editcutout,0,x,y,0);gridchange();} // called from menu static void m_editblock (GtkWidget*w,gpointer data) {editblock (curx,cury);} static void m_editempty (GtkWidget*w,gpointer data) {editempty (curx,cury);} static void m_editcutout(GtkWidget*w,gpointer data) {editcutout(curx,cury);} // bar behind cursor static void m_editbarb(GtkWidget*w,gpointer data) { if(dir>=100) return; symmdo(a_editbar,!isbar(curx,cury,dir+ndir[gtype]),curx,cury,dir+ndir[gtype]); gridchange(); } // merge/join ahead static void m_editmerge(GtkWidget*w,gpointer data) { if(dir>=100) return; symmdo(a_editmerge,!ismerge(curx,cury,dir),curx,cury,dir); gridchange(); } // flip grid in main diagonal static void m_editflip(GtkWidget*w,gpointer data) {int i,j,k,t;struct square s;struct lprop l;ABM p[MXCT]; if(gshape[gtype]>2) return; for(i=0;i>1); // swap bars around else t=((t&1)<<2)|( t&2 )|((t&4)>>2); gsq[i][j].bars=t; t=gsq[i][j].merge; if(gshape[gtype]==0) t=((t&1)<<1)|((t&2)>>1); // swap merges around else t=((t&1)<<2)|( t&2 )|((t&4)>>2); gsq[i][j].merge=t; t=gsq[i][j].dsel; if(gshape[gtype]==0) t=((t&1)<<1)|((t&2)>>1); // swap directional selects around else t=((t&1)<<2)|( t&2 )|((t&4)>>2); gsq[i][j].dsel=t; k=(gshape[gtype]==0)?1:2; l=gsq[i][j].lp[0];gsq[i][j].lp[0]=gsq[i][j].lp[k];gsq[i][j].lp[k]=l; // if dechecked we want to swap the across and down contents if(getdech(i,j)) { memcpy(p,gsq[i][j].ctbm[0],sizeof(p)); memcpy(gsq[i][j].ctbm[0],gsq[i][j].ctbm[k],sizeof(p)); memcpy(gsq[i][j].ctbm[k],p,sizeof(p)); t=gsq[i][j].ctlen[0]; gsq[i][j].ctlen[0]=gsq[i][j].ctlen[k]; gsq[i][j].ctlen[k]=t; } } for(i=0;i=0 ;i--) u=gsq[i][j],gsq[i][j]=t,t=u;} gridchange(); syncgui(); } static void m_vlnew(GtkWidget*w,gpointer data) { if(nvl>=NVL) {reperr("Limit on number of\nfree lights reached");return;} m_selnone(w,data); selmode=2; vls[nvl].x[0]=curx; vls[nvl].y[0]=cury; vls[nvl].l=1; vls[nvl].sel=1; resetlp(&vls[nvl].lp); nvl++; gridchange(); } static int getselvl() {int i; if(selmode!=2) return -1; if(nsel!=1) return -1; for(i=0;i=MXCL) {reperr("Limit on length of\nfree light reached");return;} vls[i].x[vls[i].l]=curx; vls[i].y[vls[i].l]=cury; vls[i].l++; DEB1 printf("VL %d l=%d\n",i,vls[i].l); gridchange(); } static void m_vldelete(GtkWidget*w,gpointer data) {int i,j,d; if(selmode!=2) return; d=-1; for(i=0,j=0;i=100) { if(d>=0) dir=d+100; else dir=0; } nvl=j; gridchange(); } static void m_vlcurtail(GtkWidget*w,gpointer data) {int i; i=getselvl(); if(i==-1) return; if(vls[i].l<2) {m_vldelete(w,data);return;} vls[i].l--; DEB1 printf("VL %d l=%d\n",i,vls[i].l); gridchange(); } static void m_vlmodify(GtkWidget*w,gpointer data) {int i; i=getselvl(); if(i==-1) return; mvldia(i); } static void m_helpabout(GtkWidget*w,gpointer data) {char s[2560]; sprintf(s, "%s\n\n" "Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant\n" "\n" "This program is free software; you can redistribute it and/or modify " "it under the terms of version 2 of the GNU General Public License as " "published by the Free Software Foundation.\n" "\n" "This program is distributed in the hope that it will be useful, " "but WITHOUT ANY WARRANTY; without even the implied warranty of " "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along " "with this program; if not, write to the Free Software Foundation, Inc., " "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n" "\n" "For more information visit http://www.quinapalus.com/ or " "contact qxw@quinapalus.com\n" ,RELEASE); okbox(s);} static int w_destroy(void) {gtk_main_quit();return 0;} static int w_delete(void) {return !(!unsaved||areyousure("quit"));} static void moveleft(int*x,int*y) { switch(gtype) { case 0:case 1:case 2:case 6:case 8:(*x)--;break; case 7: if(*x==0) *y=height-1-*y; case 3:case 4:case 5:case 9: *x=(*x+width-1)%width; break; } } static void moveright(int*x,int*y) { switch(gtype) { case 0:case 1:case 2:case 6:case 8:(*x)++;break; case 7: if(*x==width-1) *y=height-1-*y; case 3:case 4:case 5:case 9: *x=(*x+1)%width; break; } } static void moveup(int*x,int*y) { switch(gtype) { case 0:case 1:case 2:case 3:case 4:case 5:case 7:(*y)--;break; case 8: if(*y==0) *x=width-1-*x; case 6:case 9: *y=(*y+height-1)%height; break; } } static void movedown(int*x,int*y) { switch(gtype) { case 0:case 1:case 2:case 3:case 4:case 5:case 7:(*y)++;break; case 8: if(*y==height-1) *x=width-1-*x; case 6:case 9: *y=(*y+1)%height; break; } } static void movehome(int*x,int*y) {int l,lx[MXCL],ly[MXCL]; l=getlight(lx,ly,*x,*y,dir); if(l<1) return; *x=lx[0];*y=ly[0]; } static void moveend(int*x,int*y) {int l,lx[MXCL],ly[MXCL]; l=getlight(lx,ly,*x,*y,dir); if(l<1) return; *x=lx[l-1];*y=ly[l-1]; } static void nextdir() { do{ dir++; if(dir==ndir[gtype]) dir=100; if(dir>=100+nvl) dir=0; if(dirkeyval; f=event->state; if(k>='a'&&k<='z') k+='A'-'a'; DEB1 printf("keypress event: %x %x\n",k,f); f&=12; // mask off flags except CTRL, ALT if((k==GDK_Tab&&f==0)||(((k>='A'&&k<='Z')||(k>='0'&&k<='9'))&&f==0)) { // tab and letters, digits r=1; if(setechar(curx,cury,dir,(k==GDK_Tab)?' ':k)) ccontdia(); // open dialogue if not a simple case refreshsqmg(curx,cury); refreshnum(); if(dir<100) stepforwmifingrid(&curx,&cury,dir); undo_push(); } if(k==' '&&f==0) {r=1; if(dir<100) stepforwmifingrid(&curx,&cury,dir);} if(k==GDK_Left &&f==0) {r=1; if(dir>=100) dir=0; x=curx;y=cury;moveleft (&x,&y);if(isingrid(x,y)) curx=x,cury=y;} if(k==GDK_Right&&f==0) {r=1; if(dir>=100) dir=0; x=curx;y=cury;moveright(&x,&y);if(isingrid(x,y)) curx=x,cury=y;} if(k==GDK_Up &&f==0) {r=1; if(dir>=100) dir=0; x=curx;y=cury;moveup (&x,&y);if(isingrid(x,y)) curx=x,cury=y;} if(k==GDK_Down &&f==0) {r=1; if(dir>=100) dir=0; x=curx;y=cury;movedown (&x,&y);if(isingrid(x,y)) curx=x,cury=y;} if(k==GDK_Home &&f==0) {r=1; x=curx;y=cury;movehome (&x,&y);if(isingrid(x,y)) curx=x,cury=y;} if(k==GDK_End &&f==0) {r=1; x=curx;y=cury;moveend (&x,&y);if(isingrid(x,y)) curx=x,cury=y;} if((k==GDK_Page_Up )&&f==0) {r=1; prevdir();} if((k==GDK_Page_Down||k=='/')&&f==0) {r=1; nextdir();} if(k=='.'&&f==0) {r=1; editempty (curx,cury);} if(k==','&&f==0) {r=1; editblock (curx,cury);} if(k==GDK_BackSpace&&f==0) {r=1; if(dir<100) stepbackifingrid(&curx,&cury,dir);} if(r) { // if we have processed the key, update both old and new cursor positions curmoved(); compute(0); } return r; } // convert screen coords to internal square coords; k is bitmap of sufficiently nearby edges static void ptrtosq(int*x,int*y,int*k,int x0,int y0) {double u,v,u0,v0,r=0,t=0,xa=0,ya=0,xb=0,yb=0;int i,j; u0=((double)x0-bawdpx)/pxsq;v0=((double)y0-bawdpx)/pxsq; *k=0; switch(gshape[gtype]) { case 0: i=(int)floor(u0);u=u0-i; j=(int)floor(v0);v=v0-j; *x=i;*y=j; break; case 1: i=(int)floor(u0/1.2);u=u0-i*1.2; if((i&1)==0) { j=(int)floor(v0/1.4);v=v0-j*1.4; if(u>=0.4) {*x=i;*y=j;break;} if( 0.7*u+0.4*v<0.28) {*x=i-1;*y=j-1;break;} if(-0.7*u+0.4*v>0.28) {*x=i-1;*y=j; break;} *x=i;*y=j;break; } else { j=(int)floor((v0-0.7)/1.4);v=v0-0.7-j*1.4; if(u>=0.4) {*x=i;*y=j;break;} if( 0.7*u+0.4*v<0.28) {*x=i-1;*y=j; break;} if(-0.7*u+0.4*v>0.28) {*x=i-1;*y=j+1;break;} *x=i;*y=j;break; } case 2: j=(int)floor(v0/1.2);v=v0-j*1.2; if((j&1)==0) { i=(int)floor(u0/1.4);u=u0-i*1.4; if(v>=0.4) {*y=j;*x=i;break;} if( 0.7*v+0.4*u<0.28) {*y=j-1;*x=i-1;break;} if(-0.7*v+0.4*u>0.28) {*y=j-1;*x=i; break;} *y=j;*x=i;break; } else { i=(int)floor((u0-0.7)/1.4);u=u0-0.7-i*1.4; if(v>=0.4) {*y=j;*x=i;break;} if( 0.7*v+0.4*u<0.28) {*y=j-1;*x=i; break;} if(-0.7*v+0.4*u>0.28) {*y=j-1;*x=i+1;break;} *y=j;*x=i;break; } case 3:case 4: u=u0-height;v=v0-height; r=sqrt(u*u+v*v); if(r<1e-3) {*x=-1;*y=-1;return;} r=height-r; t=atan2(u,-v)*width/2/PI; if(gtype==4) t+=.5; if(t<0) t+=width; *x=(int)floor(t); *y=(int)floor(r); break; } switch(gshape[gtype]) { // click-near-edge bitmap case 0:case 1:case 2: for(i=0;i1-PXEDGE ) *k|=2; if(r-*y< PXEDGE ) *k|=8; r=height-r; if(t-*x>1-PXEDGE/((r<2)?1:(r-1))) *k|=1; if(t-*x< PXEDGE/((r<2)?1:(r-1))) *k|=4; break; } } int dragflag=-1; // store selectedness state of square where shift-drag starts, or -1 if none in progress static void mousel(int x,int y) { // left button if(selmode!=0) m_selnone(0,0); selmode=0; if(dragflag==-1) dragflag=gsq[x][y].fl&16; // set f if at beginning of shift-drag if((gsq[x][y].fl&16)==dragflag) { selcell(x,y,!dragflag); selchange(); } } static void mouser(int x,int y) { // right button if(dir>=100) return; if(selmode!=1) m_selnone(0,0); selmode=1; if(dragflag==-1) dragflag=issellight(x,y,dir); if(issellight(x,y,dir)==dragflag) { sellight(x,y,dir,!dragflag); selchange(); } } // pointer motion static gint mousemove(GtkWidget*widget,GdkEventMotion*event) { int e,k,x,y; ptrtosq(&x,&y,&e,(int)floor(event->x+.5),(int)floor(event->y)); k=(int)(event->state); // buttons and modifiers // DEB2 printf("mouse move event (%d,%d) %d e=%02x\n",x,y,k,e); if(!isingrid(x,y)) return 0; if((k&GDK_SHIFT_MASK)==0) {dragflag=-1;return 0;} // shift not held down: reset f if (k&GDK_BUTTON3_MASK) mouser(x,y); else if(k&GDK_BUTTON1_MASK) mousel(x,y); else dragflag=-1; // button not held down: reset dragflag return 0; } // click in grid area static gint button_press_event(GtkWidget*widget,GdkEventButton*event) { int b,e,ee,k,x,y; ptrtosq(&x,&y,&e,(int)floor(event->x+.5),(int)floor(event->y)); k=event->state; DEB2 printf("button press event (%f,%f) -> (%d,%d) e=%02x button=%08x type=%08x state=%08x\n",event->x,event->y,x,y,e,(int)event->button,(int)event->type,k); if(event->type==GDK_BUTTON_RELEASE) dragflag=-1; if(event->type!=GDK_BUTTON_PRESS) goto ew0; if(k&GDK_SHIFT_MASK) { if (event->button==3) mouser(x,y); else if(event->button==1) mousel(x,y); return 0; } if(event->button!=1) goto ew0; // only left clicks do anything now if(!isingrid(x,y)) goto ew0; ee=!!(e&(e-1)); // more than one bit set in e? if(clickblock&&ee) { // flip between block and space when a square is clicked near a corner if((gsq[x][y].fl&1)==0) {editblock(x,y);gridchange();goto ew1;} else {editempty(x,y);gridchange();goto ew1;} } else if(clickbar&&e&&!ee) { // flip bar if clicked in middle of edge b=logbase2(e); DEB2 printf(" x=%d y=%d e=%d b=%d isbar(x,y,b)=%d\n",x,y,e,b,isbar(x,y,b)); symmdo(a_editbar,!isbar(x,y,b),x,y,b); gridchange(); goto ew1; } if(x==curx&&y==cury) nextdir(); // flip direction for click in square of cursor else { curx=x; cury=y; // not trapped as producing bar or block, so move cursor if(dir>=100) dir=0; } curmoved(); gridchangen(); ew1: gtk_window_set_focus(GTK_WINDOW(mainw),grid_da); ew0: DEB2 printf("Exiting button_press_event()\n"); return FALSE; } // word list entry selected static void selrow(GtkWidget*widget,gint row,gint column, GdkEventButton*event,gpointer data) { int i,l,l0,lx[MXCL],ly[MXCL],nc; ABM*abmp[MXLE]; DEB1 printf("row select event\n"); if(llistem&EM_JUM) return; // don't allow click-to-enter for jumbles l=getlight(lx,ly,curx,cury,dir); if(l<2) return; nc=getlightbmp(abmp,curx,cury,dir); if(nc<2) return; if(!llistp) return; if(row<0||row>=llistn) return; if(llistp[row]>=ltotal) return; // light building has not caught up yet, so ignore click DEB1 printf("lts[llistp[row]].s=<%s>\n",lts[llistp[row]].s); l0=strlen(lts[llistp[row]].s); if(lts[llistp[row]].tagged) l0-=NMSG; if(l0!=nc) return; for(i=0;iallocation.width,widget->allocation.height); return TRUE; } // redraw the grid area static gint expose_event(GtkWidget*widget,GdkEventExpose*event) {double u,v; cairo_t*cr; DEB4 printf("expose event x=%d y=%d w=%d h=%d\n",event->area.x,event->area.y,event->area.width,event->area.height),fflush(stdout); if(widget==grid_da) { cr=gdk_cairo_create(widget->window); cairo_rectangle(cr,event->area.x,event->area.y,event->area.width,event->area.height); cairo_clip(cr); repaint(cr); cairo_destroy(cr); if(curmf) { mgcentre(&u,&v,curx,cury,0,1); DEB1 printf("curmoved: %f %f %d %d\n",u,v,pxsq,bawdpx); gtk_adjustment_clamp_page(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(grid_sw)),(u-.5)*pxsq+bawdpx-3,(u+.5)*pxsq+bawdpx+3); // scroll window to follow cursor gtk_adjustment_clamp_page(gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(grid_sw)),(v-.5)*pxsq+bawdpx-3,(v+.5)*pxsq+bawdpx+3); curmf=0; } } return FALSE; } void invaldarect(int x0,int y0,int x1,int y1) {GdkRectangle r; r.x=x0; r.y=y0; r.width=x1-x0; r.height=y1-y0; DEB4 printf("invalidate(%d,%d - %d,%d)\n",x0,y0,x1,y1); gdk_window_invalidate_rect(grid_da->window,&r,0); } void invaldaall() { DEB4 printf("invalidate all\n"); gdk_window_invalidate_rect(grid_da->window,0,0); } // GTK DIALOGUES // general filename dialogue: button text in but, default file extension in ext // result copied to res if non-NULL, "filename" otherwise static int filedia(char*res,char*but,char*ext,char*filetype,int write) { GtkWidget*dia; int i; char*p,t[SLEN+50]; GtkFileFilter*filt0,*filt1; if(!res) res=filename; filt0=gtk_file_filter_new(); if(!filt0) return 0; gtk_file_filter_add_pattern(filt0,"*"); gtk_file_filter_set_name(filt0,"All files"); filt1=gtk_file_filter_new(); if(!filt1) return 0; strcpy(t,"*"); strcat(t,strrchr(ext,'.')); // printf("<%s>\n",t); gtk_file_filter_add_pattern(filt1,t); for(i=1;t[i];i++) t[i]=toupper(t[i]); // printf("<%s>\n",t); gtk_file_filter_add_pattern(filt1,t); strcpy(t,filetype); strcat(t," files"); gtk_file_filter_set_name(filt1,t); dia=gtk_file_chooser_dialog_new(but,0, write?GTK_FILE_CHOOSER_ACTION_SAVE:GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dia),filt1); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dia),filt0); if(strcmp(filenamebase,"")) strcpy(res,filenamebase); else strcpy(res,"untitled"); strcat(res,ext); strcpy(t,res); if(write) gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dia),t); ew0: i=gtk_dialog_run(GTK_DIALOG(dia)); i=i==GTK_RESPONSE_OK; if(i) { p=gtk_file_chooser_get_filename((GtkFileChooser*)dia); if(strlen(p)>SLEN) {reperr("Filename too long");goto ew0;} strcpy(res,p); } else strcpy(res,""); gtk_widget_destroy(dia); return i; // return 1 if got name successfully (although may be empty string) } // cell contents dialogue static int ccontdia() { GtkWidget*dia,*vb,*l0,*m[MAXNDIR],*lm[MAXNDIR],*e[MAXNDIR]; int i,u,x,y; char s[100],t[MXCT*(NL+4)]; x=curx; y=cury; getmergerep(&x,&y,x,y); if(!isclear(x,y)) {reperr("Please place the cursor on a cell\nthat can contain characters");return 1;} dia=gtk_dialog_new_with_buttons("Cell contents", GTK_WINDOW(mainw),GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,GTK_STOCK_OK,GTK_RESPONSE_OK,NULL); gtk_dialog_set_default_response(GTK_DIALOG(dia),GTK_RESPONSE_OK); vb=gtk_vbox_new(0,2); // box to hold everything gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox),vb,TRUE,TRUE,0); u=getdech(x,y); if(u) { l0=gtk_label_new("Contribution from cell"); gtk_misc_set_alignment(GTK_MISC(l0),0,0.5); gtk_box_pack_start(GTK_BOX(vb),l0,TRUE,TRUE,0); } for(i=0;ibgcol; gcbg.red =((i>>16)&255)*257; gcbg.green=((i>> 8)&255)*257; gcbg.blue =((i )&255)*257; i=sps[0]->fgcol; gcfg.red =((i>>16)&255)*257; gcfg.green=((i>> 8)&255)*257; gcfg.blue =((i )&255)*257; i=sps[0]->mkcol; gcmk.red =((i>>16)&255)*257; gcmk.green=((i>> 8)&255)*257; gcmk.blue =((i )&255)*257; dia=gtk_dialog_new_with_buttons(g?"Default cell properties":"Selected cell properties", GTK_WINDOW(mainw),GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,GTK_STOCK_APPLY,GTK_RESPONSE_OK,NULL); vb=gtk_vbox_new(0,2); // box to hold everything gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox),vb,TRUE,TRUE,0); if(!g) { sprop_or=gtk_check_button_new_with_mnemonic("_Override default cell properties"); gtk_box_pack_start(GTK_BOX(vb),sprop_or,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(sprop_or),"clicked",GTK_SIGNAL_FUNC(sprop_setactive),0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); } w=gtk_hbox_new(0,2); sprop_l0=gtk_label_new("Background colour: "); gtk_label_set_width_chars(GTK_LABEL(sprop_l0),18); gtk_misc_set_alignment(GTK_MISC(sprop_l0),1,0.5); gtk_box_pack_start(GTK_BOX(w),sprop_l0,FALSE,FALSE,0); sprop_cbg=gtk_color_button_new_with_color(&gcbg); gtk_box_pack_start(GTK_BOX(w),sprop_cbg,FALSE,FALSE,0); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w=gtk_hbox_new(0,2); sprop_l1=gtk_label_new("Foreground colour: "); gtk_label_set_width_chars(GTK_LABEL(sprop_l1),18); gtk_misc_set_alignment(GTK_MISC(sprop_l1),1,0.5); gtk_box_pack_start(GTK_BOX(w),sprop_l1,FALSE,FALSE,0); sprop_cfg=gtk_color_button_new_with_color(&gcfg); gtk_box_pack_start(GTK_BOX(w),sprop_cfg,FALSE,FALSE,0); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w=gtk_hbox_new(0,2); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); sprop_l2=gtk_label_new("Font style: "); gtk_box_pack_start(GTK_BOX(w),sprop_l2,FALSE,FALSE,0); gtk_label_set_width_chars(GTK_LABEL(sprop_l2),18); gtk_misc_set_alignment(GTK_MISC(sprop_l2),1,0.5); sprop_w20=gtk_combo_box_new_text(); gtk_box_pack_start(GTK_BOX(w),sprop_w20,FALSE,FALSE,0); gtk_combo_box_append_text(GTK_COMBO_BOX(sprop_w20),"Normal"); gtk_combo_box_append_text(GTK_COMBO_BOX(sprop_w20),"Bold"); gtk_combo_box_append_text(GTK_COMBO_BOX(sprop_w20),"Italic"); gtk_combo_box_append_text(GTK_COMBO_BOX(sprop_w20),"Bold italic"); i=sps[0]->fstyle; if(i<0) i=0; if(i>3) i=3; gtk_combo_box_set_active(GTK_COMBO_BOX(sprop_w20),i); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w=gtk_hbox_new(0,2); sprop_l4=gtk_label_new("Mark colour: "); gtk_label_set_width_chars(GTK_LABEL(sprop_l4),18); gtk_misc_set_alignment(GTK_MISC(sprop_l4),1,0.5); gtk_box_pack_start(GTK_BOX(w),sprop_l4,FALSE,FALSE,0); sprop_cmk=gtk_color_button_new_with_color(&gcmk); gtk_box_pack_start(GTK_BOX(w),sprop_cmk,FALSE,FALSE,0); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); for(i=0;i<2;i++) { w=gtk_hbox_new(0,2); for(j=0;jmk[d]); gtk_box_pack_start(GTK_BOX(w),sprop_mke[d],FALSE,FALSE,0); } gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); } w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); sprop_tr=gtk_check_button_new_with_mnemonic("_Flag for answer treatment"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sprop_tr),sps[0]->ten); gtk_box_pack_start(GTK_BOX(vb),sprop_tr,TRUE,TRUE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w=gtk_hbox_new(0,2); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); sprop_l3=gtk_label_new("Lights intersecting here "); gtk_box_pack_start(GTK_BOX(w),sprop_l3,FALSE,FALSE,0); gtk_misc_set_alignment(GTK_MISC(sprop_l3),1,0.5); sprop_w21=gtk_combo_box_new_text(); gtk_box_pack_start(GTK_BOX(w),sprop_w21,FALSE,FALSE,0); gtk_combo_box_append_text(GTK_COMBO_BOX(sprop_w21),"must agree"); gtk_combo_box_append_text(GTK_COMBO_BOX(sprop_w21),"need not agree: horizontal display"); gtk_combo_box_append_text(GTK_COMBO_BOX(sprop_w21),"need not agree: vertical display"); i=sps[0]->dech; if(i<0) i=0; if(i>2) i=2; gtk_combo_box_set_active(GTK_COMBO_BOX(sprop_w21),i); if(!g) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sprop_or),(sps[0]->spor)&1); sprop_setactive(); gtk_widget_show_all(dia); i=gtk_dialog_run(GTK_DIALOG(dia)); if(i==GTK_RESPONSE_OK) { gtk_color_button_get_color(GTK_COLOR_BUTTON(sprop_cbg),&gcbg); gtk_color_button_get_color(GTK_COLOR_BUTTON(sprop_cfg),&gcfg); gtk_color_button_get_color(GTK_COLOR_BUTTON(sprop_cmk),&gcmk); for(j=0;jspor=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sprop_or)); sps[j]->bgcol= (((gcbg.red >>8)&255)<<16)+ (((gcbg.green>>8)&255)<< 8)+ (((gcbg.blue >>8)&255) ); sps[j]->fgcol= (((gcfg.red >>8)&255)<<16)+ (((gcfg.green>>8)&255)<< 8)+ (((gcfg.blue >>8)&255) ); sps[j]->mkcol= (((gcmk.red >>8)&255)<<16)+ (((gcmk.green>>8)&255)<< 8)+ (((gcmk.blue >>8)&255) ); i=gtk_combo_box_get_active(GTK_COMBO_BOX(sprop_w20)); if(i>=0&&i<4) sps[j]->fstyle=i; sps[j]->ten=!!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sprop_tr)); i=gtk_combo_box_get_active(GTK_COMBO_BOX(sprop_w21)); if(i>=0&&i<3) sps[j]->dech=i; for(d=0;dmk[d],gtk_entry_get_text(GTK_ENTRY(sprop_mke[d])),MXMK); sps[j]->mk[d][MXMK]=0; make7bitclean(sps[j]->mk[d]); } } gridchange(); } gtk_widget_destroy(dia); refreshall(); return 1; } // light properties dialogue static GtkWidget*lprop_e[MAXNDICTS],*lprop_f[NLEM],*lprop_or,*lprop_tr,*lprop_nn; static int lprop_g; static int lprop_setactive() {int i,k; k=lprop_g||gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lprop_or)); for(i=0;ivbox),vb,TRUE,TRUE,0); if(!g) { lprop_or=gtk_check_button_new_with_mnemonic("_Override default light properties"); gtk_box_pack_start(GTK_BOX(vb),lprop_or,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(lprop_or),"clicked",GTK_SIGNAL_FUNC(lprop_setactive),0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); } for(i=0;i"); else { strcat(s,"\""); if(strlen(dafilters[i])>25) {strncat(s,dafilters[i],22); strcat(s,"...");} else strcat(s,dafilters[i]); strcat(s,"\""); } } else { if(strlen(dfnames[i])>25) {strcat(s,"...");strcat(s,dfnames[i]+strlen(dfnames[i])-22);} else strcat(s,dfnames[i]); } strcat(s,")"); lprop_e[i]=gtk_check_button_new_with_mnemonic(s); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lprop_e[i]),((lps[0]->dmask)>>i)&1); gtk_box_pack_start(GTK_BOX(vb),lprop_e[i],TRUE,TRUE,0); } w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); lprop_tr=gtk_check_button_new_with_mnemonic("_Enable answer treatment"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lprop_tr),lps[0]->ten); gtk_box_pack_start(GTK_BOX(vb),lprop_tr,TRUE,TRUE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); for(i=0;iemask)>>i)&1); gtk_box_pack_start(GTK_BOX(vb),lprop_f[i],TRUE,TRUE,0); } w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); lprop_nn=gtk_check_button_new_with_mnemonic("Light _does not receive a number"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lprop_nn),lps[0]->dnran); gtk_box_pack_start(GTK_BOX(vb),lprop_nn,TRUE,TRUE,0); if(!g) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lprop_or),(lps[0]->lpor)&1); lprop_setactive(); gtk_widget_show_all(dia); i=gtk_dialog_run(GTK_DIALOG(dia)); if(i==GTK_RESPONSE_OK) { for(j=0;jlpor=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lprop_or)); lps[j]->dmask=0; for(i=0;idmask|=1<emask=0; for(i=0;iemask|=1<ten=!!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lprop_tr)); lps[j]->dnran=!!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lprop_nn)); if(lps[j]->emask==0) { reperr("No entry methods selected:\nthis would make fill impossible.\nAllowing lights to be entered normally."); lps[j]->emask=EM_FWD; } } gridchange(); } gtk_widget_destroy(dia); return 1; } // treatment dialogue static GtkWidget*treat_a[NMSG],*treat_f,*treat_c,*treat_m[NMSG],*treat_lm[NMSG],*treat_b0,*treat_b1[NMSG],*treat_b2[NMSG],*treat_b3; static int treatcomboorder[NATREAT]={0,1,2,3,4,10,11,5,6,7,8,9}; // put misprints in the right place static int treat_browse(GtkWidget*w,gpointer p) { GtkWidget*dia; DEB1 printf("w=%p p=%p\n",w,p);fflush(stdout); dia=gtk_file_chooser_dialog_new("Choose a treatment plug-in",0, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); if(gtk_dialog_run(GTK_DIALOG(dia))==GTK_RESPONSE_OK) gtk_entry_set_text(GTK_ENTRY(treat_f),gtk_file_chooser_get_filename((GtkFileChooser*)dia)); gtk_widget_destroy(dia); return 1; } static int treat_setactive() {int i,j,k,l; k=treatcomboorder[gtk_combo_box_get_active(GTK_COMBO_BOX(treat_c))]; for(i=0;i0&&treatpermok[k][i]); gtk_label_set_text(GTK_LABEL(treat_lm[i]),treat_lab[j?k:(TREAT_PLUGIN)][i]); } gtk_widget_set_sensitive(treat_b0,k==TREAT_PLUGIN); gtk_widget_set_sensitive(treat_b3,k!=0); return 1; } static int treatdia(void) { GtkWidget*dia,*e[NMSG],*c[NMSG],*l,*b,*vb; int i,j; char s[(NL+4)*MXFL+1],*p; filler_stop(); dia=gtk_dialog_new_with_buttons("Answer treatment",GTK_WINDOW(mainw),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,GTK_STOCK_APPLY,GTK_RESPONSE_OK,NULL); vb=gtk_vbox_new(0,2); // box to hold everything gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox),vb,TRUE,TRUE,0); treat_c=gtk_combo_box_new_text(); for(i=0;i0&&s[j-1]=='?') j--; s[j]=0; // strip trailing "?"s gtk_entry_set_text(GTK_ENTRY(c[i]),s); gtk_box_pack_start(GTK_BOX(treat_b2[i]),c[i],FALSE,FALSE,0); gtk_box_pack_start(GTK_BOX(vb),treat_b2[i],TRUE,TRUE,0); } treat_b0=gtk_hbox_new(0,2); l=gtk_label_new("Treatment plug-in: "); gtk_label_set_width_chars(GTK_LABEL(l),25); gtk_misc_set_alignment(GTK_MISC(l),1,0.5); gtk_box_pack_start(GTK_BOX(treat_b0),l,FALSE,FALSE,0); treat_f=gtk_entry_new(); gtk_entry_set_max_length(GTK_ENTRY(treat_f),SLEN-1); gtk_entry_set_text(GTK_ENTRY(treat_f),tpifname); gtk_box_pack_start(GTK_BOX(treat_b0),treat_f,FALSE,FALSE,0); b=gtk_button_new_with_label("Browse..."); gtk_box_pack_start(GTK_BOX(treat_b0),b,FALSE,FALSE,0); gtk_signal_connect(GTK_OBJECT(b),"clicked",GTK_SIGNAL_FUNC(treat_browse),0); gtk_box_pack_start(GTK_BOX(vb),treat_b0,TRUE,TRUE,0); treat_b3=gtk_check_button_new_with_mnemonic("Treated answer _must be a word"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(treat_b3),tambaw); gtk_box_pack_start(GTK_BOX(vb),treat_b3,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(treat_c),"changed",GTK_SIGNAL_FUNC(treat_setactive),0); for(i=0;i2) treatorder[i]=2; if(!treatpermok[treatmode][i]) treatorder[i]=0; j=strtoabms(treatcstr[i],MXFL,(char*)gtk_entry_get_text(GTK_ENTRY(c[i])),1); for(;j=NATREAT) treatmode=0; if(treatmode==TREAT_PLUGIN) { if((p=loadtpi())) { sprintf(s,"Error loading custom plug-in\n%.200s",p); reperr(s); } } else unloadtpi(); } gridchange(); gtk_widget_destroy(dia); return 1; } // dictionary list dialogue static GtkWidget*dictl_e[MAXNDICTS]; static int dictl_browse(GtkWidget*w,gpointer p) { GtkWidget*dia; DEB1 printf("w=%p p=%p\n",w,p);fflush(stdout); dia=gtk_file_chooser_dialog_new("Choose a dictionary",0, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); if(gtk_dialog_run(GTK_DIALOG(dia))==GTK_RESPONSE_OK) gtk_entry_set_text(GTK_ENTRY(dictl_e[(intptr_t)p]),gtk_file_chooser_get_filename((GtkFileChooser*)dia)); gtk_widget_destroy(dia); return 1; } static int dictldia(void) { GtkWidget*dia,*l,*b,*t,*f0[MAXNDICTS],*f1[MAXNDICTS]; int i,j; char s[SLEN]; char tdfnames[MAXNDICTS][SLEN]; // Temporary versions of dictionary names, etc char tdsfilters[MAXNDICTS][SLEN]; char tdafilters[MAXNDICTS][SLEN]; filler_stop(); dia=gtk_dialog_new_with_buttons("Dictionaries",GTK_WINDOW(mainw),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,GTK_STOCK_APPLY,GTK_RESPONSE_OK,NULL); t=gtk_table_new(MAXNDICTS+1,5,0); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox),t,TRUE,TRUE,0); l=gtk_label_new(NULL);gtk_label_set_markup(GTK_LABEL(l),"File" ); gtk_table_attach(GTK_TABLE(t),l,1,2,0,1,0,0,0,0); l=gtk_label_new(""); gtk_table_attach(GTK_TABLE(t),l,3,4,0,1,0,0,10,0); l=gtk_label_new(NULL);gtk_label_set_markup(GTK_LABEL(l),"File filter" ); gtk_table_attach(GTK_TABLE(t),l,4,5,0,1,0,0,0,0); l=gtk_label_new(NULL);gtk_label_set_markup(GTK_LABEL(l),"Answer filter"); gtk_table_attach(GTK_TABLE(t),l,5,6,0,1,0,0,0,0); for(i=0;i%d",i+1); l=gtk_label_new(NULL); gtk_label_set_markup(GTK_LABEL(l),s); gtk_table_attach(GTK_TABLE(t),l,0,1,i+1,i+2,0,0,5,0); dictl_e[i]=gtk_entry_new(); gtk_entry_set_max_length(GTK_ENTRY(dictl_e[i]),SLEN-1); gtk_entry_set_text(GTK_ENTRY(dictl_e[i]),dfnames[i]); gtk_table_attach(GTK_TABLE(t),dictl_e[i],1,2,i+1,i+2,0,0,0,0); b=gtk_button_new_with_label("Browse..."); gtk_table_attach(GTK_TABLE(t),b,2,3,i+1,i+2,0,0,0,0); f0[i]=gtk_entry_new(); gtk_entry_set_max_length(GTK_ENTRY(f0[i]),SLEN-1); gtk_entry_set_text(GTK_ENTRY(f0[i]),dsfilters[i]); gtk_table_attach(GTK_TABLE(t),f0[i],4,5,i+1,i+2,0,0,5,0); f1[i]=gtk_entry_new(); gtk_entry_set_max_length(GTK_ENTRY(f1[i]),SLEN-1); gtk_entry_set_text(GTK_ENTRY(f1[i]),dafilters[i]); gtk_table_attach(GTK_TABLE(t),f1[i],5,6,i+1,i+2,0,0,5,0); gtk_signal_connect(GTK_OBJECT(b),"clicked",GTK_SIGNAL_FUNC(dictl_browse),(void*)(intptr_t)i); } gtk_widget_show_all(dia); for(;;) { j=gtk_dialog_run(GTK_DIALOG(dia)); if(j==GTK_RESPONSE_CANCEL||j==GTK_RESPONSE_DELETE_EVENT) goto ew0; for(j=0;jvbox),vb,TRUE,TRUE,0); t=gtk_table_new(2,2,0); gtk_box_pack_start(GTK_BOX(vb),t,TRUE,TRUE,0); l=gtk_label_new("Title: "); gtk_table_attach(GTK_TABLE(t),l,0,1,0,1,0,0,0,0); gtk_label_set_width_chars(GTK_LABEL(l),10); gtk_misc_set_alignment(GTK_MISC(l),1,0.5); ttl=gtk_entry_new(); gtk_table_attach(GTK_TABLE(t),ttl,1,2,0,1,0,0,0,0); gtk_entry_set_max_length(GTK_ENTRY(ttl),SLEN-1); gtk_entry_set_text(GTK_ENTRY(ttl),gtitle); l=gtk_label_new("Author: "); gtk_table_attach(GTK_TABLE(t),l,0,1,1,2,0,0,0,0); gtk_label_set_width_chars(GTK_LABEL(l),10); gtk_misc_set_alignment(GTK_MISC(l),1,0.5); aut=gtk_entry_new(); gtk_table_attach(GTK_TABLE(t),aut,1,2,1,2,0,0,0,0); gtk_entry_set_max_length(GTK_ENTRY(aut),SLEN-1); gtk_entry_set_text(GTK_ENTRY(aut),gauthor); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w=gtk_hbox_new(0,2); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); l=gtk_label_new("Grid type: "); gtk_box_pack_start(GTK_BOX(w),l,FALSE,FALSE,0); gtk_label_set_width_chars(GTK_LABEL(l),10); gtk_misc_set_alignment(GTK_MISC(l),1,0.5); gprop_w20=gtk_combo_box_new_text(); gtk_box_pack_start(GTK_BOX(w),gprop_w20,FALSE,FALSE,0); for(i=0;i=1&&i<=MXSZ) width=i; i=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w31)); if(i>=1&&i<=MXSZ) height=i; i=gtk_combo_box_get_active(GTK_COMBO_BOX(gprop_w20)); if(i>=0&&ivbox),vb,TRUE,TRUE,0); l=gtk_label_new(" Enter a coordinate pair for each cell in the path on a "); gtk_box_pack_start(GTK_BOX(vb),l,FALSE,TRUE,0); gtk_misc_set_alignment(GTK_MISC(l),0,0.5); l=gtk_label_new(" separate line. Coordinates are counted from zero. "); gtk_box_pack_start(GTK_BOX(vb),l,FALSE,TRUE,0); gtk_misc_set_alignment(GTK_MISC(l),0,0.5); for(i=0,p=s;i=MXSZ) x[i]=MXSZ-1; if(q==p) goto err0; p=q; while(isspace(*p)||*p==',') p++; y[i]=strtol(p,&q,10); if(y[i]<0) y[i]=0; if(y[i]>=MXSZ) y[i]=MXSZ-1; if(q==p) goto err0; p=q; } if(i>0) { vls[v].l=i; memcpy(vls[v].x,x,i*sizeof(int)); memcpy(vls[v].y,y,i*sizeof(int)); } gridchange(); } gtk_widget_destroy(dia); return 1; err0: reperr("Each line must contain\ntwo numeric coordinate values\nseparated by a comma or space"); goto ew0; err1: reperr("Free light length limit reached"); goto ew0; } // preferences dialogue static int prefsdia(void) { GtkWidget*dia,*l,*w,*w30,*w31,*w32,*w00,*w01,*w02,*w20,*w21,*w10,*w11,*w12,*w14,*vb; int i; dia=gtk_dialog_new_with_buttons("Preferences",GTK_WINDOW(mainw),GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,GTK_STOCK_APPLY,GTK_RESPONSE_OK,NULL); vb=gtk_vbox_new(0,3); // box to hold all the options gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox),vb,TRUE,TRUE,0); l=gtk_label_new(NULL);gtk_label_set_markup(GTK_LABEL(l),"Export preferences"); gtk_box_pack_start(GTK_BOX(vb),l,TRUE,TRUE,0); w=gtk_hbox_new(0,3); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); l=gtk_label_new("EPS/SVG export square size: "); gtk_box_pack_start(GTK_BOX(w),l,FALSE,FALSE,0); w30=gtk_spin_button_new_with_range(10,72,1); gtk_box_pack_start(GTK_BOX(w),w30,FALSE,FALSE,0); l=gtk_label_new(" points"); gtk_box_pack_start(GTK_BOX(w),l,FALSE,FALSE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w=gtk_hbox_new(0,3); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); l=gtk_label_new("HTML/PNG export square size: "); gtk_box_pack_start(GTK_BOX(w),l,FALSE,FALSE,0); w31=gtk_spin_button_new_with_range(10,72,1); gtk_box_pack_start(GTK_BOX(w),w31,FALSE,FALSE,0); l=gtk_label_new(" pixels"); gtk_box_pack_start(GTK_BOX(w),l,FALSE,FALSE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w32=gtk_check_button_new_with_label("Include numbers in filled grids"); gtk_box_pack_start(GTK_BOX(vb),w32,TRUE,TRUE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); l=gtk_label_new(NULL);gtk_label_set_markup(GTK_LABEL(l),"Editing preferences"); gtk_box_pack_start(GTK_BOX(vb),l,TRUE,TRUE,0); w00=gtk_check_button_new_with_label("Clicking corners makes blocks"); gtk_box_pack_start(GTK_BOX(vb),w00,TRUE,TRUE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w01=gtk_check_button_new_with_label("Clicking edges makes bars"); gtk_box_pack_start(GTK_BOX(vb),w01,TRUE,TRUE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w02=gtk_check_button_new_with_label("Show numbers and marks while editing"); gtk_box_pack_start(GTK_BOX(vb),w02,TRUE,TRUE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); l=gtk_label_new(NULL);gtk_label_set_markup(GTK_LABEL(l),"Statistics preferences"); gtk_box_pack_start(GTK_BOX(vb),l,TRUE,TRUE,0); l=gtk_label_new("Desirable checking ratios");gtk_misc_set_alignment(GTK_MISC(l),0,0.5); gtk_box_pack_start(GTK_BOX(vb),l,TRUE,TRUE,0); w=gtk_hbox_new(0,3); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); l=gtk_label_new("Minimum: "); gtk_box_pack_start(GTK_BOX(w),l,FALSE,FALSE,0); w20=gtk_spin_button_new_with_range(0,100,1); gtk_box_pack_start(GTK_BOX(w),w20,FALSE,FALSE,0); l=gtk_label_new("% Maximum: "); gtk_box_pack_start(GTK_BOX(w),l,FALSE,FALSE,0); w21=gtk_spin_button_new_with_range(0,100,1); gtk_box_pack_start(GTK_BOX(w),w21,FALSE,FALSE,0); l=gtk_label_new("% plus one cell "); gtk_box_pack_start(GTK_BOX(w),l,FALSE,FALSE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); l=gtk_label_new(NULL);gtk_label_set_markup(GTK_LABEL(l),"Autofill preferences"); gtk_box_pack_start(GTK_BOX(vb),l,TRUE,TRUE,0); w10=gtk_radio_button_new_with_label_from_widget(NULL,"Deterministic"); gtk_box_pack_start(GTK_BOX(vb),w10,TRUE,TRUE,0); w11=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(w10),"Slightly randomised"); gtk_box_pack_start(GTK_BOX(vb),w11,TRUE,TRUE,0); w12=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(w11),"Highly randomised"); gtk_box_pack_start(GTK_BOX(vb),w12,TRUE,TRUE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w14=gtk_check_button_new_with_label("Prevent duplicate answers and lights"); gtk_box_pack_start(GTK_BOX(vb),w14,TRUE,TRUE,0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); // set widgets from current preferences values gtk_spin_button_set_value(GTK_SPIN_BUTTON(w30),eptsq); gtk_spin_button_set_value(GTK_SPIN_BUTTON(w31),hpxsq); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w32),lnis); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w00),clickblock); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w01),clickbar); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w02),shownums); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w14),afunique); gtk_spin_button_set_value(GTK_SPIN_BUTTON(w20),mincheck); gtk_spin_button_set_value(GTK_SPIN_BUTTON(w21),maxcheck); if(afrandom==0) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w10),1); if(afrandom==1) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w11),1); if(afrandom==2) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w12),1); gtk_widget_show_all(dia); i=gtk_dialog_run(GTK_DIALOG(dia)); if(i==GTK_RESPONSE_OK) { // set preferences values back from values (with bounds checking) eptsq=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w30)); if(eptsq<10) eptsq=10; if(eptsq>72) eptsq=72; hpxsq=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w31)); if(hpxsq<10) hpxsq=10; if(hpxsq>72) hpxsq=72; lnis=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w32))&1; clickblock=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w00))&1; clickbar=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w01))&1; shownums=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w02))&1; mincheck=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w20)); if(mincheck< 0) mincheck= 0; if(mincheck>100) mincheck=100; maxcheck=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w21)); if(maxcheck< 0) maxcheck= 0; if(maxcheck>100) maxcheck=100; afunique=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w14)); if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w10))) afrandom=0; if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w11))) afrandom=1; if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w12))) afrandom=2; saveprefs(); // save to preferences file (failing silently) } gtk_widget_destroy(dia); compute(0); // because of new checking ratios invaldaall(); return 1; } // update statistics window if in view // main widget table is st_te[][], rows below in st_r[] void stats_upd(void) { int i,j; char s[SLEN]; if(!stats) return; for(i=2;i<=MXLE;i++) { // one row for each word length if(st_lc[i]>0) { sprintf(s,"%d",i); gtk_label_set_text(GTK_LABEL(st_te[i][0]),s); sprintf(s,"%d",st_lc[i]); gtk_label_set_text(GTK_LABEL(st_te[i][1]),s); sprintf(s,st_lucc[i]?"%d (%.1f%%)":" ",st_lucc[i],100.0*st_lucc[i]/st_lc[i]); gtk_label_set_text(GTK_LABEL(st_te[i][2]),s); sprintf(s,st_locc[i]?"%d (%.1f%%)":" ",st_locc[i],100.0*st_locc[i]/st_lc[i]); gtk_label_set_text(GTK_LABEL(st_te[i][3]),s); sprintf(s,st_lc[i]?"%.2f:%.2f:%.2f":" " ,1.0*st_lmnc[i]/i,1.0*st_lsc[i]/st_lc[i]/i,1.0*st_lmxc[i]/i); gtk_label_set_text(GTK_LABEL(st_te[i][4]),s); for(j=0;j<5;j++) gtk_widget_show(st_te[i][j]); // show row if non-empty... } else for(j=0;j<5;j++) {gtk_label_set_text(GTK_LABEL(st_te[i][j])," ");gtk_widget_hide(st_te[i][j]);} // ... and hide row if empty } sprintf(s," Total lights: %d",nw); gtk_label_set_text(GTK_LABEL(st_r[0]),s); if(nw>0) sprintf(s," Mean length: %.1f",(double)nc/nw); else strcpy (s," Mean length: -"); gtk_label_set_text(GTK_LABEL(st_r[1]),s); if(nc>0 ) sprintf(s," Checked light letters: %d/%d (%.1f%%)",st_sc,nc,100.0*st_sc/nc); else strcpy (s," Checked light letters: -"); gtk_label_set_text(GTK_LABEL(st_r[2]),s); if(ne0>0) sprintf(s," Checked grid cells: %d/%d (%.1f%%)",st_ce,ne0,100.0*st_ce/ne0); else strcpy (s," Checked grid cells: -"); gtk_label_set_text(GTK_LABEL(st_r[3]),s); sprintf(s," Lights with double unches: %d",st_2u-st_3u); gtk_label_set_text(GTK_LABEL(st_r[4]),s); sprintf(s," Lights with triple, quadruple etc. unches: %d",st_3u); gtk_label_set_text(GTK_LABEL(st_r[5]),s); sprintf(s," Lights too long for filler: %d",st_tlf); gtk_label_set_text(GTK_LABEL(st_r[6]),s); sprintf(s," Total free lights: %d",nvl); gtk_label_set_text(GTK_LABEL(st_r[7]),s); sprintf(s," Free lights too long for filler: %d",st_vltlf); gtk_label_set_text(GTK_LABEL(st_r[8]),s); if(st_tmtlf) sprintf(s," There are too many treated lights to use filler discretionary modes"); else sprintf(s," "); gtk_label_set_text(GTK_LABEL(st_r[9]),s); if(hist_da->window) gdk_window_invalidate_rect(hist_da->window,0,0); } // histogram update static gint hist_configure_event(GtkWidget*widget,GdkEventConfigure*event) { DEB4 printf("hist config event: new w=%d h=%d\n",widget->allocation.width,widget->allocation.height); return TRUE; } // draw one block of histogram (if non-empty), returning new x coordinate static int drawhblock(cairo_t*cr,int x,int l,int n,double r,double g,double b) { int y0=230,w=12; int h,i,m,u,v; char s[SLEN]; u=0; for(i=0;im) m=st_hist[i]; // max in histogram for(i=0;iarea.x,event->area.y,event->area.width,event->area.height),fflush(stdout); if(widget==hist_da) { cr=gdk_cairo_create(widget->window); cairo_rectangle(cr,event->area.x,event->area.y,event->area.width,event->area.height); cairo_clip(cr); drawhist(cr); cairo_destroy(cr); } return FALSE; } // create statistics dialogue static void stats_init(void) { int i,j; GtkWidget*w0,*w1,*nb,*vb0,*vb1; for(i=0;ivbox),nb,FALSE,TRUE,0); vb0=gtk_vbox_new(0,3); gtk_notebook_append_page(GTK_NOTEBOOK(nb),vb0,gtk_label_new("General")); w0=gtk_table_new(MXLE+2,5,FALSE); st_te[0][0]=gtk_label_new(" Length "); st_te[0][1]=gtk_label_new(" Count "); st_te[0][2]=gtk_label_new(" Underchecked "); st_te[0][3]=gtk_label_new(" Overchecked "); st_te[0][4]=gtk_label_new(" Check ratio min:mean:max "); for(j=0;j<5;j++) gtk_widget_show(st_te[0][j]); w1=gtk_hseparator_new();gtk_table_attach_defaults(GTK_TABLE(w0),w1,0,5,1,2);gtk_widget_show(w1); for(i=2;i<=MXLE;i++) for(j=0;j<5;j++) st_te[i][j]=gtk_label_new(""); // initialise all table entries to empty strings for(i=0;i<=MXLE;i++) for(j=0;j<5;j++) if(st_te[i][j]) gtk_table_attach_defaults(GTK_TABLE(w0),st_te[i][j],j,j+1,i,i+1); w1=gtk_hseparator_new();gtk_table_attach_defaults(GTK_TABLE(w0),w1,0,5,MXLE+1,MXLE+2);gtk_widget_show(w1); gtk_box_pack_start(GTK_BOX(vb0),w0,FALSE,TRUE,0);gtk_widget_show(w0); for(j=0;j<10;j++) { st_r[j]=gtk_label_new(" "); // blank rows at the bottom for now gtk_misc_set_alignment(GTK_MISC(st_r[j]),0,0.5); gtk_box_pack_start(GTK_BOX(vb0),st_r[j],FALSE,TRUE,0); gtk_widget_show(st_r[j]); } vb1=gtk_vbox_new(0,3); gtk_notebook_append_page(GTK_NOTEBOOK(nb),vb1,gtk_label_new("Entry histogram")); // drawing area for histogram and events it captures hist_da=gtk_drawing_area_new(); gtk_drawing_area_size(GTK_DRAWING_AREA(hist_da),588,250); gtk_widget_set_events(hist_da,GDK_EXPOSURE_MASK); gtk_signal_connect(GTK_OBJECT(hist_da),"expose_event",GTK_SIGNAL_FUNC(hist_expose_event),NULL); gtk_signal_connect(GTK_OBJECT(hist_da),"configure_event",GTK_SIGNAL_FUNC(hist_configure_event),NULL); gtk_box_pack_start(GTK_BOX(vb1),hist_da,FALSE,TRUE,0);gtk_widget_show(hist_da); gtk_widget_show(hist_da); gtk_widget_show(vb1); gtk_widget_show(vb0); gtk_widget_show(nb); } // remove statistics window (if not already gone or in the process of going) static void stats_quit(GtkDialog*w,int i0,void*p0) { DEB4 printf("stats_quit()\n"),fflush(stdout); if(i0!=GTK_RESPONSE_DELETE_EVENT&&stats!=NULL) gtk_widget_destroy(stats); stats=NULL; DEB4 printf("stats_quit()\n"),fflush(stdout); } // open stats window if not already open, and update it static int statsdia(void) { if(!stats) stats_init(); stats_upd(); gtk_widget_show(stats); gtk_signal_connect(GTK_OBJECT(stats),"response",GTK_SIGNAL_FUNC(stats_quit),NULL); // does repeating this matter? return 0; } void killcurrdia(void) { if(currentdia) gtk_dialog_response(GTK_DIALOG(currentdia),GTK_RESPONSE_CANCEL); // kill the current dialogue box } void setposslabel(char*s) { gtk_label_set_text(GTK_LABEL(poss_label),s); } // update feasible list to screen void updatefeas(void) {int i; char t0[MXFL*3+100],t1[MXLE+50],*u[2],p0[SLEN],p1[SLEN]; DEB1 printf("updatefeas()\n"); u[0]=t0; u[1]=t1; p1[0]='\0'; gtk_clist_freeze(GTK_CLIST(clist)); // avoid glitchy-looking updates gtk_clist_clear(GTK_CLIST(clist)); if(!isclear(curx,cury)) goto ew0; for(i=0;iscore)); // negative ans values cannot occur here gtk_clist_append(GTK_CLIST(clist),u); } if(getechar(curx,cury)==' ') { if(gsq[curx][cury].e0->flbm==0) strcpy(p0,""); else getposs(gsq[curx][cury].e0,p0,0,0); // get feasible letter list with dash suppressed if(strlen(p0)==0) sprintf(p1," No feasible characters"); else sprintf(p1," Feasible character%s: %s",(strlen(p0)==1)?"":"s",p0); } else p1[0]=0; ew0: gtk_clist_thaw(GTK_CLIST(clist)); // make list visible DEB1 printf("Setting poss label to >%s<\n",p1); setposslabel(p1); return; } static void syncselmenu() { DEB1 printf("syncselmenu mode=%d,n=%d\n",selmode,nsel); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf100),selmode==0&&nsel>0); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf201),selmode==2&&nsel==1); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf202),selmode==2&&nsel==1); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf203),selmode==2&&nsel==1); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf204),selmode==2&&nsel>0); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf300),selmode==0&&nsel>0); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf301),(selmode==1||selmode==2)&&nsel>0); } static void syncsymmmenu() {int i,m; gtk_menu_item_activate((GtkMenuItem*)gtk_item_factory_get_widget_by_action(item_factory,0x100+symmr)); gtk_menu_item_activate((GtkMenuItem*)gtk_item_factory_get_widget_by_action(item_factory,0x200+symmm)); gtk_menu_item_activate((GtkMenuItem*)gtk_item_factory_get_widget_by_action(item_factory,0x300+symmd)); m=symmrmask(); for(i=1;i<=12;i++) gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0x100+i),(m>>i)&1); m=symmmmask(); for(i=0;i<= 3;i++) gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0x200+i),(m>>i)&1); m=symmdmask(); for(i=0;i<= 3;i++) gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0x300+i),(m>>i)&1); i=(gshape[gtype]==3||gshape[gtype]==4); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf000),!i); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf001), i); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0xf002), i); i=(gshape[gtype]>0); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0x404),!i); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0x414),!i); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0x433),!i); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0x443),!i); } // sync GUI with possibly new grid properties, flags, symmetry, width, height void syncgui(void) { if(!isingrid(curx,cury)) {curx=0,cury=0;} // keep cursor in grid if(dir>=ndir[gtype]&&dir<100) dir=0; // make sure direction is feasible if(dir>=100+nvl) dir=0; gtk_drawing_area_size(GTK_DRAWING_AREA(grid_da),dawidth()+3,daheight()+3); gtk_widget_show(grid_da); syncsymmmenu(); syncselmenu(); setwintitle(); draw_init(); refreshall(0,shownums?7:3); curmoved(); } // menus static GtkItemFactoryEntry menu_items[] = { { "/_File", 0, 0, 0, ""}, { "/File/_New", 0, 0, 0, ""}, { "/File/New/Current shape and size", "N", m_filenew, 0, "",GTK_STOCK_NEW}, { "/File/New/Blank 9x9", 0, m_filenew, 0x090980}, { "/File/New/Blank 10x10", 0, m_filenew, 0x0a0a80}, { "/File/New/Blank 11x11", 0, m_filenew, 0x0b0b80}, { "/File/New/Blank 11x13", 0, m_filenew, 0x0b0d80}, { "/File/New/Blank 12x12", 0, m_filenew, 0x0c0c80}, { "/File/New/Blank 13x11", 0, m_filenew, 0x0d0b80}, { "/File/New/Blank 13x13", 0, m_filenew, 0x0d0d80}, { "/File/New/Blank 14x14", 0, m_filenew, 0x0e0e80}, { "/File/New/Blank 15x15", 0, m_filenew, 0x0f0f80}, { "/File/New/Blocked 13x13 template", 0, 0, 0, ""}, { "/File/New/Blocked 13x13 template/No unches on edges", 0, m_filenew, 0x0d0d00}, { "/File/New/Blocked 13x13 template/Unches left and right", 0, m_filenew, 0x0d0d01}, { "/File/New/Blocked 13x13 template/Unches top and bottom", 0, m_filenew, 0x0d0d02}, { "/File/New/Blocked 13x13 template/Unches on all edges", 0, m_filenew, 0x0d0d03}, { "/File/New/Blocked 15x15 template", 0, 0, 0, ""}, { "/File/New/Blocked 15x15 template/No unches on edges", 0, m_filenew, 0x0f0f00}, { "/File/New/Blocked 15x15 template/Unches left and right", 0, m_filenew, 0x0f0f01}, { "/File/New/Blocked 15x15 template/Unches top and bottom", 0, m_filenew, 0x0f0f02}, { "/File/New/Blocked 15x15 template/Unches on all edges", 0, m_filenew, 0x0f0f03}, { "/File/New/Blocked 17x17 template", 0, 0, 0, ""}, { "/File/New/Blocked 17x17 template/No unches on edges", 0, m_filenew, 0x111100}, { "/File/New/Blocked 17x17 template/Unches left and right", 0, m_filenew, 0x111101}, { "/File/New/Blocked 17x17 template/Unches top and bottom", 0, m_filenew, 0x111102}, { "/File/New/Blocked 17x17 template/Unches on all edges", 0, m_filenew, 0x111103}, { "/File/New/Blocked 19x19 template", 0, 0, 0, ""}, { "/File/New/Blocked 19x19 template/No unches on edges", 0, m_filenew, 0x131300}, { "/File/New/Blocked 19x19 template/Unches left and right", 0, m_filenew, 0x131301}, { "/File/New/Blocked 19x19 template/Unches top and bottom", 0, m_filenew, 0x131302}, { "/File/New/Blocked 19x19 template/Unches on all edges", 0, m_filenew, 0x131303}, { "/File/New/Blocked 21x21 template", 0, 0, 0, ""}, { "/File/New/Blocked 21x21 template/No unches on edges", 0, m_filenew, 0x151500}, { "/File/New/Blocked 21x21 template/Unches left and right", 0, m_filenew, 0x151501}, { "/File/New/Blocked 21x21 template/Unches top and bottom", 0, m_filenew, 0x151502}, { "/File/New/Blocked 21x21 template/Unches on all edges", 0, m_filenew, 0x151503}, { "/File/New/Blocked 23x23 template", 0, 0, 0, ""}, { "/File/New/Blocked 23x23 template/No unches on edges", 0, m_filenew, 0x171700}, { "/File/New/Blocked 23x23 template/Unches left and right", 0, m_filenew, 0x171701}, { "/File/New/Blocked 23x23 template/Unches top and bottom", 0, m_filenew, 0x171702}, { "/File/New/Blocked 23x23 template/Unches on all edges", 0, m_filenew, 0x171703}, { "/File/New/Blocked 25x25 template", 0, 0, 0, ""}, { "/File/New/Blocked 25x25 template/No unches on edges", 0, m_filenew, 0x191900}, { "/File/New/Blocked 25x25 template/Unches left and right", 0, m_filenew, 0x191901}, { "/File/New/Blocked 25x25 template/Unches top and bottom", 0, m_filenew, 0x191902}, { "/File/New/Blocked 25x25 template/Unches on all edges", 0, m_filenew, 0x191903}, { "/File/sep0", 0, 0, 0, ""}, { "/File/_Open...", "O", m_fileopen, 0, "",GTK_STOCK_OPEN}, { "/File/_Save", "S", m_filesave, 0, "",GTK_STOCK_SAVE}, { "/File/Save _as...", 0, m_filesaveas, 0, "",GTK_STOCK_SAVE_AS}, { "/File/sep1", 0, 0, 0, ""}, { "/File/Export _blank grid image", 0, 0, 0, ""}, { "/File/Export blank grid image/as _EPS...", 0, m_fileexport, 0x401}, { "/File/Export blank grid image/as _SVG...", 0, m_fileexport, 0x402}, { "/File/Export blank grid image/as _PNG...", 0, m_fileexport, 0x403}, { "/File/Export blank grid image/as _HTML...", 0, m_fileexport, 0x404}, { "/File/Export _filled grid image", 0, 0, 0, ""}, { "/File/Export filled grid image/as _EPS...", 0, m_fileexport, 0x411}, { "/File/Export filled grid image/as _SVG...", 0, m_fileexport, 0x412}, { "/File/Export filled grid image/as _PNG...", 0, m_fileexport, 0x413}, { "/File/Export filled grid image/as _HTML...", 0, m_fileexport, 0x414}, { "/File/Export ans_wers", 0, 0, 0, ""}, { "/File/Export answers/As _text...", 0, m_fileexport, 0x420}, { "/File/Export answers/As _HTML...", 0, m_fileexport, 0x423}, { "/File/Export _puzzle", 0, 0, 0, ""}, { "/File/Export puzzle/As _HTML...", 0, m_fileexport, 0x433}, { "/File/Export puzzle/As HTML+_SVG...", 0, m_fileexport, 0x434}, { "/File/Export puzzle/As HTML+_PNG...", 0, m_fileexport, 0x435}, { "/File/Export so_lution", 0, 0, 0, ""}, { "/File/Export solution/As _HTML...", 0, m_fileexport, 0x443}, { "/File/Export solution/As HTML+_SVG...", 0, m_fileexport, 0x444}, { "/File/Export solution/As HTML+_PNG...", 0, m_fileexport, 0x445}, { "/File/sep2", 0, 0, 0, ""}, { "/File/I_mport free light paths", 0, m_importvls}, { "/File/E_xport free light paths", 0, m_exportvls}, { "/File/sep3", 0, 0, 0, ""}, { "/File/_Quit", "Q", m_filequit, 0, "",GTK_STOCK_QUIT}, { "/_Edit", 0, 0, 0, ""}, { "/Edit/_Undo", "Z", m_undo, 0, "",GTK_STOCK_UNDO}, { "/Edit/_Redo", "Y", m_redo, 0, "",GTK_STOCK_REDO}, { "/Edit/sep1", 0, 0, 0, ""}, { "/Edit/_Solid block", "Insert", m_editblock}, { "/Edit/_Bar before", "Return", m_editbarb}, { "/Edit/_Empty", "Delete", m_editempty}, { "/Edit/_Cutout", "C", m_editcutout}, { "/Edit/_Merge with next", "M", m_editmerge}, { "/Edit/sep2", 0, 0, 0, ""}, { "/Edit/Cell c_ontents...", "I", m_cellcont, 0}, { "/Edit/Clear _all cells", "X", m_eraseall, 0, "",GTK_STOCK_CLEAR}, { "/Edit/C_lear selected cells", "X", m_erasesel, 0xf100,"",GTK_STOCK_CLEAR}, { "/Edit/sep3", 0, 0, 0, ""}, { "/Edit/_Free light", 0, 0, 0, ""}, { "/Edit/Free light/_Start new", 0, m_vlnew, 0xf200}, { "/Edit/Free light/_Extend selected", "E", m_vlextend, 0xf201}, { "/Edit/Free light/_Shorten selected", "D", m_vlcurtail, 0xf202}, { "/Edit/Free light/_Modify selected", 0, m_vlmodify, 0xf203}, { "/Edit/Free light/_Delete selected", 0, m_vldelete, 0xf204}, { "/Edit/sep4", 0, 0, 0, ""}, { "/Edit/Flip in main _diagonal", 0, m_editflip, 0xf000}, { "/Edit/Rotate cloc_kwise", "greater", m_editrot, 0xf001}, { "/Edit/Rotate a_nticlockwise", "less", m_editrot, 0xf002}, { "/Edit/sep5", 0, 0, 0, ""}, { "/Edit/_Zoom", 0, 0, 0, ""}, { "/Edit/Zoom/_Out", "minus", m_zoom, -2}, { "/Edit/Zoom/_1 50%", "8", m_zoom, 0}, { "/Edit/Zoom/_2 71%", "9", m_zoom, 1}, { "/Edit/Zoom/_3 100%", "0", m_zoom, 2}, { "/Edit/Zoom/_4 141%", "1", m_zoom, 3}, { "/Edit/Zoom/_5 200%", "2", m_zoom, 4}, { "/Edit/Zoom/_In", "plus", m_zoom, -1}, { "/Edit/Show s_tatistics", 0, m_showstats}, { "/Edit/_Preferences...", 0, m_editprefs, 0, "", GTK_STOCK_PREFERENCES}, { "/_Properties", 0, 0, 0, ""}, { "/Properties/_Grid properties...", 0, m_editgprop, 0, "",GTK_STOCK_PROPERTIES}, { "/Properties/Default _cell properties...", 0, m_dsprop, 0}, { "/Properties/Selected c_ell properties...", 0, m_sprop, 0xf300}, { "/Properties/Default _light properties...", 0, m_dlprop, 0}, { "/Properties/Selected l_ight properties...", 0, m_lprop, 0xf301}, { "/_Select", 0, 0, 0, ""}, { "/Select/Current _cell", "C", m_selcell}, { "/Select/Current _light", "L", m_sellight}, { "/Select/Cell _mode <> light mode", "M", m_selmode}, { "/Select/_Free light", "F", m_selfvl}, { "/Select/sep0", 0, 0, 0, ""}, { "/Select/_All", "A", m_selall}, { "/Select/_Invert", "I", m_selinv}, { "/Select/_Nothing", "N", m_selnone}, { "/Select/sep1", 0, 0, 0, ""}, { "/Select/Cell_s", 0, 0, 0, ""}, { "/Select/Cells/overriding default _properties", 0, m_selcover}, { "/Select/Cells/flagged for _answer treatment", 0, m_selctreat}, { "/Select/Cells/that are _unchecked", 0, m_selcunch}, { "/Select/Li_ghts", 0, 0, 0, ""}, { "/Select/Lights/_in current direction", 0, m_sellpar}, { "/Select/Lights/overriding default _properties", 0, m_sellover}, { "/Select/Lights/with answer treatment _enabled", 0, m_selltreat}, { "/Select/Lights/with _double or more unches", 0, m_selviol, 1}, { "/Select/Lights/with _triple or more unches", 0, m_selviol, 2}, { "/Select/Lights/that are _underchecked", 0, m_selviol, 4}, { "/Select/Lights/that are _overchecked", 0, m_selviol, 8}, { "/Sy_mmetry", 0, 0, 0, ""}, { "/Symmetry/No rotational", 0, m_symm0, 0x0101,""}, { "/Symmetry/Twofold rotational", 0, m_symm0, 0x0102,"/Symmetry/No rotational" }, { "/Symmetry/Threefold rotational", 0, m_symm0, 0x0103,"/Symmetry/Twofold rotational" }, { "/Symmetry/Fourfold rotational", 0, m_symm0, 0x0104,"/Symmetry/Threefold rotational" }, { "/Symmetry/Fivefold rotational", 0, m_symm0, 0x0105,"/Symmetry/Fourfold rotational" }, { "/Symmetry/Sixfold rotational", 0, m_symm0, 0x0106,"/Symmetry/Fivefold rotational" }, { "/Symmetry/Sevenfold rotational", 0, m_symm0, 0x0107,"/Symmetry/Sixfold rotational" }, { "/Symmetry/Eightfold rotational", 0, m_symm0, 0x0108,"/Symmetry/Sevenfold rotational" }, { "/Symmetry/Ninefold rotational", 0, m_symm0, 0x0109,"/Symmetry/Eightfold rotational" }, { "/Symmetry/Tenfold rotational", 0, m_symm0, 0x010a,"/Symmetry/Ninefold rotational" }, { "/Symmetry/Elevenfold rotational", 0, m_symm0, 0x010b,"/Symmetry/Tenfold rotational" }, { "/Symmetry/Twelvefold rotational", 0, m_symm0, 0x010c,"/Symmetry/Elevenfold rotational" }, { "/Symmetry/sep1", 0, 0, 0, ""}, { "/Symmetry/No mirror", 0, m_symm1, 0x0200,""}, { "/Symmetry/Left-right mirror", 0, m_symm1, 0x0201,"/Symmetry/No mirror"}, { "/Symmetry/Up-down mirror", 0, m_symm1, 0x0202,"/Symmetry/Left-right mirror"}, { "/Symmetry/Both", 0, m_symm1, 0x0203,"/Symmetry/Up-down mirror"}, { "/Symmetry/sep2", 0, 0, 0, ""}, { "/Symmetry/No duplication", 0, m_symm2, 0x0300,""}, { "/Symmetry/Left-right duplication", 0, m_symm2, 0x0301,"/Symmetry/No duplication"}, { "/Symmetry/Up-down duplication", 0, m_symm2, 0x0302,"/Symmetry/Left-right duplication"}, { "/Symmetry/Both", 0, m_symm2, 0x0303,"/Symmetry/Up-down duplication"}, { "/_Autofill", 0, 0, 0, ""}, { "/Autofill/_Dictionaries...", 0, m_dictionaries}, { "/Autofill/Answer _treatment...", 0, m_afctreat}, { "/Autofill/sep1", 0, 0, 0, ""}, { "/Autofill/Auto_fill", "G", m_autofill, 1, "",GTK_STOCK_EXECUTE}, { "/Autofill/Autofill _selected cells", "G", m_autofill, 2, "",GTK_STOCK_EXECUTE}, { "/Autofill/Accept _hints", "A", m_accept}, { "/Help", 0, 0, 0, ""}, { "/Help/About", 0, m_helpabout, 0, "",GTK_STOCK_ABOUT}, }; // build main window and other initialisation void startgtk(void) { GtkAccelGroup*accel_group; GtkWidget *list_sw,*vbox,*hbox,*paned,*menubar,*zmin,*zmout; // main window and content pxsq=zoompx[zoomf]; mainw=gtk_window_new(GTK_WINDOW_TOPLEVEL); // main window gtk_widget_set_name(mainw,"Qxw"); gtk_window_set_default_size(GTK_WINDOW(mainw),780,560); gtk_window_set_title(GTK_WINDOW(mainw),"Qxw"); gtk_window_set_position(GTK_WINDOW(mainw),GTK_WIN_POS_CENTER); // box in the window vbox=gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(mainw),vbox); // menu in the vbox accel_group=gtk_accel_group_new(); item_factory=gtk_item_factory_new(GTK_TYPE_MENU_BAR,"
",accel_group); gtk_item_factory_create_items(item_factory,sizeof(menu_items)/sizeof(menu_items[0]),menu_items,NULL); gtk_window_add_accel_group(GTK_WINDOW(mainw),accel_group); menubar=gtk_item_factory_get_widget(item_factory,"
"); gtk_box_pack_start(GTK_BOX(vbox),menubar,FALSE,TRUE,0); // window is divided into two parts, or `panes' paned=gtk_hpaned_new(); gtk_box_pack_start(GTK_BOX(vbox),paned,TRUE,TRUE,0); // scrolled windows in the panes grid_sw=gtk_scrolled_window_new(NULL,NULL); gtk_container_set_border_width(GTK_CONTAINER(grid_sw),10); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(grid_sw),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); gtk_paned_pack1(GTK_PANED(paned),grid_sw,1,1); list_sw=gtk_scrolled_window_new(NULL,NULL); gtk_container_set_border_width(GTK_CONTAINER(list_sw),10); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(list_sw),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); gtk_paned_pack2(GTK_PANED(paned),list_sw,0,1); gtk_paned_set_position(GTK_PANED(paned),560); // drawing area for grid and events it captures grid_da=gtk_drawing_area_new(); gtk_drawing_area_size(GTK_DRAWING_AREA(grid_da),100,100); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(grid_sw),grid_da); GTK_WIDGET_SET_FLAGS(grid_da,GTK_CAN_FOCUS); gtk_widget_set_events(grid_da,GDK_EXPOSURE_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|GDK_KEY_PRESS_MASK|GDK_POINTER_MOTION_MASK); // list of feasible words // clist=gtk_clist_new(2); clist=gtk_clist_new(1); gtk_clist_set_column_width(GTK_CLIST(clist),0,180); // gtk_clist_set_column_width(GTK_CLIST(clist),1,80); gtk_clist_set_column_title(GTK_CLIST(clist),0,"Feasible words"); // gtk_clist_set_column_title(GTK_CLIST(clist),1,"Scores"); gtk_clist_column_titles_passive(GTK_CLIST(clist)); gtk_clist_column_titles_show(GTK_CLIST(clist)); gtk_clist_set_selection_mode(GTK_CLIST(clist),GTK_SELECTION_SINGLE); gtk_container_add(GTK_CONTAINER(list_sw),clist); // box for widgets across the bottom of the window hbox=gtk_hbox_new(FALSE,0); zmout=gtk_button_new(); gtk_button_set_image(GTK_BUTTON(zmout),gtk_image_new_from_stock(GTK_STOCK_ZOOM_OUT,GTK_ICON_SIZE_MENU)); gtk_box_pack_start(GTK_BOX(hbox),zmout,FALSE,FALSE,0); gtk_signal_connect(GTK_OBJECT(zmout),"clicked",GTK_SIGNAL_FUNC(m_zoom),(gpointer)-2); zmin =gtk_button_new(); gtk_button_set_image(GTK_BUTTON(zmin ),gtk_image_new_from_stock(GTK_STOCK_ZOOM_IN ,GTK_ICON_SIZE_MENU)); gtk_box_pack_start(GTK_BOX(hbox),zmin ,FALSE,FALSE,0); gtk_signal_connect(GTK_OBJECT(zmin ),"clicked",GTK_SIGNAL_FUNC(m_zoom),(gpointer)-1); poss_label=gtk_label_new(" Feasible characters:"); gtk_box_pack_start(GTK_BOX(hbox),poss_label,FALSE,FALSE,0); gtk_box_pack_end(GTK_BOX(vbox),hbox,FALSE,FALSE,0); gtk_signal_connect(GTK_OBJECT(grid_da),"expose_event",GTK_SIGNAL_FUNC(expose_event),NULL); gtk_signal_connect(GTK_OBJECT(grid_da),"configure_event",GTK_SIGNAL_FUNC(configure_event),NULL); gtk_signal_connect(GTK_OBJECT(clist),"select_row",GTK_SIGNAL_FUNC(selrow),NULL); gtk_signal_connect(GTK_OBJECT(grid_da),"button_press_event",GTK_SIGNAL_FUNC(button_press_event),NULL); gtk_signal_connect(GTK_OBJECT(grid_da),"button_release_event",GTK_SIGNAL_FUNC(button_press_event),NULL); gtk_signal_connect_after(GTK_OBJECT(grid_da),"key_press_event",GTK_SIGNAL_FUNC(keypress),NULL); gtk_signal_connect(GTK_OBJECT(grid_da),"motion_notify_event",GTK_SIGNAL_FUNC(mousemove),NULL); gtk_signal_connect(GTK_OBJECT(mainw),"delete_event",GTK_SIGNAL_FUNC(w_delete),NULL); gtk_signal_connect(GTK_OBJECT(mainw),"destroy",GTK_SIGNAL_FUNC(w_destroy),NULL); gtk_widget_show_all(mainw); gtk_window_set_focus(GTK_WINDOW(mainw),grid_da); } void stopgtk(void) { } qxw-20140331/changelog.gz0000640000175000017500000000221512317044644012672 0ustar momoRchangelogVr6+vr3b;tJNh";%%1hILiMO pv|q5X~Շ.::~stX2֤%OZXAWk#fm5YӮS$tpՎX]RaeRaG=nH:",ABh\KMwGЭ,}`.&>$WoQ-E@S0Uʻcηc$<)ik^QCxx˳\3CMJ0!9pFo| F*P{6I6166& LC_j 1p[8|׼WbiـXx 3瓇+7tǻR6J.$;tFQj3H7 Q'*;a%A 4'\B{mT7>>zRg<֜Y-.P?/?d4'Q!DTppP]eMģ]f]M.ʀ3NTC8ܺqxYK~ 8nY\H5KZ Qz;Eu{=zH5|0>WhH i#V8Hq={qSǝ;AaT%zW'~Dt5ۦ_ xۑ0KNG4L J+]t~}V=]JN^EZ ox/5v `>3Q#-ޗ 5w`&MC`b;G{ÛGvuFWDd1x0q[ldSKa or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern int dir,curx,cury; extern int unsaved; extern int symmr,symmd,symmm; extern int gtype,ndir[NGTYPE],gshape[NGTYPE]; extern int width,height; extern char gtitle[SLEN]; extern char gauthor[SLEN]; extern int uhead,utail,uhwm; extern int st_lc[MXLE+1]; extern int st_lucc[MXLE+1]; extern int st_locc[MXLE+1]; extern int st_lsc[MXLE+1]; extern int st_lmnc[MXLE+1]; extern int st_lmxc[MXLE+1]; extern int st_hist[NL+2]; extern int st_sc; extern int st_ce; extern int st_2u,st_3u; extern int st_tlf,st_vltlf,st_tmtlf; extern struct sprop dsp; extern struct lprop dlp; // functions called by grid filler extern void updatefeas(void); extern void updategrid(void); extern void mkfeas(void); // interface functions to gsq[][] extern int getflags(int x,int y); extern int getbgcol(int x,int y); extern int getfgcol(int x,int y); extern int getmkcol(int x,int y); extern int getfstyle(int x,int y); extern int getdech(int x,int y); extern int getnumber(int x,int y); extern void getmk(char*s,int x,int y,int c); extern void a_load(void); extern void a_save(void); extern void a_importvls(char*fn); extern void a_exportvls(char*fn); extern void a_filenew(int flags); extern void saveprefs(void); extern void a_editblock (int k,int x,int y,int d); extern void a_editcutout(int k,int x,int y,int d); extern void a_editempty (int k,int x,int y,int d); extern void a_editmerge (int k,int x,int y,int d); extern void a_editbar (int k,int x,int y,int d); extern int symmrmask(void); extern int symmmmask(void); extern int symmdmask(void); extern int cbits(ABM x); extern int logbase2(ABM x); extern void undo_push(void); extern void undo_pop(void); extern char abmtoechar(ABM b); extern void abmtocs(char*s,ABM b,int inv,int dash); extern void abmtostr(char*s,ABM b,int dash); extern void abmstodispstr(char*s,ABM*b,int l); extern void abmstostr(char*s,ABM*b,int l,int dash); extern int strtoabms(ABM*p,int l,char*s,int dash); extern int getlightd(int*lx,int*ly,int x,int y,int d); extern int getlightbmp(ABM**p,int x,int y,int d); extern int getlight(int*lx,int*ly,int x,int y,int d); extern int isstartoflight(int x,int y,int d); extern int issellight(int x,int y,int d); extern void sellight(int x,int y,int d,int k); extern int isclear(int x,int y); extern int isbar(int x,int y,int d); extern int ismerge(int x,int y,int d); extern int isingrid(int x,int y); extern int sqexists(int i,int j); extern int clearbefore(int x,int y,int d); extern int clearafter(int x,int y,int d); extern int getword(int x,int y,int d,char*s); extern int getmergegroupd(int*gx,int*gy,int x,int y,int d); extern int getmergegroup(int*gx,int*gy,int x,int y); extern int getmergedir(int x,int y); extern void getmergerep(int*mx,int*my,int x,int y); extern int isownmergerep(int x,int y); extern void getmergerepd(int*mx,int*my,int x,int y,int d); extern int compute(int mode); extern void symmdo(void f(int,int,int,int),int k,int x,int y,int d); extern char getechar(int x,int y); extern int setechar(int x,int y,int d,char c); extern void clrcont(int x,int y); extern void stepback(int*x,int*y,int d); extern void donumbers(void); extern int stepbackifingrid (int*x,int*y,int d); extern void stepforw (int*x,int*y,int d); extern int stepforwmifingrid(int*x,int*y,int d); extern char*titlebyauthor(void); extern int preexport(void); extern void postexport(void); extern void make7bitclean(char*s); extern int onebit(ABM x); extern void resetlp(struct lprop*lp); qxw-20140331/examples/0000750000175000017500000000000012317044644012216 5ustar momoqxw-20140331/examples/letters-latent.qxw0000640000175000017500000002751412142412606015731 0ustar momo#QXW2 http://www.quinapalus.com GP 0 12 12 4 0 0 TTL + AUT + GLP 1 1 0 0 GSP ffffff 000000 0 0 TM 0 7 0 + TMSG 0 0 +betterawittyfool TMSG 0 1 + DFN 0 +/usr/share/dict/words DFN 1 + DFN 2 + DFN 3 + DFN 4 + DFN 5 + DFN 6 + DFN 7 + DFN 8 + DSF 0 +^.*+(? or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // gcc -Wall -fPIC plugin_behead.c -o plugin_behead.so -shared #include int treat(const char*answer) { strcpy(light,answer+1); return treatedanswer(light); } qxw-20140331/examples/torus-filled.qxw0000640000175000017500000004010012142412606015355 0ustar momo#QXW2v1 http://www.quinapalus.com GP 9 18 9 1 0 0 TTL + AUT + GLP 1 1 0 0 GSP ffffff 000000 0 0 0 0 TM 0 0 0 + TMSG 0 0 + TMSG 0 1 + DFN 0 +/usr/share/dict/words DFN 1 + DFN 2 + DFN 3 + DFN 4 + DFN 5 + DFN 6 + DFN 7 + DFN 8 + DSF 0 + DSF 1 + DSF 2 + DSF 3 + DSF 4 + DSF 5 + DSF 6 + DSF 7 + DSF 8 + DAF 0 + DAF 1 + DAF 2 + DAF 3 + DAF 4 + DAF 5 + DAF 6 + DAF 7 + DAF 8 + SQ 0 0 2 0 0 S SQ 1 0 0 0 0 N SQ 2 0 2 0 0 E SQ 3 0 0 0 0 E SQ 4 0 2 0 0 Z SQ 5 0 0 0 0 I SQ 6 0 0 0 0 E SQ 7 0 1 0 0 R SQ 8 0 1 0 0 E SQ 9 0 2 0 0 M SQ 10 0 0 0 0 A SQ 11 0 2 0 0 H SQ 12 0 0 0 0 R SQ 13 0 2 0 0 A SQ 14 0 0 0 0 T SQ 15 0 0 0 0 T SQ 16 0 1 0 0 A SQ 17 0 1 0 0 L SQ 0 1 2 0 0 N SQ 1 1 0 0 0 T SQ 2 1 2 0 0 I SQ 3 1 0 0 0 L SQ 4 1 0 0 0 E SQ 5 1 1 0 0 S SQ 6 1 1 0 0 G SQ 7 1 2 0 0 S SQ 8 1 0 0 0 A SQ 9 1 2 0 0 N SQ 10 1 0 0 0 N SQ 11 1 2 0 0 Y SQ 12 1 0 0 0 A SQ 13 1 0 0 0 S SQ 14 1 1 0 0 I SQ 15 1 1 0 0 R SQ 16 1 2 0 0 P SQ 17 1 0 0 0 A SQ 0 2 2 0 0 R SQ 1 2 0 0 0 A SQ 2 2 0 0 0 T SQ 3 2 1 0 0 O SQ 4 2 1 0 0 V SQ 5 2 2 0 0 M SQ 6 2 0 0 0 O SQ 7 2 2 0 0 R SQ 8 2 0 0 0 D SQ 9 2 2 0 0 A SQ 10 2 0 0 0 N SQ 11 2 0 0 0 C SQ 12 2 1 0 0 Y SQ 13 2 1 0 0 E SQ 14 2 2 0 0 C SQ 15 2 0 0 0 A SQ 16 2 2 0 0 S SQ 17 2 0 0 0 T SQ 0 3 0 0 0 N SQ 1 3 1 0 0 G SQ 2 3 1 0 0 O SQ 3 3 2 0 0 N SQ 4 3 0 0 0 E SQ 5 3 2 0 0 S SQ 6 3 0 0 0 T SQ 7 3 2 0 0 L SQ 8 3 0 0 0 I SQ 9 3 0 0 0 K SQ 10 3 1 0 0 E SQ 11 3 1 0 0 A SQ 12 3 2 0 0 S SQ 13 3 0 0 0 L SQ 14 3 2 0 0 E SQ 15 3 0 0 0 D SQ 16 3 2 0 0 D SQ 17 3 0 0 0 I SQ 0 4 1 0 0 E SQ 1 4 2 0 0 S SQ 2 4 0 0 0 P SQ 3 4 2 0 0 O SQ 4 4 0 0 0 N SQ 5 4 2 0 0 S SQ 6 4 0 0 0 I SQ 7 4 0 0 0 O SQ 8 4 1 0 0 N SQ 9 4 1 0 0 A SQ 10 4 2 0 0 W SQ 11 4 0 0 0 I SQ 12 4 2 0 0 R SQ 13 4 0 0 0 E SQ 14 4 2 0 0 S SQ 15 4 0 0 0 E SQ 16 4 0 0 0 W SQ 17 4 1 0 0 N SQ 0 5 0 0 0 U SQ 1 5 2 0 0 I SQ 2 5 0 0 0 L SQ 3 5 2 0 0 D SQ 4 5 0 0 0 E SQ 5 5 0 0 0 R SQ 6 5 1 0 0 S SQ 7 5 1 0 0 U SQ 8 5 2 0 0 G SQ 9 5 0 0 0 R SQ 10 5 2 0 0 A SQ 11 5 0 0 0 N SQ 12 5 2 0 0 D SQ 13 5 0 0 0 C SQ 14 5 0 0 0 R SQ 15 5 1 0 0 U SQ 16 5 1 0 0 I SQ 17 5 2 0 0 G SQ 0 6 0 0 0 T SQ 1 6 2 0 0 H SQ 2 6 0 0 0 E SQ 3 6 0 0 0 E SQ 4 6 1 0 0 S SQ 5 6 1 0 0 E SQ 6 6 2 0 0 E SQ 7 6 0 0 0 S SQ 8 6 2 0 0 C SQ 9 6 0 0 0 A SQ 10 6 2 0 0 P SQ 11 6 0 0 0 I SQ 12 6 0 0 0 S SQ 13 6 1 0 0 T SQ 14 6 1 0 0 H SQ 15 6 2 0 0 P SQ 16 6 0 0 0 R SQ 17 6 2 0 0 I SQ 0 7 0 0 0 E SQ 1 7 0 0 0 S SQ 2 7 1 0 0 S SQ 3 7 1 0 0 C SQ 4 7 2 0 0 T SQ 5 7 0 0 0 A SQ 6 7 2 0 0 N SQ 7 7 0 0 0 T SQ 8 7 2 0 0 R SQ 9 7 0 0 0 I SQ 10 7 0 0 0 S SQ 11 7 1 0 0 T SQ 12 7 1 0 0 U SQ 13 7 2 0 0 S SQ 14 7 0 0 0 A SQ 15 7 2 0 0 F SQ 16 7 0 0 0 E SQ 17 7 2 0 0 N SQ 0 8 1 0 0 R SQ 1 8 1 0 0 O SQ 2 8 2 0 0 S SQ 3 8 0 0 0 H SQ 4 8 2 0 0 E SQ 5 8 0 0 0 L SQ 6 8 2 0 0 L SQ 7 8 0 0 0 E SQ 8 8 0 0 0 R SQ 9 8 1 0 0 S SQ 10 8 1 0 0 P SQ 11 8 2 0 0 E SQ 12 8 0 0 0 N SQ 13 8 2 0 0 F SQ 14 8 0 0 0 E SQ 15 8 2 0 0 T SQ 16 8 0 0 0 T SQ 17 8 0 0 0 E SQSP 0 0 ffffff 000000 0 0 0 0 SQSP 1 0 ffffff 000000 0 0 0 0 SQSP 2 0 ffffff 000000 0 0 0 0 SQSP 3 0 ffffff 000000 0 0 0 0 SQSP 4 0 ffffff 000000 0 0 0 0 SQSP 5 0 ffffff 000000 0 0 0 0 SQSP 6 0 ffffff 000000 0 0 0 0 SQSP 7 0 ffffff 000000 0 0 0 0 SQSP 8 0 ffffff 000000 0 0 0 0 SQSP 9 0 ffffff 000000 0 0 0 0 SQSP 10 0 ffffff 000000 0 0 0 0 SQSP 11 0 ffffff 000000 0 0 0 0 SQSP 12 0 ffffff 000000 0 0 0 0 SQSP 13 0 ffffff 000000 0 0 0 0 SQSP 14 0 ffffff 000000 0 0 0 0 SQSP 15 0 ffffff 000000 0 0 0 0 SQSP 16 0 ffffff 000000 0 0 0 0 SQSP 17 0 ffffff 000000 0 0 0 0 SQSP 0 1 ffffff 000000 0 0 0 0 SQSP 1 1 ffffff 000000 0 0 0 0 SQSP 2 1 ffffff 000000 0 0 0 0 SQSP 3 1 ffffff 000000 0 0 0 0 SQSP 4 1 ffffff 000000 0 0 0 0 SQSP 5 1 ffffff 000000 0 0 0 0 SQSP 6 1 ffffff 000000 0 0 0 0 SQSP 7 1 ffffff 000000 0 0 0 0 SQSP 8 1 ffffff 000000 0 0 0 0 SQSP 9 1 ffffff 000000 0 0 0 0 SQSP 10 1 ffffff 000000 0 0 0 0 SQSP 11 1 ffffff 000000 0 0 0 0 SQSP 12 1 ffffff 000000 0 0 0 0 SQSP 13 1 ffffff 000000 0 0 0 0 SQSP 14 1 ffffff 000000 0 0 0 0 SQSP 15 1 ffffff 000000 0 0 0 0 SQSP 16 1 ffffff 000000 0 0 0 0 SQSP 17 1 ffffff 000000 0 0 0 0 SQSP 0 2 ffffff 000000 0 0 0 0 SQSP 1 2 ffffff 000000 0 0 0 0 SQSP 2 2 ffffff 000000 0 0 0 0 SQSP 3 2 ffffff 000000 0 0 0 0 SQSP 4 2 ffffff 000000 0 0 0 0 SQSP 5 2 ffffff 000000 0 0 0 0 SQSP 6 2 ffffff 000000 0 0 0 0 SQSP 7 2 ffffff 000000 0 0 0 0 SQSP 8 2 ffffff 000000 0 0 0 0 SQSP 9 2 ffffff 000000 0 0 0 0 SQSP 10 2 ffffff 000000 0 0 0 0 SQSP 11 2 ffffff 000000 0 0 0 0 SQSP 12 2 ffffff 000000 0 0 0 0 SQSP 13 2 ffffff 000000 0 0 0 0 SQSP 14 2 ffffff 000000 0 0 0 0 SQSP 15 2 ffffff 000000 0 0 0 0 SQSP 16 2 ffffff 000000 0 0 0 0 SQSP 17 2 ffffff 000000 0 0 0 0 SQSP 0 3 ffffff 000000 0 0 0 0 SQSP 1 3 ffffff 000000 0 0 0 0 SQSP 2 3 ffffff 000000 0 0 0 0 SQSP 3 3 ffffff 000000 0 0 0 0 SQSP 4 3 ffffff 000000 0 0 0 0 SQSP 5 3 ffffff 000000 0 0 0 0 SQSP 6 3 ffffff 000000 0 0 0 0 SQSP 7 3 ffffff 000000 0 0 0 0 SQSP 8 3 ffffff 000000 0 0 0 0 SQSP 9 3 ffffff 000000 0 0 0 0 SQSP 10 3 ffffff 000000 0 0 0 0 SQSP 11 3 ffffff 000000 0 0 0 0 SQSP 12 3 ffffff 000000 0 0 0 0 SQSP 13 3 ffffff 000000 0 0 0 0 SQSP 14 3 ffffff 000000 0 0 0 0 SQSP 15 3 ffffff 000000 0 0 0 0 SQSP 16 3 ffffff 000000 0 0 0 0 SQSP 17 3 ffffff 000000 0 0 0 0 SQSP 0 4 ffffff 000000 0 0 0 0 SQSP 1 4 ffffff 000000 0 0 0 0 SQSP 2 4 ffffff 000000 0 0 0 0 SQSP 3 4 ffffff 000000 0 0 0 0 SQSP 4 4 ffffff 000000 0 0 0 0 SQSP 5 4 ffffff 000000 0 0 0 0 SQSP 6 4 ffffff 000000 0 0 0 0 SQSP 7 4 ffffff 000000 0 0 0 0 SQSP 8 4 ffffff 000000 0 0 0 0 SQSP 9 4 ffffff 000000 0 0 0 0 SQSP 10 4 ffffff 000000 0 0 0 0 SQSP 11 4 ffffff 000000 0 0 0 0 SQSP 12 4 ffffff 000000 0 0 0 0 SQSP 13 4 ffffff 000000 0 0 0 0 SQSP 14 4 ffffff 000000 0 0 0 0 SQSP 15 4 ffffff 000000 0 0 0 0 SQSP 16 4 ffffff 000000 0 0 0 0 SQSP 17 4 ffffff 000000 0 0 0 0 SQSP 0 5 ffffff 000000 0 0 0 0 SQSP 1 5 ffffff 000000 0 0 0 0 SQSP 2 5 ffffff 000000 0 0 0 0 SQSP 3 5 ffffff 000000 0 0 0 0 SQSP 4 5 ffffff 000000 0 0 0 0 SQSP 5 5 ffffff 000000 0 0 0 0 SQSP 6 5 ffffff 000000 0 0 0 0 SQSP 7 5 ffffff 000000 0 0 0 0 SQSP 8 5 ffffff 000000 0 0 0 0 SQSP 9 5 ffffff 000000 0 0 0 0 SQSP 10 5 ffffff 000000 0 0 0 0 SQSP 11 5 ffffff 000000 0 0 0 0 SQSP 12 5 ffffff 000000 0 0 0 0 SQSP 13 5 ffffff 000000 0 0 0 0 SQSP 14 5 ffffff 000000 0 0 0 0 SQSP 15 5 ffffff 000000 0 0 0 0 SQSP 16 5 ffffff 000000 0 0 0 0 SQSP 17 5 ffffff 000000 0 0 0 0 SQSP 0 6 ffffff 000000 0 0 0 0 SQSP 1 6 ffffff 000000 0 0 0 0 SQSP 2 6 ffffff 000000 0 0 0 0 SQSP 3 6 ffffff 000000 0 0 0 0 SQSP 4 6 ffffff 000000 0 0 0 0 SQSP 5 6 ffffff 000000 0 0 0 0 SQSP 6 6 ffffff 000000 0 0 0 0 SQSP 7 6 ffffff 000000 0 0 0 0 SQSP 8 6 ffffff 000000 0 0 0 0 SQSP 9 6 ffffff 000000 0 0 0 0 SQSP 10 6 ffffff 000000 0 0 0 0 SQSP 11 6 ffffff 000000 0 0 0 0 SQSP 12 6 ffffff 000000 0 0 0 0 SQSP 13 6 ffffff 000000 0 0 0 0 SQSP 14 6 ffffff 000000 0 0 0 0 SQSP 15 6 ffffff 000000 0 0 0 0 SQSP 16 6 ffffff 000000 0 0 0 0 SQSP 17 6 ffffff 000000 0 0 0 0 SQSP 0 7 ffffff 000000 0 0 0 0 SQSP 1 7 ffffff 000000 0 0 0 0 SQSP 2 7 ffffff 000000 0 0 0 0 SQSP 3 7 ffffff 000000 0 0 0 0 SQSP 4 7 ffffff 000000 0 0 0 0 SQSP 5 7 ffffff 000000 0 0 0 0 SQSP 6 7 ffffff 000000 0 0 0 0 SQSP 7 7 ffffff 000000 0 0 0 0 SQSP 8 7 ffffff 000000 0 0 0 0 SQSP 9 7 ffffff 000000 0 0 0 0 SQSP 10 7 ffffff 000000 0 0 0 0 SQSP 11 7 ffffff 000000 0 0 0 0 SQSP 12 7 ffffff 000000 0 0 0 0 SQSP 13 7 ffffff 000000 0 0 0 0 SQSP 14 7 ffffff 000000 0 0 0 0 SQSP 15 7 ffffff 000000 0 0 0 0 SQSP 16 7 ffffff 000000 0 0 0 0 SQSP 17 7 ffffff 000000 0 0 0 0 SQSP 0 8 ffffff 000000 0 0 0 0 SQSP 1 8 ffffff 000000 0 0 0 0 SQSP 2 8 ffffff 000000 0 0 0 0 SQSP 3 8 ffffff 000000 0 0 0 0 SQSP 4 8 ffffff 000000 0 0 0 0 SQSP 5 8 ffffff 000000 0 0 0 0 SQSP 6 8 ffffff 000000 0 0 0 0 SQSP 7 8 ffffff 000000 0 0 0 0 SQSP 8 8 ffffff 000000 0 0 0 0 SQSP 9 8 ffffff 000000 0 0 0 0 SQSP 10 8 ffffff 000000 0 0 0 0 SQSP 11 8 ffffff 000000 0 0 0 0 SQSP 12 8 ffffff 000000 0 0 0 0 SQSP 13 8 ffffff 000000 0 0 0 0 SQSP 14 8 ffffff 000000 0 0 0 0 SQSP 15 8 ffffff 000000 0 0 0 0 SQSP 16 8 ffffff 000000 0 0 0 0 SQSP 17 8 ffffff 000000 0 0 0 0 SQLP 0 0 0 1 1 0 0 SQLP 0 0 1 1 1 0 0 SQLP 1 0 0 1 1 0 0 SQLP 1 0 1 1 1 0 0 SQLP 2 0 0 1 1 0 0 SQLP 2 0 1 1 1 0 0 SQLP 3 0 0 1 1 0 0 SQLP 3 0 1 1 1 0 0 SQLP 4 0 0 1 1 0 0 SQLP 4 0 1 1 1 0 0 SQLP 5 0 0 1 1 0 0 SQLP 5 0 1 1 1 0 0 SQLP 6 0 0 1 1 0 0 SQLP 6 0 1 1 1 0 0 SQLP 7 0 0 1 1 0 0 SQLP 7 0 1 1 1 0 0 SQLP 8 0 0 1 1 0 0 SQLP 8 0 1 1 1 0 0 SQLP 9 0 0 1 1 0 0 SQLP 9 0 1 1 1 0 0 SQLP 10 0 0 1 1 0 0 SQLP 10 0 1 1 1 0 0 SQLP 11 0 0 1 1 0 0 SQLP 11 0 1 1 1 0 0 SQLP 12 0 0 1 1 0 0 SQLP 12 0 1 1 1 0 0 SQLP 13 0 0 1 1 0 0 SQLP 13 0 1 1 1 0 0 SQLP 14 0 0 1 1 0 0 SQLP 14 0 1 1 1 0 0 SQLP 15 0 0 1 1 0 0 SQLP 15 0 1 1 1 0 0 SQLP 16 0 0 1 1 0 0 SQLP 16 0 1 1 1 0 0 SQLP 17 0 0 1 1 0 0 SQLP 17 0 1 1 1 0 0 SQLP 0 1 0 1 1 0 0 SQLP 0 1 1 1 1 0 0 SQLP 1 1 0 1 1 0 0 SQLP 1 1 1 1 1 0 0 SQLP 2 1 0 1 1 0 0 SQLP 2 1 1 1 1 0 0 SQLP 3 1 0 1 1 0 0 SQLP 3 1 1 1 1 0 0 SQLP 4 1 0 1 1 0 0 SQLP 4 1 1 1 1 0 0 SQLP 5 1 0 1 1 0 0 SQLP 5 1 1 1 1 0 0 SQLP 6 1 0 1 1 0 0 SQLP 6 1 1 1 1 0 0 SQLP 7 1 0 1 1 0 0 SQLP 7 1 1 1 1 0 0 SQLP 8 1 0 1 1 0 0 SQLP 8 1 1 1 1 0 0 SQLP 9 1 0 1 1 0 0 SQLP 9 1 1 1 1 0 0 SQLP 10 1 0 1 1 0 0 SQLP 10 1 1 1 1 0 0 SQLP 11 1 0 1 1 0 0 SQLP 11 1 1 1 1 0 0 SQLP 12 1 0 1 1 0 0 SQLP 12 1 1 1 1 0 0 SQLP 13 1 0 1 1 0 0 SQLP 13 1 1 1 1 0 0 SQLP 14 1 0 1 1 0 0 SQLP 14 1 1 1 1 0 0 SQLP 15 1 0 1 1 0 0 SQLP 15 1 1 1 1 0 0 SQLP 16 1 0 1 1 0 0 SQLP 16 1 1 1 1 0 0 SQLP 17 1 0 1 1 0 0 SQLP 17 1 1 1 1 0 0 SQLP 0 2 0 1 1 0 0 SQLP 0 2 1 1 1 0 0 SQLP 1 2 0 1 1 0 0 SQLP 1 2 1 1 1 0 0 SQLP 2 2 0 1 1 0 0 SQLP 2 2 1 1 1 0 0 SQLP 3 2 0 1 1 0 0 SQLP 3 2 1 1 1 0 0 SQLP 4 2 0 1 1 0 0 SQLP 4 2 1 1 1 0 0 SQLP 5 2 0 1 1 0 0 SQLP 5 2 1 1 1 0 0 SQLP 6 2 0 1 1 0 0 SQLP 6 2 1 1 1 0 0 SQLP 7 2 0 1 1 0 0 SQLP 7 2 1 1 1 0 0 SQLP 8 2 0 1 1 0 0 SQLP 8 2 1 1 1 0 0 SQLP 9 2 0 1 1 0 0 SQLP 9 2 1 1 1 0 0 SQLP 10 2 0 1 1 0 0 SQLP 10 2 1 1 1 0 0 SQLP 11 2 0 1 1 0 0 SQLP 11 2 1 1 1 0 0 SQLP 12 2 0 1 1 0 0 SQLP 12 2 1 1 1 0 0 SQLP 13 2 0 1 1 0 0 SQLP 13 2 1 1 1 0 0 SQLP 14 2 0 1 1 0 0 SQLP 14 2 1 1 1 0 0 SQLP 15 2 0 1 1 0 0 SQLP 15 2 1 1 1 0 0 SQLP 16 2 0 1 1 0 0 SQLP 16 2 1 1 1 0 0 SQLP 17 2 0 1 1 0 0 SQLP 17 2 1 1 1 0 0 SQLP 0 3 0 1 1 0 0 SQLP 0 3 1 1 1 0 0 SQLP 1 3 0 1 1 0 0 SQLP 1 3 1 1 1 0 0 SQLP 2 3 0 1 1 0 0 SQLP 2 3 1 1 1 0 0 SQLP 3 3 0 1 1 0 0 SQLP 3 3 1 1 1 0 0 SQLP 4 3 0 1 1 0 0 SQLP 4 3 1 1 1 0 0 SQLP 5 3 0 1 1 0 0 SQLP 5 3 1 1 1 0 0 SQLP 6 3 0 1 1 0 0 SQLP 6 3 1 1 1 0 0 SQLP 7 3 0 1 1 0 0 SQLP 7 3 1 1 1 0 0 SQLP 8 3 0 1 1 0 0 SQLP 8 3 1 1 1 0 0 SQLP 9 3 0 1 1 0 0 SQLP 9 3 1 1 1 0 0 SQLP 10 3 0 1 1 0 0 SQLP 10 3 1 1 1 0 0 SQLP 11 3 0 1 1 0 0 SQLP 11 3 1 1 1 0 0 SQLP 12 3 0 1 1 0 0 SQLP 12 3 1 1 1 0 0 SQLP 13 3 0 1 1 0 0 SQLP 13 3 1 1 1 0 0 SQLP 14 3 0 1 1 0 0 SQLP 14 3 1 1 1 0 0 SQLP 15 3 0 1 1 0 0 SQLP 15 3 1 1 1 0 0 SQLP 16 3 0 1 1 0 0 SQLP 16 3 1 1 1 0 0 SQLP 17 3 0 1 1 0 0 SQLP 17 3 1 1 1 0 0 SQLP 0 4 0 1 1 0 0 SQLP 0 4 1 1 1 0 0 SQLP 1 4 0 1 1 0 0 SQLP 1 4 1 1 1 0 0 SQLP 2 4 0 1 1 0 0 SQLP 2 4 1 1 1 0 0 SQLP 3 4 0 1 1 0 0 SQLP 3 4 1 1 1 0 0 SQLP 4 4 0 1 1 0 0 SQLP 4 4 1 1 1 0 0 SQLP 5 4 0 1 1 0 0 SQLP 5 4 1 1 1 0 0 SQLP 6 4 0 1 1 0 0 SQLP 6 4 1 1 1 0 0 SQLP 7 4 0 1 1 0 0 SQLP 7 4 1 1 1 0 0 SQLP 8 4 0 1 1 0 0 SQLP 8 4 1 1 1 0 0 SQLP 9 4 0 1 1 0 0 SQLP 9 4 1 1 1 0 0 SQLP 10 4 0 1 1 0 0 SQLP 10 4 1 1 1 0 0 SQLP 11 4 0 1 1 0 0 SQLP 11 4 1 1 1 0 0 SQLP 12 4 0 1 1 0 0 SQLP 12 4 1 1 1 0 0 SQLP 13 4 0 1 1 0 0 SQLP 13 4 1 1 1 0 0 SQLP 14 4 0 1 1 0 0 SQLP 14 4 1 1 1 0 0 SQLP 15 4 0 1 1 0 0 SQLP 15 4 1 1 1 0 0 SQLP 16 4 0 1 1 0 0 SQLP 16 4 1 1 1 0 0 SQLP 17 4 0 1 1 0 0 SQLP 17 4 1 1 1 0 0 SQLP 0 5 0 1 1 0 0 SQLP 0 5 1 1 1 0 0 SQLP 1 5 0 1 1 0 0 SQLP 1 5 1 1 1 0 0 SQLP 2 5 0 1 1 0 0 SQLP 2 5 1 1 1 0 0 SQLP 3 5 0 1 1 0 0 SQLP 3 5 1 1 1 0 0 SQLP 4 5 0 1 1 0 0 SQLP 4 5 1 1 1 0 0 SQLP 5 5 0 1 1 0 0 SQLP 5 5 1 1 1 0 0 SQLP 6 5 0 1 1 0 0 SQLP 6 5 1 1 1 0 0 SQLP 7 5 0 1 1 0 0 SQLP 7 5 1 1 1 0 0 SQLP 8 5 0 1 1 0 0 SQLP 8 5 1 1 1 0 0 SQLP 9 5 0 1 1 0 0 SQLP 9 5 1 1 1 0 0 SQLP 10 5 0 1 1 0 0 SQLP 10 5 1 1 1 0 0 SQLP 11 5 0 1 1 0 0 SQLP 11 5 1 1 1 0 0 SQLP 12 5 0 1 1 0 0 SQLP 12 5 1 1 1 0 0 SQLP 13 5 0 1 1 0 0 SQLP 13 5 1 1 1 0 0 SQLP 14 5 0 1 1 0 0 SQLP 14 5 1 1 1 0 0 SQLP 15 5 0 1 1 0 0 SQLP 15 5 1 1 1 0 0 SQLP 16 5 0 1 1 0 0 SQLP 16 5 1 1 1 0 0 SQLP 17 5 0 1 1 0 0 SQLP 17 5 1 1 1 0 0 SQLP 0 6 0 1 1 0 0 SQLP 0 6 1 1 1 0 0 SQLP 1 6 0 1 1 0 0 SQLP 1 6 1 1 1 0 0 SQLP 2 6 0 1 1 0 0 SQLP 2 6 1 1 1 0 0 SQLP 3 6 0 1 1 0 0 SQLP 3 6 1 1 1 0 0 SQLP 4 6 0 1 1 0 0 SQLP 4 6 1 1 1 0 0 SQLP 5 6 0 1 1 0 0 SQLP 5 6 1 1 1 0 0 SQLP 6 6 0 1 1 0 0 SQLP 6 6 1 1 1 0 0 SQLP 7 6 0 1 1 0 0 SQLP 7 6 1 1 1 0 0 SQLP 8 6 0 1 1 0 0 SQLP 8 6 1 1 1 0 0 SQLP 9 6 0 1 1 0 0 SQLP 9 6 1 1 1 0 0 SQLP 10 6 0 1 1 0 0 SQLP 10 6 1 1 1 0 0 SQLP 11 6 0 1 1 0 0 SQLP 11 6 1 1 1 0 0 SQLP 12 6 0 1 1 0 0 SQLP 12 6 1 1 1 0 0 SQLP 13 6 0 1 1 0 0 SQLP 13 6 1 1 1 0 0 SQLP 14 6 0 1 1 0 0 SQLP 14 6 1 1 1 0 0 SQLP 15 6 0 1 1 0 0 SQLP 15 6 1 1 1 0 0 SQLP 16 6 0 1 1 0 0 SQLP 16 6 1 1 1 0 0 SQLP 17 6 0 1 1 0 0 SQLP 17 6 1 1 1 0 0 SQLP 0 7 0 1 1 0 0 SQLP 0 7 1 1 1 0 0 SQLP 1 7 0 1 1 0 0 SQLP 1 7 1 1 1 0 0 SQLP 2 7 0 1 1 0 0 SQLP 2 7 1 1 1 0 0 SQLP 3 7 0 1 1 0 0 SQLP 3 7 1 1 1 0 0 SQLP 4 7 0 1 1 0 0 SQLP 4 7 1 1 1 0 0 SQLP 5 7 0 1 1 0 0 SQLP 5 7 1 1 1 0 0 SQLP 6 7 0 1 1 0 0 SQLP 6 7 1 1 1 0 0 SQLP 7 7 0 1 1 0 0 SQLP 7 7 1 1 1 0 0 SQLP 8 7 0 1 1 0 0 SQLP 8 7 1 1 1 0 0 SQLP 9 7 0 1 1 0 0 SQLP 9 7 1 1 1 0 0 SQLP 10 7 0 1 1 0 0 SQLP 10 7 1 1 1 0 0 SQLP 11 7 0 1 1 0 0 SQLP 11 7 1 1 1 0 0 SQLP 12 7 0 1 1 0 0 SQLP 12 7 1 1 1 0 0 SQLP 13 7 0 1 1 0 0 SQLP 13 7 1 1 1 0 0 SQLP 14 7 0 1 1 0 0 SQLP 14 7 1 1 1 0 0 SQLP 15 7 0 1 1 0 0 SQLP 15 7 1 1 1 0 0 SQLP 16 7 0 1 1 0 0 SQLP 16 7 1 1 1 0 0 SQLP 17 7 0 1 1 0 0 SQLP 17 7 1 1 1 0 0 SQLP 0 8 0 1 1 0 0 SQLP 0 8 1 1 1 0 0 SQLP 1 8 0 1 1 0 0 SQLP 1 8 1 1 1 0 0 SQLP 2 8 0 1 1 0 0 SQLP 2 8 1 1 1 0 0 SQLP 3 8 0 1 1 0 0 SQLP 3 8 1 1 1 0 0 SQLP 4 8 0 1 1 0 0 SQLP 4 8 1 1 1 0 0 SQLP 5 8 0 1 1 0 0 SQLP 5 8 1 1 1 0 0 SQLP 6 8 0 1 1 0 0 SQLP 6 8 1 1 1 0 0 SQLP 7 8 0 1 1 0 0 SQLP 7 8 1 1 1 0 0 SQLP 8 8 0 1 1 0 0 SQLP 8 8 1 1 1 0 0 SQLP 9 8 0 1 1 0 0 SQLP 9 8 1 1 1 0 0 SQLP 10 8 0 1 1 0 0 SQLP 10 8 1 1 1 0 0 SQLP 11 8 0 1 1 0 0 SQLP 11 8 1 1 1 0 0 SQLP 12 8 0 1 1 0 0 SQLP 12 8 1 1 1 0 0 SQLP 13 8 0 1 1 0 0 SQLP 13 8 1 1 1 0 0 SQLP 14 8 0 1 1 0 0 SQLP 14 8 1 1 1 0 0 SQLP 15 8 0 1 1 0 0 SQLP 15 8 1 1 1 0 0 SQLP 16 8 0 1 1 0 0 SQLP 16 8 1 1 1 0 0 SQLP 17 8 0 1 1 0 0 SQLP 17 8 1 1 1 0 0 SQCT 0 0 0 S SQCT 1 0 0 N SQCT 2 0 0 E SQCT 3 0 0 E SQCT 4 0 0 Z SQCT 5 0 0 I SQCT 6 0 0 E SQCT 7 0 0 R SQCT 8 0 0 E SQCT 9 0 0 M SQCT 10 0 0 A SQCT 11 0 0 H SQCT 12 0 0 R SQCT 13 0 0 A SQCT 14 0 0 T SQCT 15 0 0 T SQCT 16 0 0 A SQCT 17 0 0 L SQCT 0 1 0 N SQCT 1 1 0 T SQCT 2 1 0 I SQCT 3 1 0 L SQCT 4 1 0 E SQCT 5 1 0 S SQCT 6 1 0 G SQCT 7 1 0 S SQCT 8 1 0 A SQCT 9 1 0 N SQCT 10 1 0 N SQCT 11 1 0 Y SQCT 12 1 0 A SQCT 13 1 0 S SQCT 14 1 0 I SQCT 15 1 0 R SQCT 16 1 0 P SQCT 17 1 0 A SQCT 0 2 0 R SQCT 1 2 0 A SQCT 2 2 0 T SQCT 3 2 0 O SQCT 4 2 0 V SQCT 5 2 0 M SQCT 6 2 0 O SQCT 7 2 0 R SQCT 8 2 0 D SQCT 9 2 0 A SQCT 10 2 0 N SQCT 11 2 0 C SQCT 12 2 0 Y SQCT 13 2 0 E SQCT 14 2 0 C SQCT 15 2 0 A SQCT 16 2 0 S SQCT 17 2 0 T SQCT 0 3 0 N SQCT 1 3 0 G SQCT 2 3 0 O SQCT 3 3 0 N SQCT 4 3 0 E SQCT 5 3 0 S SQCT 6 3 0 T SQCT 7 3 0 L SQCT 8 3 0 I SQCT 9 3 0 K SQCT 10 3 0 E SQCT 11 3 0 A SQCT 12 3 0 S SQCT 13 3 0 L SQCT 14 3 0 E SQCT 15 3 0 D SQCT 16 3 0 D SQCT 17 3 0 I SQCT 0 4 0 E SQCT 1 4 0 S SQCT 2 4 0 P SQCT 3 4 0 O SQCT 4 4 0 N SQCT 5 4 0 S SQCT 6 4 0 I SQCT 7 4 0 O SQCT 8 4 0 N SQCT 9 4 0 A SQCT 10 4 0 W SQCT 11 4 0 I SQCT 12 4 0 R SQCT 13 4 0 E SQCT 14 4 0 S SQCT 15 4 0 E SQCT 16 4 0 W SQCT 17 4 0 N SQCT 0 5 0 U SQCT 1 5 0 I SQCT 2 5 0 L SQCT 3 5 0 D SQCT 4 5 0 E SQCT 5 5 0 R SQCT 6 5 0 S SQCT 7 5 0 U SQCT 8 5 0 G SQCT 9 5 0 R SQCT 10 5 0 A SQCT 11 5 0 N SQCT 12 5 0 D SQCT 13 5 0 C SQCT 14 5 0 R SQCT 15 5 0 U SQCT 16 5 0 I SQCT 17 5 0 G SQCT 0 6 0 T SQCT 1 6 0 H SQCT 2 6 0 E SQCT 3 6 0 E SQCT 4 6 0 S SQCT 5 6 0 E SQCT 6 6 0 E SQCT 7 6 0 S SQCT 8 6 0 C SQCT 9 6 0 A SQCT 10 6 0 P SQCT 11 6 0 I SQCT 12 6 0 S SQCT 13 6 0 T SQCT 14 6 0 H SQCT 15 6 0 P SQCT 16 6 0 R SQCT 17 6 0 I SQCT 0 7 0 E SQCT 1 7 0 S SQCT 2 7 0 S SQCT 3 7 0 C SQCT 4 7 0 T SQCT 5 7 0 A SQCT 6 7 0 N SQCT 7 7 0 T SQCT 8 7 0 R SQCT 9 7 0 I SQCT 10 7 0 S SQCT 11 7 0 T SQCT 12 7 0 U SQCT 13 7 0 S SQCT 14 7 0 A SQCT 15 7 0 F SQCT 16 7 0 E SQCT 17 7 0 N SQCT 0 8 0 R SQCT 1 8 0 O SQCT 2 8 0 S SQCT 3 8 0 H SQCT 4 8 0 E SQCT 5 8 0 L SQCT 6 8 0 L SQCT 7 8 0 E SQCT 8 8 0 R SQCT 9 8 0 S SQCT 10 8 0 P SQCT 11 8 0 E SQCT 12 8 0 N SQCT 13 8 0 F SQCT 14 8 0 E SQCT 15 8 0 T SQCT 16 8 0 T SQCT 17 8 0 E END qxw-20140331/examples/xmas.qxw0000640000175000017500000004505612142412606013733 0ustar momo#QXW2 http://www.quinapalus.com GP 0 15 15 2 0 0 TTL +Season AUT +Oleg GLP 1 1 0 0 GSP ffffff 000000 0 0 TM 0 0 0 + TMSG 0 0 + TMSG 0 1 + DFN 0 +/usr/share/dict/words DFN 1 + DFN 2 + DFN 3 + DFN 4 + DFN 5 + DFN 6 + DFN 7 + DFN 8 + DSF 0 +^.*+(? or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // gcc -Wall -fPIC plugin_behead_message.c -o plugin_behead_message.so -shared #include "qxwplugin.h" int treat(const char*answer) { if(answer[0]!=msgcharAZ09[0]) return 0; strcpy(light,answer+1); return treatedanswer(light); } qxw-20140331/examples/circle.qxw0000640000175000017500000005425312142412606014223 0ustar momo#QXW2 http://www.quinapalus.com GP 3 30 9 2 0 0 TTL + AUT + GLP 1 3 0 0 GSP ffffff 000000 0 0 TM 0 0 0 + TMSG 0 0 + TMSG 0 1 + DFN 0 +/usr/share/dict/words DFN 1 + DFN 2 + DFN 3 + DFN 4 + DFN 5 + DFN 6 + DFN 7 + DFN 8 + DSF 0 +^.*+(? or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __DICTS_H__ #define __DICTS_H__ #define MAXNDICTS 9 // must fit in a word extern char dfnames[MAXNDICTS][SLEN]; extern char dsfilters[MAXNDICTS][SLEN]; extern char dafilters[MAXNDICTS][SLEN]; extern char lemdesc[NLEM][LEMDESCLEN]; extern char*lemdescADVP[NLEM]; extern int atotal; // total answers extern int ltotal; // total lights extern int ultotal; // total uniquified lights extern int loaddicts(int sil); extern void freedicts(void); extern int loaddefdicts(void); extern int getinitflist(int**l,int*ll,struct lprop*lp,int wlen); extern int pregetinitflist(void); extern int postgetinitflist(void); extern char*loadtpi(void); extern void unloadtpi(void); #endif qxw-20140331/LICENCE0000640000175000017500000004310312142412606011360 0ustar momo GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. qxw-20140331/qxwplugin.h0000640000175000017500000000424012272730646012614 0ustar momo// $Id: qxwplugin.h 517 2014-01-31 14:22:28Z mo $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant http://www.quinapalus.com E-mail: qxw@quinapalus.com This file is part of Qxw. Qxw is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Qxw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Qxw. If not, see or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #define MXSZ 63 // maximum grid dimension #define MXLE 250 // maximum entries in light extern int treatedanswer(const char*light); extern int isword(const char*light); #ifdef _WIN32 __declspec(dllexport) void init(); __declspec(dllexport) void finit(); __declspec(dllexport) int treat(const char*answer); __declspec(dllimport) int clueorderindex; __declspec(dllimport) int gridorderindex[]; __declspec(dllimport) int checking[]; __declspec(dllimport) int lightlength; __declspec(dllimport) int lightx; __declspec(dllimport) int lighty; __declspec(dllimport) int lightdir; __declspec(dllimport) char*treatmessage[]; __declspec(dllimport) char*treatmessageAZ[]; __declspec(dllimport) char*treatmessageAZ09[]; __declspec(dllimport) char msgchar[]; __declspec(dllimport) char msgcharAZ[]; __declspec(dllimport) char msgcharAZ09[]; #else extern int clueorderindex; extern int*gridorderindex; extern int*checking; extern int lightlength; extern int lightx; extern int lighty; extern int lightdir; extern char*treatmessage[]; extern char*treatmessageAZ[]; extern char*treatmessageAZ09[]; extern char msgchar[]; extern char msgcharAZ[]; extern char msgcharAZ09[]; #endif static char light[MXLE+1]; qxw-20140331/filler.h0000640000175000017500000000231312272730646012032 0ustar momo// $Id: filler.h 517 2014-01-31 14:22:28Z mo $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant http://www.quinapalus.com E-mail: qxw@quinapalus.com This file is part of Qxw. Qxw is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Qxw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Qxw. If not, see or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __FILLER_H__ #define __FILLER_H__ // control functions for filler called from main code extern void filler_init(); extern void filler_finit(); extern int filler_start(int mode); extern void filler_wait(); extern void filler_stop(); extern void getposs(struct entry*e,char*s,int r,int dash); extern int filler_status; #endif qxw-20140331/dicts.c0000640000175000017500000010405012317043161011644 0ustar momo// $Id: dicts.c 563 2014-04-02 17:18:40Z mo $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant http://www.quinapalus.com E-mail: qxw@quinapalus.com This file is part of Qxw. Qxw is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Qxw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Qxw. If not, see or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // DICTIONARY #include #include #include // required for string conversion functions #ifdef _WIN32 // using wrapper for Windows dynamic linking functions #include "pfdlfcn.h" #else #include #endif #include "common.h" #include "qxw.h" #include "gui.h" #include "dicts.h" // default dictionaries #ifdef _WIN32 // Windows default dictionaries stored in subfolder of {app} folder #define NDEFDICTS 1 char*defdictfn[NDEFDICTS]={ "\\Dictionaries\\UKACD18plus.txt" }; #else #define NDEFDICTS 4 char*defdictfn[NDEFDICTS]={ "/usr/dict/words", "/usr/share/dict/words", "/usr/share/dict/british-english", "/usr/share/dict/american-english"}; #endif // ISO-8859-1 character mapping to remove accents and fold case: #=reject word, .=ignore character for answer/light purposes static char chmap[256]="\ ................\ ................\ ................\ 0123456789......\ .ABCDEFGHIJKLMNO\ PQRSTUVWXYZ.....\ .ABCDEFGHIJKLMNO\ PQRSTUVWXYZ.....\ ................\ ................\ ................\ ................\ AAAAAAECEEEEIIII\ .NOOOOO.OUUUUY..\ AAAAAAECEEEEIIII\ .NOOOOO.OUUUUY.Y"; // set up possible file encodings for dictionary files #define NFILEENC 6 struct fileenc { char nenc[12]; // name of encoding for g_convert() char bom[4]; // potential byte order mark at start of file int lbom; // length of byte order mark }; static struct fileenc fenc[NFILEENC] = { {"ISO-8859-1",{'\x00'}, 0}, {"UTF-8" ,{'\xEF','\xBB','\xBF'}, 3}, {"UTF-16LE" ,{'\xFF','\xFE'}, 2}, {"UTF-16BE" ,{'\xFE','\xFF'}, 2}, {"UTF-32LE" ,{'\xFF','\xFE','\x00','\x00'},4}, {"UTF-32BE" ,{'\x00','\x00','\xFE','\xFF'},4} }; int chartol[256]; ABM chartoabm[256]; char ltochar[NL]; #define MEMBLK 100000 // unit of memory allocation struct memblk { struct memblk*next; int ct; char s[MEMBLK]; }; static struct memblk*dstrings[MAXNDICTS]={0}; // dictionary string pools struct answer*ans=0,**ansp=0; struct light*lts=0; int atotal=0; // total answers int ltotal=0; // total lights int ultotal=0; // total uniquified lights char dfnames[MAXNDICTS][SLEN]; char dsfilters[MAXNDICTS][SLEN]; char dafilters[MAXNDICTS][SLEN]; char lemdesc[NLEM][LEMDESCLEN]={""," (rev.)"," (cyc.)"," (cyc., rev.)","*"}; char*lemdescADVP[NLEM]={"normally","reversed","cyclically permuted","cyclically permuted and reversed","with any other permutation"}; #define HTABSZ 1048576 static int ahtab[HTABSZ]={0}; static int cmpans(const void*p,const void*q) {int u; // string comparison for qsort u=strcmp( (*(struct answer**)p)->ul,(*(struct answer**)q)->ul); if(u) return u; u=strcmp( (*(struct answer**)p)->cf,(*(struct answer**)q)->cf); return u; } static void freedstrings(int d) { // free string pool associated with dictionary d struct memblk*p; while(dstrings[d]) {p=dstrings[d]->next;free(dstrings[d]);dstrings[d]=p;} } void freedicts(void) { // free all memory allocated by loaddicts, even if it aborted mid-load int i; for(i=0;iMXLE) return 0; // too long: skip if(sre) { i=pcre_exec(sre,0,s0,l0,0,0,pcreov,120); DEB1 if(i<-1) printf("PCRE error %d\n",i); if(i<0) return 0; // failed match } if(are) { i=pcre_exec(are,0,s1,l1,0,0,pcreov,120); DEB1 if(i<-1) printf("PCRE error %d\n",i); if(i<0) return 0; // failed match } if(memblkp==NULL||memblkl+2+l0+1+l1+1>MEMBLK) { // allocate more memory if needed (this always happens on first pass round loop) q=(struct memblk*)malloc(sizeof(struct memblk)); if(q==NULL) {return -2;} q->next=NULL; if(memblkp==NULL) dstrings[dn]=q; else memblkp->next=q; // link into list memblkp=q; memblkp->ct=0; memblkl=0; } *(memblkp->s+memblkl++)=(char)floor(f*10.0+128.5); // score with rounding *(memblkp->s+memblkl++)=dn; strcpy(memblkp->s+memblkl,s0);memblkl+=l0+1; strcpy(memblkp->s+memblkl,s1);memblkl+=l1+1; memblkp->ct++; // count words in this memblk return 1; } // read n bytes from fp and interpret as little-endian integer static int getint(FILE*fp,int n) { int i,u; for(i=0,u=0;i=0;j--) { // big-endian... p=hnodep[(m>>j)&1]+n; if(*p==-1) { // node does not exist yet if(nhn>=MXHNODES) return -1; *p=nhn; hnodep[0][nhn]=-1; hnodep[1][nhn]=-1; hnodec[nhn]=-1; nhn++; } n=*p; } hnodec[n]=i; return 0; } // Attempt to load a .TSD file. Return number of words >=0 on success, <0 on error. static int loadtsd(FILE*fp,int format,int dn,pcre*sre,pcre*are) { int c,i,j,l,m,ml,n,u,nw; int hoff[MXLE+1]; // file offsets into Huffman coded block int dcount[MXLE+1]; // number of words of each length char s0[SLEN],s1[SLEN]; GError*error=NULL; gchar*sp=NULL; DEB1 printf("attempting to load TSD format=0x%x\n",format); if(format!='0'&&format!='1') return -1; // only TSD0 and TSD1 supported if(fseek(fp,4,SEEK_SET)<0) return -1; ml=getint(fp,2); // maximum length if(ml<1) return -1; DEB1 printf("ml=%d\n",ml); if(format=='1') getint(fp,12*ml+12); // skip some bytes for(i=0;i0) { DEB1 { printf("%02x %c ",i,isprint(i)?i:'?'); for(j=l-1;j>=0;j--) printf("%d",(m>>j)&1); printf("\n"); } if(addhcode(m,l,i)<0) return -1; } } nw=0; for(l=1;lMXLE) break; if(hoff[l]==0) continue; if(fseek(fp,hoff[l],SEEK_SET)<0) return -1; DEB1 printf("starting to read length %d at offset %08x; dstrings[%d]=%p memblkp=%p\n",l,hoff[l],dn,dstrings[dn],memblkp); j=8; c=0; for(i=0;i>j)&1); n=hnodep[(c>>j)&1][n]; j++; u=hnodec[n]; if(u!=-1) { DEB16 printf(" %02x %c\n",u,isprint(u)?u:'?'); if((u>=0x01&&u<0x7f)||(u>=0xa0&&u<=0xff)) { s1[m]=u; if(m=0) {at=i; goto exit;} // successfully read } rewind(fp); if(fgets(bom,5,fp)!=NULL) { // re-read BOM at start of file for(i=0; i0) { if(!(memcmp(bom,fenc[i].bom,fenc[i].lbom))) { mode=i; // BOM found so set mode fseek(fp,fenc[i].lbom,SEEK_SET); // and skip past BOM break; } } } // end of for loop if(mode>1) { // Cannot read this file encoding mode sprintf(t,"Dictionary %d\nCannot read file encoding:\ntry UTF-8 or ISO-8859-1 encoding",dn+1); if(!sil) reperr(t); rc=-1; goto exit; } if(mode==-1) rewind(fp); // No BOM, so read words from start of file } retry: at=0; freedstrings(dn); memblkl=0; // number of bytes stored so far in current memory block memblkp=0; // current memblk being filled dstrings[dn]=0; DEB1 printf("Reading dictionary file %s (in mode %d), owd=%d\n",dfnames[dn],mode,owd); for(;;) { // Loop through dictionary 'words' until EOF if(owd) strcpy(t,dafilters[dn]); else { if(feof(fp)) break; if(fgets(t,SLEN,fp)==NULL) break; } j=strlen(t)-1; if(j<0) goto skipword; while(j>=0&&t[j]>=0&&t[j]<=' ') t[j--]=0; // Strip control characters/white space from end of string while (j>=0&&((t[j]>='0'&&t[j]<='9')||t[j]=='.'||t[j]=='+'||t[j]=='-')) j--; // get score (if any) from end j++; f=0.0; if(j==0||t[j-1]!=' ') j=strlen(t); // all digits, or no space? treat it as a 'word' else { sscanf(t+j,"%f",&f); if(f>= 10.0) f= 10.0; if(f<=-10.0) f=-10.0; t[j--]=0; // rest of input is treated as a 'word' (which may contain spaces) } while(j>=0&&t[j]>=0&&t[j]<=' ') t[j--]=0; // remove trailing white space j++; if(j<1) goto skipword; // t now contains 'word' for conversion; convert it first to UTF-8 for use as citation form if(mode>-1) { // Use encoding mode based on BOM sp=g_convert(t,-1,"UTF-8",fenc[mode].nenc,NULL,&l0,&error); if(error) { sprintf(t,"Dictionary %d\nFile not encoded in accordance with its BOM",dn+1); if(!sil) reperr(t); rc=-1; goto exit; } } else { // try fallback encodings in turn if(mode==-1) sp=g_convert(t,-1,"UTF-8","UTF-8",NULL,&l0,&error); // effectively a check for valid UTF-8 if(mode==-2) sp=g_convert(t,-1,"UTF-8","ISO-8859-1",NULL,&l0,&error); if(error) { DEB1 { printf("error: %s\n",error->message); for(i=0;t[i];i++) printf(" %02x",t[i]); printf("\n"); } g_clear_error(&error); freedstrings(dn); rewind(fp); mode--; if(mode>-3) goto retry; // go round again and try next encoding // here if none of the encodings worked sprintf(t,"Dictionary %d\nFile does not use a recognised encoding:\ntry UTF-8 or ISO-8859-1",dn+1); if(!sil) reperr(t); rc=-1; goto exit; } } // here we have UTF-8 citation form of string in sp, length in l0 if(l0>=SLEN) goto skipword; strcpy(s0,sp); g_free(sp),sp=0; // Now convert s0 to ISO-8859-1 to create untreated light form sp=g_convert(s0,-1,"ISO-8859-1","UTF-8",NULL,NULL,&error); if(error==NULL) {strcpy(s1,sp);} // ISO-8859-1 form in s1 else { DEB1 printf("Failed conversion to ISO-8859-1: %s\n",s0); goto skipword; // 'word' contained symbols outside of Latin-1 character set; go to next word } i=adddictword(s0,s1,dn,sre,are,f); if(i==-2) {rc=-2; goto exit;} if(i==1) at++; skipword: if(sp) g_free(sp),sp=0; g_clear_error(&error); if(owd) break; } // End of 'word' for loop exit: if(sp) g_free(sp),sp=0; if(sre) pcre_free(sre); if(are) pcre_free(are); g_clear_error(&error); if(!owd) if(fp) fclose(fp); if(rc<0) return rc; else return at; } int loaddicts(int sil) { // load (or reload) dictionaries from dfnames[] // sil=1 suppresses error reporting // returns: 0=success; 1=bad file; 2=no words; 4=out of memory struct answer*ap; struct memblk*p; int at,dn,i,j,k,l,rc,u; char t[SLEN]; unsigned int h; freedicts(); at=0; rc=0; for(dn=0;dn0) { freedicts(); return rc; } // allocate array space from counts atotal=at; DEB1 printf("atotal=%9d\n",atotal); ans =(struct answer* )malloc(atotal*sizeof(struct answer)); if(ans ==NULL) goto ew4; ansp=(struct answer**)malloc(atotal*sizeof(struct answer*)); if(ansp==NULL) goto ew4; // pointer array for sorting k=0; for(dn=0;dnct;i++) { // loop over all words ans[k].score=pow(10.0,((float)(*(unsigned char*)(p->s+l++))-128.0)/10.0); ans[k].cfdmask= ans[k].dmask=1<<*(unsigned char*)(p->s+l++); ans[k].cf =p->s+l; l+=strlen(p->s+l)+1; ans[k].acf =0; ans[k].ul =p->s+l; l+=strlen(p->s+l)+1; k++; } p=p->next; } } DEB1 printf("k=%9d\n",k); assert(k==atotal); for(i=0;iul,ansp[i-1]->ul)) {j++;ap=ansp[j]=ansp[i];} else { ansp[j]->dmask|=ansp[i]->dmask; // union masks ansp[j]->score*=ansp[i]->score; // multiply scores over duplicate entries if(strcmp(ansp[i]->cf,ansp[i-1]->cf)) ap->acf=ansp[i],ap=ansp[i]; // different citation forms? link them together else ap->cfdmask|=ansp[i]->cfdmask; // cf:s the same: union masks } } atotal=j+1; for(i=0;iul[j];j++) h=h*113+ansp[i]->ul[j]; h=h%HTABSZ; ansp[i]->ahlink=ahtab[h]; ahtab[h]=i; } for(i=0;iscore>= 1e10) ansp[i]->score= 1e10; // clamp scores if(ansp[i]->score<=-1e10) ansp[i]->score=-1e10; } for(i=0;iacf==0) continue; // printf("A%9d %08x %20s %5.2f",i,ansp[i]->dmask,ansp[i]->ul,ansp[i]->score); ap=ansp[i]; do { // printf(" \"%s\"",ap->cf); ap=ap->acf; } while(ap); // printf("\n"); } DEB1 printf("Total unique answers by entry: %d\n",atotal); return 0; ew4: freedicts(); reperr("Out of memory loading dictionaries"); return 4; } // Run through some likely candidates for default dictionaries. // Return // 0: found something // !=0: nothing found int loaddefdicts() {int i; for(i=0;ihist,0,sizeof(l->hist)); l->lbm=0; m=0; n=strlen(l->s); if(l->tagged) n-=NMSG; for(i=0;is[i]]; l->hist[j]++; if(l->hist[j]>m) m=l->hist[j]; l->lbm|=1ULL<nhistorder=0; for(i=m;i>0;i--) for(j=0;jhist[j]==i) l->historder[l->nhistorder++]=j; } // return index of light, creating if it doesn't exist; -1 on no memory static int findlight(const char*s,int tagged,int a,int e) { unsigned int h0,h1; int f,i,u,l0; int l; int len0,len1; struct light*p; struct memblk*q; len0=strlen(s); if(tagged) len0-=NMSG; assert(len0>0); h0=0; for(i=0;i=clts) { // out of space to store light structures? (always happens first time) clts=clts*2+5000; // try again a bit bigger p=realloc(lts,clts*sizeof(struct light)); if(!p) return -1; lts=p; DEB2 printf("lts realloc: %d\n",clts); } l=hstab[h0]; u=-1; // look for the light string, independent of how it arose f=0; while(l!=-1) { len1=strlen(lts[l].s); if(lts[l].tagged) len1-=NMSG; assert(len1>0); if(len0==len1&&!strncmp(s,lts[l].s,len0)) { // match as far as non-tag part is concerned u=lts[l].uniq; if(!strcmp(s,lts[l].s)) {f=1; break;} // exact match including possible tags } l=lts[l].hashslink; } if(f==0) { // we do not have a full-string match l0=strlen(s)+1; if(lml+l0>MEMBLK) { // make space to store copy of light string DEB1 printf("memblk alloc\n"); q=(struct memblk*)malloc(sizeof(struct memblk)); if(!q) return -1; q->next=NULL; if(lmp==NULL) lstrings=q; else lmp->next=q; // link into list lmp=q; lml=0; } if(u==-1) u=ultotal++; // allocate new uniquifying number if needed lts[ltotal].s=lmp->s+lml; strcpy(lmp->s+lml,s);lml+=l0; } else { lts[ltotal].s=lts[l].s; } lts[ltotal].ans=a; lts[ltotal].em=e; lts[ltotal].uniq=u; lts[ltotal].tagged=tagged; lts[ltotal].hashslink =hstab [h0]; hstab [h0]=ltotal; // insert into hash tables lts[ltotal].hashaeslink=haestab[h1]; haestab[h1]=ltotal; dohistdata(lts+ltotal); return ltotal++; } // is word in dictionaries specified by curdm? int isword(const char*s) { int i,p; unsigned int h; h=0; for(i=0;s[i];i++) h=h*113+s[i]; h=h%HTABSZ; p=ahtab[h]; while(p!=-1) { if(!strcmp(s,ansp[p]->ul)) return !!(ansp[p]->dmask&curdm); p=ansp[p]->ahlink; } return 0; } // add light to feasible list: s=text of light, a=answer from which treated, e=entry method // returns 0 if OK, !=0 on (out of memory) error static int addlight(const char*s,int a,int e) { int l; int*p; char t[MXFL+1]; // curten should never be set when adding msgword[]:s (got from msglprop); as MXLE+NMSG<=MXFL this never overflows l=strlen(s); if(l<1) return 0; // is this test needed? memcpy(t,s,l); if(curten) memcpy(t+l,msgcharAZ09,NMSG),l+=NMSG; // append tag characters if any t[l]=0; l=findlight(t,curten,a,e); if(l<0) return l; if(ntfl>=ctfl) { ctfl=ctfl*3/2+500; p=realloc(tfl,ctfl*sizeof(int)); if(!p) return -1; tfl=p; DEB2 printf("tfl realloc: %d\n",ctfl); } tfl[ntfl++]=l; return 0; } // Add treated answer to feasible light list if suitable // returns !=0 for error int treatedanswer(const char*s) { char s0[MXFL+1]; int j,k,l,u; l=strlen(s); if(l!=lightlength) return 0; assert(l>0); if(tambaw&&!isword(s)) return 0; if(curem&EM_JUM) { // jumbled entry method return addlight(s,curans,4); // just store normal entry } if(curem&EM_FWD) { // forwards entry method u=addlight(s,curans,0);if(u) return u; } if(curem&EM_REV) { for(j=0;j='A'&&s[i]<='Z')||(s[i]>='0'&&s[i]<='9')); switch(treatmode) { case 0:return treatedanswer(s); case 1: // Playfair if(l!=lightlength) return 0; if(ODD(l)) return 0; for(i=0;i=l0&&clueorderindex>=l1) return treatedanswer(s); c0=clueorderindexlightlength) return 0; c0=msgcharAZ09[0]; if(c0=='-') return treatedanswer(s); if(l!=lightlength-1) return 0; for(i=0;i<=l;i++) { for(j=0;jnext;free(lstrings);lstrings=p;} lmp=0; lml=MEMBLK; FREEX(tfl);ctfl=0;ntfl=0; FREEX(lts);clts=0;ltotal=0;ultotal=0; for(i=0;idmask,curem=lp->emask,curten=lp->ten; for(i=0;i\n",i,msgword[i]); u=addlight(msgword[i],-1-i,0); if(u) return u; goto ex0; } if((curem&EM_ALL)==0) curem=EM_FWD; // force normal entry to be allowed if all are disabled lightlength=llen; DEB2 printf("getinitflist(%p) llen=%d dmask=%08x emask=%08x ten=%d:\n",lp,llen,curdm,curem,curten); memset(mfl,0,sizeof(mfl)); for(i=0;i0) { for(j=0;treatmsgAZ09[i][j];j++) mfl[i]|=chartoabm[(int)treatmsgAZ09[i][j]]; if(ntw>(int)strlen(treatmsgAZ09[i])) mfl[i]|=ABM_DASH; // add in "-" if message not long enough if(clueorderindex0) msgcharAZ09[i]=ltochar[logbase2(ml[i])]; // extract msgchar:s from counters DEB2 { printf(" building list with msgcharAZ09[]="); for(i=0;idmask)==0) continue; // not in a valid dictionary if(curten) u=treatans(ansp[curans]->ul); else u=treatedanswer(ansp[curans]->ul); if(u) return u; if(abort_flag) return -5; } for(i=0;i0) { b=mfl[i]&~(ml[i]|(ml[i]-1)); // clear bits mf[] and below b&=~(b-1); // find new bottom set bit if(b) {ml[i]=b; break;} // try next feasible character ml[i]=mfl[i]&~(mfl[i]-1); // reset to bottom set bit and proceed to advance next character } if(i==NMSG) break; // finish when all combinations done } ex0: *l=malloc(ntfl*sizeof(int)); if(*l==0) return 1; memcpy(*l,tfl,ntfl*sizeof(int)); *ll=ntfl; DEB2 printf("%d entries\n",ntfl); return 0; } qxw-20140331/icon-48x48.png0000640000175000017500000000243512305361707012637 0ustar momoPNG  IHDR00WsRGBbKGD pHYs B(xtIME#IhIDAThYMh[=әMbI %4]BK1(m .\u.}A"BWlEAR ihI mƙ%6ן``;{{LZ+¾wtX^iI>0b2].)H$vr 8U8N i*Sh4Ǐc||\}6nݺwAjeYe}}tTU-$ϟ,<~?}FHe׮]39D___}nlB",Al4 0 x%Aa~]!IR4#b̧X˸>|>>|xlVtUXu(ܹs"L'>WVVpB!AܸqwE<:'1t=󀑔J%J%2V+CQ 'Zr"$944DY,:L&z ###'& /^^/jb{{;GBSE`,~-zx޽kD( R<zzzj;00ł?6%d;E \t 6-!~?, ~a;;; Q4Bv{`X42I麎R0%Vkfb(PUp|.i wێˊϟ,D.3UU ۄ:&''kl=33D"bE +Wos11I~6ׯ_suuϟ?,@Y(\2~pysʙ$4~iB@ 4wZjt 455mR),,,`llㄔ fFdf8pCwo QE$v5M( $ @"4E6Ze|ŸF+vMU#sݍZ>+ 0<<ɪÇD"ÈF)p>}D|rFiIRUUvttׯ_R#;Rb$_~{>GoooWy&E1 3L&Su/^l#,ޮ $Ij?z( ? IENDB`qxw-20140331/common.h0000640000175000017500000002633012316544551012047 0ustar momo// $Id: common.h 556 2014-04-01 14:12:56Z mo $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011-2014 Mark Owen; Windows port by Peter Flippant http://www.quinapalus.com E-mail: qxw@quinapalus.com This file is part of Qxw. Qxw is free software: you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. Qxw is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Qxw. If not, see or write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __COMMON_H__ #define __COMMON_H__ #include #include #include #include #include #include #include #ifdef _WIN32 // Additional includes for windows version #include #include #include #define _USE_MATH_DEFINES #define DIR_SEP_STR "\\" #define DIR_SEP_CHAR '\\' #else #define DIR_SEP_STR "/" #define DIR_SEP_CHAR '/' #endif #include #define RELEASE "20140331" #define NGTYPE 10 #define MAXNDIR 3 #define MXSZ 63 // max grid size X and Y #define MXCL 250 // max number of cells in light; must be >=MXSZ*2; allow more for VL:s #define MXCT 31 // max contents in one cell per direction #define NMSG 2 // number of messages passed to treatment code #define MXLE 250 // max entries in a light before tags added #define MXFL 255 // max entries in a word that filler can cope with: light plus tags, >=MXLE+NMSG; <256 to allow poscnt[] not to overflow #define MXLT (MAXNDIR*MXSZ*MXSZ+NVL) // max number of lights (conservative estimate) #define NVL (MXSZ*4) // max number of virtual lights #define MAXNMK (MAXNDIR*2) // max number of corner marks #define MXMK 30 // max length of mark string // note that a light (in a Mobius grid with all squares full) can contain up to MXSZ*2*MXCT entries, which is >MXLE; and that // if filler discretion is enabled, the number of treated lights must be <=MXFL, otherwise only limited by MXLT // MXFL<=255 to prevent unsigned char histograms overflowing #define NLEM 5 // number of light entry methods #define NATREAT 12 // number of treatments #define TREAT_PLUGIN 9 #define PI (M_PI) #define ODD(x) ((x)&1) #define EVEN(x) (!((x)&1)) #define FREEX(p) if(p) {free(p);p=0;} #define SLEN 1000 // maximum length for filenames, strings etc.; Windows has MAX_PATH around 260; should be >>MXFL #define LEMDESCLEN 20 #define MX(a,b) ((a)>(b)?(a):(b)) extern int debug; #define DEB1 if(debug&1) #define DEB2 if(debug&2) #define DEB4 if(debug&4) #define DEB8 if(debug&8) #define DEB16 if(debug&16) // Terminology: // A `square' is the quantum of area in the grid; one or more squares (a `merge group') form // an `entry', which is a single enclosed white area in the grid where a letter (or group of letters) will appear. // bldstructs() constructs the words and entries from the square data produced // by the editor. #define NL 37 // number of letters in alphabet (A..Z, 0..9, -); '-' is only used internally for "spread" words typedef unsigned long long int ABM; // alphabet bitmap type #define ABM_ALL ((1ULL< or # write to the Free Software Foundation, Inc., 51 Franklin Street, # Fifth Floor, Boston, MA 02110-1301, USA. all:: qxw deb:: qxw CFLAGS := -Wall -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security `pkg-config --cflags glib-2.0` `pkg-config --cflags gtk+-2.0` -I/opt/local/include LFLAGS := -Wl,-Bsymbolic-functions -Wl,-z,relro -L/opt/local/lib -lgtk-x11-2.0 -lgdk-x11-2.0 -lm -lcairo -lgobject-2.0 -lpcre -lglib-2.0 -pthread -lgthread-2.0 # -lrt as well? ifneq ($(filter deb,$(MAKECMDGOALS)),) CFLAGS:= $(CFLAGS) -g else CFLAGS:= $(CFLAGS) -O9 endif qxw: qxw.o filler.o dicts.o gui.o draw.o Makefile gcc -rdynamic -Wall -ldl qxw.o filler.o dicts.o gui.o draw.o $(LFLAGS) -o qxw qxw.o: qxw.c qxw.h filler.h dicts.h draw.h gui.h common.h Makefile gcc $(CFLAGS) -c qxw.c -o qxw.o gui.o: gui.c qxw.h filler.h dicts.h draw.h gui.h common.h Makefile gcc $(CFLAGS) -c gui.c -o gui.o filler.o: filler.c qxw.h filler.h dicts.h common.h Makefile gcc $(CFLAGS) -c filler.c -o filler.o dicts.o: dicts.c dicts.h gui.h common.h Makefile gcc $(CFLAGS) -fno-strict-aliasing -c dicts.c -o dicts.o draw.o: draw.c qxw.h draw.h gui.h common.h Makefile gcc $(CFLAGS) -c draw.c -o draw.o .PHONY: clean clean: rm -f dicts.o draw.o filler.o gui.o qxw.o qxw .PHONY: install install: mkdir -p $(DESTDIR)/usr/games cp -a qxw $(DESTDIR)/usr/games/qxw mkdir -p $(DESTDIR)/usr/include/qxw cp -a qxwplugin.h $(DESTDIR)/usr/include/qxw/qxwplugin.h mkdir -p $(DESTDIR)/usr/share/applications cp -a qxw.desktop $(DESTDIR)/usr/share/applications/qxw.desktop mkdir -p $(DESTDIR)/usr/share/pixmaps cp -a qxw.xpm $(DESTDIR)/usr/share/pixmaps/qxw.xpm mkdir -p $(DESTDIR)/usr/share/icons/hicolor/48x48/apps cp -a icon-48x48.png $(DESTDIR)/usr/share/icons/hicolor/48x48/apps/qxw.png ## REL-