qxw-20110923/0000755000175000017500000000000011637123151010404 5ustar momoqxw-20110923/qxw.h0000644000175000017500000000741611637123103011401 0ustar momo/* $Id: qxw.h -1 $ */ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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. */ extern int dir,curx,cury; extern int unsaved; extern char bname[SLEN]; extern char filename[SLEN+50]; 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[MXSZ+1]; extern int st_lucc[MXSZ+1]; extern int st_locc[MXSZ+1]; extern int st_lsc[MXSZ+1]; extern int st_lmnc[MXSZ+1]; extern int st_lmxc[MXSZ+1]; extern int st_sc; extern int st_ce; extern int st_2u,st_3u; extern int st_tlf,st_vltlf; 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 getfstyle(int x,int y); extern int getdech(int x,int y); extern int getnumber(int x,int y); extern void a_load(void); extern void a_save(void); extern void a_filenew(void); 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 int getlightd(int*lx,int*ly,int x,int y,int d); extern int getlightchp(char**p,int x,int y,int d); extern int getstartoflight(int*lx,int*ly,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(void); 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,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 resetlp(struct lprop*lp); qxw-20110923/dicts.h0000644000175000017500000000335211637123101011661 0ustar momo// $Id: dicts.h -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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 __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 lcount[MXSZ+1]; // number of lights of each length extern int atotal; // total answers extern int ltotal; // total lights extern int ultotal; // total uniquified lights extern char*aused; // answer already used while filling extern char*lused; // light already used while filling 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-20110923/gui.c0000644000175000017500000026217511637123102011345 0ustar momo// $Id: gui.c -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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 #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 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*but,char*ext,int write); static int box(int type,const char*t,const char*u,const char*v); static void gridchangen(void); static void syncselmenu(void); GtkWidget *grid_da; static GtkItemFactory *item_factory; // for menus static GtkWidget *mainw,*grid_sw,*list_sw,*vbox,*hbox,*paned,*menubar,*poss_label,*clist; // main window and content static GtkWidget*stats=NULL; // statistics window (if visible) static GtkWidget*(st_te[MXSZ+2][5]),*(st_r[9]); // statistics table static GtkWidget*currentdia; // for `Filling' dialogue box (so that we can close it automatically when filling completes) // 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 name of crossword and title bar of main window void setbname(char*s) { strncpy(bname,s,SLEN-1);bname[SLEN-1]='\0'; if(strlen(bname)>=4&&!strcmp(bname+strlen(bname)-4,".qxw")) bname[strlen(bname)-4]='\0'; setwintitle(); } // GTK MENU HANDLERS // simple menu handlers static void m_filenew(GtkWidget*w,gpointer data) {if(!unsaved||areyousure()) a_filenew();} static void m_fileopen(GtkWidget*w,gpointer data) {if(!unsaved||areyousure()) if(filedia("Open",".qxw",0)) a_load();} static void m_filesave(GtkWidget*w,gpointer data) {if(filedia("Save",".qxw",1)) a_save();} static void m_filequit(void) {if(!unsaved||areyousure()) 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=(int)data&0xff;} static void m_symm1(GtkWidget*w,gpointer data) {symmm=(int)data&0xff;} static void m_symm2(GtkWidget*w,gpointer data) {symmd=(int)data&0xff;} static void m_zoom(GtkWidget*w,gpointer data) {int u; u=(int)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((int)data) { case 0x401: if(filedia("Export blank grid as EPS",".blank.eps",1)) a_exportg(filename,4,0);break; case 0x402: if(filedia("Export blank grid as PNG",".blank.png",1)) a_exportg(filename,4,1);break; case 0x403: if(gshape[gtype]!=0) break; if(filedia("Export blank grid as HTML",".blank.html",1)) a_exportgh(0x05,""); break; case 0x411: if(filedia("Export filled grid as EPS",".eps",1)) a_exportg(filename,2,0);break; case 0x412: if(filedia("Export filled grid as PNG",".png",1)) a_exportg(filename,2,1);break; case 0x413: if(gshape[gtype]!=0) break; if(filedia("Export filled grid as HTML",".html",1)) a_exportgh(0x03,""); break; case 0x420: if(filedia("Export answers as text",".ans.txt",1)) a_exporta(0);break; case 0x423: if(filedia("Export answers as HTML",".ans.html",1)) a_exporta(1);break; case 0x433: if(gshape[gtype]!=0) break; if(filedia("Export puzzle as HTML",".html",1)) a_exportgh(0x0d,""); break; case 0x434:if(filedia("Export puzzle as HTML+PNG",".html",1)) a_exporthp(0);break; case 0x443: if(gshape[gtype]!=0) break; if(filedia("Export solution as HTML",".html",1)) a_exportgh(0x13,""); break; case 0x444:if(filedia("Export solution as HTML+PNG",".html",1)) a_exporthp(1);break; default: assert(0); } } 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;i=100||dir>=100) refreshsel(); odir=dir; curmf=1; } void gridchangen(void) { int d,i,j; // only squares at start of lights can have dsel information for(i=0;iflbmh;k=cbits(m); // get hints bitmap // printf("%d %d %16llx %d\n",x,y,m,k); if(k==1) gsq[x][y].ct[i][j]=ltochar[logbase2(m)]; e++; } } refreshsqmg(x,y); } } gridchange(); } // run filler static void m_autofill(GtkWidget*w,gpointer data) { if(fillmode) return; // already running? fillmode=(int)data; // selected mode (1=all, 2=selection) if(fillmode==2&&selmode!=0) { sel_toc(); selchange(); } if(compute()) { // start reperr("Could not start filler"); fillmode=0; return; } box(GTK_MESSAGE_INFO,"\n Filling in progress \n"," Stop ",0); // this box is closed manually or by the filler when it completes fillmode=0; // filling done } // 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;char p[MXSZ+1]; 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(getdech(i,j)) strcpy(p,gsq[i][j].ct[0]),strcpy(gsq[i][j].ct[0],gsq[i][j].ct[k]),strcpy(gsq[i][j].ct[k],p); } for(i=0;i=0 ;i--) u=gsq[i][j],gsq[i][j]=t,t=u;} gridchange(); selchange(); 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(); selchange(); } static int getselvl() {int i; if(selmode!=2) return -1; if(nsel!=1) return -1; for(i=0;i=MXSZ) {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(); selchange(); } static void m_vldelete(GtkWidget*w,gpointer data) {int i,j; if(selmode!=2) return; for(i=0,j=0;ikeyval; 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,(k==GDK_Tab)?' ':k)) ccontdia(); // open dialogue if not a simple square refreshsqmg(curx,cury); 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 &&f==0) {r=1; nextdir();} 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(); } return r; } // convert screen coords to internal square coords; k is bitmap of sufficiently nearby edges void ptrtosq(int*x,int*y,int*k,int x0,int y0) {float u,v,u0,v0,r=0,t=0,xa=0,ya=0,xb=0,yb=0;int i,j; u0=((float)x0-bawdpx)/pxsq;v0=((float)y0-bawdpx)/pxsq; *k=0; switch(gshape[gtype]) { case 0: i=floor(u0);u=u0-i; j=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,event->x,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,event->x,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 curx=x; cury=y; // not trapped as producing bar or block, so move cursor curmoved(); gridchange(); 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,lx[MXSZ],ly[MXSZ],nc; char*chp[MXSZ]; DEB1 printf("row select event\n"); l=getlight(lx,ly,curx,cury,dir); if(l<2) return; nc=getlightchp(chp,curx,cury,dir); if(nc<2) return; if(!llistp) return; if(row<0||row>=llistn) return; if(strlen(lts[llistp[row]].s)!=nc) return; for(i=0;iallocation.width,widget->allocation.height); if(widget==grid_da) { } return TRUE; } // redraw the screen from the backing pixmap static gint expose_event(GtkWidget*widget,GdkEventExpose*event) {float 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 static int filedia(char*but,char*ext,int write) { GtkWidget*dia; int i; char*p,t[SLEN+50]; 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); strcpy(filename,bname);strcat(filename,ext); strcpy(t,filename); if(write) gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dia),basename(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("File name too long");goto ew0;} strcpy(filename,p); } else strcpy(filename,""); 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,j,k,u,x,y; char c,s[100],t[MXSZ+1]; 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); 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; 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_OK,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); int setactive() {int k; k=g||gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(or)); gtk_widget_set_sensitive(cbg,k); gtk_widget_set_sensitive(cfg,k); gtk_widget_set_sensitive(l0,k); gtk_widget_set_sensitive(l1,k); gtk_widget_set_sensitive(l2,k); gtk_widget_set_sensitive(w20,k); gtk_widget_set_sensitive(tr,k); gtk_widget_set_sensitive(w21,k); return 1; } if(!g) { or=gtk_check_button_new_with_mnemonic("Override default cell properties"); gtk_box_pack_start(GTK_BOX(vb),or,TRUE,TRUE,0); gtk_signal_connect(GTK_OBJECT(or),"clicked",GTK_SIGNAL_FUNC(setactive),0); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); } w=gtk_hbox_new(0,2); l0=gtk_label_new("Background colour: "); gtk_label_set_width_chars(GTK_LABEL(l0),18); gtk_misc_set_alignment(GTK_MISC(l0),1,0.5); gtk_box_pack_start(GTK_BOX(w),l0,FALSE,FALSE,0); cbg=gtk_color_button_new_with_color(&gcbg); gtk_box_pack_start(GTK_BOX(w),cbg,FALSE,FALSE,0); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); w=gtk_hbox_new(0,2); l1=gtk_label_new("Foreground colour: "); gtk_label_set_width_chars(GTK_LABEL(l1),18); gtk_misc_set_alignment(GTK_MISC(l1),1,0.5); gtk_box_pack_start(GTK_BOX(w),l1,FALSE,FALSE,0); cfg=gtk_color_button_new_with_color(&gcfg); gtk_box_pack_start(GTK_BOX(w),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); l2=gtk_label_new("Font style: "); gtk_box_pack_start(GTK_BOX(w),l2,FALSE,FALSE,0); gtk_label_set_width_chars(GTK_LABEL(l2),18); gtk_misc_set_alignment(GTK_MISC(l2),1,0.5); w20=gtk_combo_box_new_text(); gtk_box_pack_start(GTK_BOX(w),w20,FALSE,FALSE,0); gtk_combo_box_append_text(GTK_COMBO_BOX(w20),"Normal"); gtk_combo_box_append_text(GTK_COMBO_BOX(w20),"Bold"); gtk_combo_box_append_text(GTK_COMBO_BOX(w20),"Italic"); gtk_combo_box_append_text(GTK_COMBO_BOX(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(w20),i); w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); tr=gtk_check_button_new_with_mnemonic("Flag for _answer treatment"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tr),sps[0]->ten); gtk_box_pack_start(GTK_BOX(vb),tr,TRUE,TRUE,0); w=gtk_hbox_new(0,2); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); l3=gtk_label_new("Lights intersecting here "); gtk_box_pack_start(GTK_BOX(w),l3,FALSE,FALSE,0); gtk_misc_set_alignment(GTK_MISC(l3),1,0.5); w21=gtk_combo_box_new_text(); gtk_box_pack_start(GTK_BOX(w),w21,FALSE,FALSE,0); gtk_combo_box_append_text(GTK_COMBO_BOX(w21),"must agree"); gtk_combo_box_append_text(GTK_COMBO_BOX(w21),"need not agree: horizontal display"); gtk_combo_box_append_text(GTK_COMBO_BOX(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(w21),i); if(!g) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(or),(sps[0]->spor)&1); 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(cbg),&gcbg); gtk_color_button_get_color(GTK_COLOR_BUTTON(cfg),&gcfg); for(j=0;jspor=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(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) ); i=gtk_combo_box_get_active(GTK_COMBO_BOX(w20)); if(i>=0&&i<4) sps[j]->fstyle=i; sps[j]->ten=!!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tr)); i=gtk_combo_box_get_active(GTK_COMBO_BOX(w21)); if(i>=0&&i<3) sps[j]->dech=i; } } gtk_widget_destroy(dia); gridchange(); refreshall(); return 1; } // light properties dialogue static int lpropdia(int g) { GtkWidget*dia,*e[MAXNDICTS],*f[NLEM],*l,*w,*vb,*tr; struct lprop*lps[MXSZ*MXSZ*MAXNDIR]; int nlps; int d,i,j; char s[SLEN]; nlps=0; if(g) {lps[0]=&dlp;nlps=1;} else { if(selmode==1) for(d=0;dvbox),vb,TRUE,TRUE,0); int setactive() {int i,k; k=g||gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(l)); for(i=0;i15) {strcat(s,"...");strcat(s,dfnames[i]+strlen(dfnames[i])-12);} else if(strlen(dfnames[i])==0) strcat(s,""); else strcat(s,dfnames[i]); strcat(s,")"); e[i]=gtk_check_button_new_with_mnemonic(s); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(e[i]),((lps[0]->dmask)>>i)&1); gtk_box_pack_start(GTK_BOX(vb),e[i],TRUE,TRUE,0); } w=gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vb),w,TRUE,TRUE,0); tr=gtk_check_button_new_with_mnemonic("_Enable answer treatment"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tr),lps[0]->ten); gtk_box_pack_start(GTK_BOX(vb),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),f[i],TRUE,TRUE,0); } if(!g) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(l),(lps[0]->lpor)&1); 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(l)); 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(tr)); } gtk_widget_destroy(dia); gridchange(); return 1; } // treatment dialogue static int treatdia(void) { GtkWidget*dia,*e[NMSG],*f,*m[NMSG],*w,*c,*l,*b,*vb,*b0,*lm[NMSG]; int i; char s[SLEN],*p; char*tnames[NATREAT]={ " None", " Playfair cipher", " Substitution cipher", " Fixed Caesar/Vigenère cipher", " Variable Caesar cipher (clue order)", " Misprint (clue order)", " Delete single occurrence of letter (clue order)", " Letters latent: delete all occurrences of letter (clue order)", " Insert single letter (clue order)", " Custom plug-in"}; char*lab[NATREAT][NMSG]={ {0, 0, }, {"Keyword: ", 0, }, {"Encode ABC...Z as: ", 0, }, {"Key letter/word: ", 0, }, {"Encodings of A: ", 0, }, {"Correct letters: ", "Misprinted letters: "}, {"Letters to delete: ", 0, }, {"Letters to delete: ", 0, }, {"Letters to insert: ", 0, }, {"Message 1: ", "Message 2: " }}; dia=gtk_dialog_new_with_buttons("Answer treatment",GTK_WINDOW(mainw),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,GTK_STOCK_OK,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); c=gtk_combo_box_new_text(); for(i=0;iNATREAT) treatmode=0; if(treatmode==NATREAT-1) { if((p=loadtpi())) { sprintf(s,"Error loading custom plug-in\n%.100s",p); reperr(s); } } else unloadtpi(); } gtk_widget_destroy(dia); gridchange(); return 1; } // dictionary list dialogue static int dictldia(void) { GtkWidget*dia,*e[MAXNDICTS],*l,*b,*t,*f0[MAXNDICTS],*f1[MAXNDICTS]; int i,j; char s[SLEN]; dia=gtk_dialog_new_with_buttons("Dictionaries",GTK_WINDOW(mainw),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,GTK_STOCK_OK,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); e[i]=gtk_entry_new(); gtk_entry_set_max_length(GTK_ENTRY(e[i]),SLEN); gtk_entry_set_text(GTK_ENTRY(e[i]),dfnames[i]); gtk_table_attach(GTK_TABLE(t),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); 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); 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(browse),(void*)i); } gtk_widget_show_all(dia); i=gtk_dialog_run(GTK_DIALOG(dia)); if(i==GTK_RESPONSE_OK) { 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); 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); 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); w20=gtk_combo_box_new_text(); gtk_box_pack_start(GTK_BOX(w),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(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;i0) { vls[v].l=i; memcpy(vls[v].x,x,i*sizeof(int)); memcpy(vls[v].y,y,i*sizeof(int)); } gridchange(); selchange(); } 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,*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_OK,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 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); 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 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(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; 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(); // because ofnew 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<=MXSZ;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(ne>0) sprintf(s,"Checked grid cells: %d/%d (%.1f%%)",st_ce,ne,100.0*st_ce/ne); 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); } // create statistics dialogue static void stats_init(void) { int i,j; GtkWidget*w0,*w1,*vb; for(i=0;ivbox),vb,FALSE,TRUE,0); w0=gtk_table_new(MXSZ+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<=MXSZ;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<=MXSZ;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,MXSZ+1,MXSZ+2);gtk_widget_show(w1); gtk_box_pack_start(GTK_BOX(vb),w0,FALSE,TRUE,0);gtk_widget_show(w0); for(j=0;j<9;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(vb),st_r[j],FALSE,TRUE,0); gtk_widget_show(st_r[j]); } gtk_widget_show(vb); } // remove statistics window (if not already gone or in the process of going) 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; } // 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; } 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[MXSZ*2+100],t1[MXSZ+50],*u[2],p0[SLEN],p1[SLEN]; 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)); 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); // get feasible letter list 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; } // box for information purposes only 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 oomerr() {box(GTK_MESSAGE_ERROR,"Out of memory",GTK_STOCK_CLOSE,0);} void fnferr() {box(GTK_MESSAGE_ERROR,"File not found",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]; if(strerror_r(errno,s,SLEN)) strcpy(s,"general error"); sprintf(t,"Filing system error: %s",s); reperr(t); } int areyousure(void) { // general-purpose are-you-sure dialogue return box(GTK_MESSAGE_QUESTION,"\n Your work is not saved. \n Are you sure you want to proceed? \n"," Proceed ",GTK_STOCK_CANCEL); } 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,0x403),!i); gtk_widget_set_sensitive((GtkWidget*)gtk_item_factory_get_widget_by_action(item_factory,0x413),!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", "N", m_filenew, 0, "",GTK_STOCK_NEW}, { "/File/sep0", 0, 0, 0, ""}, { "/File/_Open...", "O", m_fileopen, 0, "",GTK_STOCK_OPEN}, { "/File/_Save as...", "S", m_filesave, 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 _PNG...", 0, m_fileexport, 0x402}, { "/File/Export blank grid image/as _HTML...", 0, m_fileexport, 0x403}, { "/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 _PNG...", 0, m_fileexport, 0x412}, { "/File/Export filled grid image/as _HTML...", 0, m_fileexport, 0x413}, { "/File/Export _answers", 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+_PNG...", 0, m_fileexport, 0x434}, { "/File/Export so_lution", 0, 0, 0, ""}, { "/File/Export solution/As _HTML...", 0, m_fileexport, 0x443}, { "/File/Export solution/As HTML+_PNG...", 0, m_fileexport, 0x444}, { "/File/sep2", 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/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; 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); 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-20110923/filler.c0000644000175000017500000003517111637123102012030 0ustar momo// $Id: filler.c -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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 "common.h" #include "filler.h" #include "dicts.h" #include "qxw.h" static int ct_malloc=0,ct_free=0; // counters for debugging static int phase=0; // state machine static int winit=0; // count of words initialised so far static int rcode=0; // return code: -3, -4: initflist errors; -2: out of stack; -1: out of memory; 1: no fill found; 2: fill found // the following stacks keep track of the filler state as it recursively tries to fill the grid #define MXSS (MXSZ*MXSZ+2) static int sdep=-1; // stack pointer static int sent[MXSS]; // entry considered at this depth static int spp[MXSS]; // which possibility we are currently trying (index into sposs) static char*sposs[MXSS]; // possibilities for this entry, 0-terminated static int**sflist[MXSS]; // pointers to restore feasible word list flist static int*sflistlen[MXSS]; // pointers to restore flistlen static int*scommit[MXSS]; // word (if any - -1 otherwise) committed at this depth static ABM*sentryfl[MXSS]; // feasible letter bitmap for this entry #define isused(l) (lused[lts[l].uniq]|aused[lts[l].ans]) #define setused(l,v) lused[lts[l].uniq]=v,aused[lts[l].ans]=v // phase 16: sort the feasible word list ready for display static int sortwlist(void) { // now done in mkfeas() return 17; } // phase 17: transfer the feasible word list to the display static int mkclist(void) { updatefeas(); return 18; } // 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); updategrid(); } // 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;float k,l; for(m=2;m>0;m--) { // m=2: loop over checked entries; then m=1: loop over unchecked entries j=-1;l=BIGF; for(i=0;i=m) { // not already entered? k=entries[i].crux; // get the priority for this entry if(k=1 otherwise static int settleents(void) {struct entry*e;struct word*w;int f,i,l; int*p; DEB1 printf("settleents() sdep=%d\n",sdep); f=0; for(i=0;iupd; // generate cell updated flags from entry updated flags for(i=0;iflist; l=w->flistlen; if(sflistlen[sdep][w-words]==-1) { // then we mustn't trash words[].flist sflist [sdep][w-words]=w->flist; sflistlen[sdep][w-words]=w->flistlen; w->flist=(int*)malloc(l*sizeof(int)); // new list can be at most as long as old one if(w->flist==NULL) return -1; // out of memory ct_malloc++; } w->flistlen=listisect(w->flist,p,l,cells[i].wp,e->flbm); // generate new feasible word list if(w->flistlen!=l) {w->upd=1;f++;} // word list has changed: feasible letter lists will need updating if(w->flistlen==0&&!w->fe) return -2; // no options left, not a fully-entered word if(w->flistlen==1&&w->commit==-1) { // down to a single word? if(afunique&&isused(w->flist[0])) { // in no-duplicates mode and only answer left is already used? (could check all on list) w->flistlen=0; // abort return -2; } else { // otherwise, if down to one word, commit it // printf("committing word %d (%s)\n",w,lts[w->flist[0]].s);fflush(stdout); setused(w->flist[0],1); // flag as used w->commit=w->flist[0]; scommit[sdep][w-words]=w->flist[0]; } } } for(i=0;ie->ch; if(ch!=' ') assert((ch>='A'&&ch<='Z')||(ch>='0'&&ch<='9')),cellfl[k]=1LL<e; // propagate from cell to entry if(e->flbm&~cellfl[j]) { // has this entry been changed by the additional constraint? e->flbm&=cellfl[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;i=0) { // avoid zero score if we've committed if(l==1) for(k=0;kscore[chartol[(int)lts[p[0]].s[k]]]+=1.0; else assert(l==0); } else { for(j=0;jscore; for(k=0;kscore[chartol[(int)lts[p[j]].s[k]]]+=f; // add in its score to this cell's score } } } for(i=0;ilength]; // if(f!=0.0) f=1.0/f; f=1.0; for(j=0;jscore[j]*=f*cells[i].score[j]; // copy scores to entries, scaled by total word count at this length } for(i=0;iscore[i]>j&&e->score[i]score[i]; // peel off scores from top down DEB2 printf("getposs(%d): j=%g\n",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&&m=0);state_restore();sdep--;} // clear state stacks and free allocated memory static void state_finit(void) { while(sdep>=0) state_pop(); freestack(); } // phase 10: initialise for search static int searchinit(void) {int u,i,j,t0; t0=clock(); for(i=winit;iten) clueorderindex++; if(clock()-t0>CLOCKS_PER_SEC/(20)) { DEB1 printf("Intercalated return while initialising word lists\n"); winit=i+1; return 10; } } if(postgetinitflist()) {rcode=-4;return 0;} memset(aused,0,atotal); memset(lused,0,ultotal); return 14; // update internal data from initial grid } // phase 11: one level deeper in seach tree static int searchdeeper(void) {int e; char s[MAXNL+1]; if(sdep==MXSS-1) {rcode=-2;return 15;} DEB1 printf("mkscores...\n"); mkscores(); DEB1 { int w; for(w=0;wCLOCKS_PER_SEC*3||ct1-ct0<0) {progress();ct0=clock();} // update display every three seconds or so return 14; // update internal data from new entry } // phase 13: backtrack when all possibilities in an entry have been exhausted without success static int searchbacktrack(void) { state_pop(); if(sdep==-1) {rcode=1;return 15;} // all done, no solution found return 12; // try next possibility at level above } // phase 14: update internal data static int searchsettle(void) {int f; f=settleents(); // rescan entries if(f==-2) return 13; if(f==-1) {rcode=-1;return 15;} // out of memory: abort f=settlewds(); // rescan words if(f==0) return 11; // no changes: proceed to next search level return 14; // need to iterate until everything settles down } // phase 15 static int searchdone(void) {int i; progress(); // copy results to display mkfeas(); // construct feasible word list state_finit(); // if(fillmode&¤tdia) gtk_dialog_response(GTK_DIALOG(currentdia),GTK_RESPONSE_CANCEL); for(i=0;i> 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 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 okbox(const char*t); extern void fserror(void); extern void oomerr(void); extern void fsgerr(void); extern void reperr(const char*s); extern int areyousure(void); extern void setbname(char*s); extern void gridchange(void); extern void killcurrdia(void); extern void setposslabel(char*s); #endif qxw-20110923/changelog0000644000175000017500000000256611637123100012261 0ustar momoPRINCIPAL CHANGES IN VERSION 20110923 De-checked cells Multiple letters in cells Square-to-square constraints (`free lights') Cyclically permuted entries Toroidal, Mobius etc. grid topologies Unconstrained "lights" (i.e., ignored by filler) Bold/italic text in cells -fPIC added to build line for plugins `Treatment enable' for cells: flag for answer treatment Export answers includes possible answers as well as lights Filler less profligate with memory Statistics: lights too long for filler, free lights Switched to gtk_file_chooser One-word dictionaries Ctrl-X: erase all cells Improved HTML bar appearance CHANGES IN VERSION 20110826 Minor change to examples Changelog included in release PRINCIPAL CHANGES IN VERSION 20110818 Minor bug fixes Hex and circular grids Cell merging Minor changes to editing keys: PgUp/Dn, arrow keys, space and tab Use Home/End within a light Grid size set via `Grid properties' in Edit menu Multiple dictionaries, reloadable during construction Dictionary filtering using PCRE Answer treatments, custom plug-ins Select sets of cells or sets of lights and apply properties to them; shift-right click Zoom view Improved rendering PNG export, HTML+PNG export New save format (will read old files) Better reporting of errors Title and author recorded Digits allowed in grid Manual now available Scores removed from display (but still work as before underneath) qxw-20110923/qxwplugin.h0000644000175000017500000000233411637123104012613 0ustar momo// $Id: qxwplugin.h -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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 light length extern int treatedanswer(const char*light); extern int isword(const char*light); extern int clueorderindex; extern int*gridorderindex; extern int lightlength; extern int lightx; extern int lighty; extern int lightdir; extern char*treatmessage[]; extern char*treatmessageAZ[]; static char light[MXSZ+1]; qxw-20110923/filler.h0000644000175000017500000000207011637123102012025 0ustar momo// $Id: filler.h -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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 int filler_start(void); extern int filler_step(void); extern void filler_stop(void); extern void getposs(struct entry*e,char*s,int r); #endif qxw-20110923/draw.c0000644000175000017500000012322111637123101011501 0ustar momo// $Id: draw.c -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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 "common.h" #include "qxw.h" #include "draw.h" #include "gui.h" #include "dicts.h" int bawdpx,hbawdpx; static int sfcxoff=0,sfcyoff=0; 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}}; 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"}}; // GRID DRAWING static void moveto(cairo_t*cc,float x,float y) {cairo_move_to(cc,x,y);} static void lineto(cairo_t*cc,float x,float y) {cairo_line_to(cc,x,y);} static void rmoveto(cairo_t*cc,float x,float y) {cairo_rel_move_to(cc,x,y);} static void rlineto(cairo_t*cc,float x,float y) {cairo_rel_line_to(cc,x,y);} static void setlinewidth(cairo_t*cc,float w) {cairo_set_line_width(cc,w);} static void setlinecap(cairo_t*cc,int c) {cairo_set_line_cap(cc,c);} static void closepath(cairo_t*cc) {cairo_close_path(cc);} static void fill(cairo_t*cc) {cairo_fill(cc);} static void stroke(cairo_t*cc) {cairo_stroke(cc);} static void strokepreserve(cairo_t*cc) {cairo_stroke_preserve(cc);} // static void clip(cairo_t*cc) {cairo_clip(cc);} static void gsave(cairo_t*cc) {cairo_save(cc);} static void grestore(cairo_t*cc) {cairo_restore(cc);} static void setrgbcolor(cairo_t*cc,float r,float g, float b) {cairo_set_source_rgb(cc,r,g,b);} static void setrgbacolor(cairo_t*cc,float r,float g, float b,float a) {cairo_set_source_rgba(cc,r,g,b,a);} static 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 );} static void setfontsize(cairo_t*cc,float h) {cairo_set_font_size(cc,h);} static void showtext(cairo_t*cc,char*s) {cairo_show_text(cc,s);} static float textwidth(cairo_t*cc,char*s,float 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 static void arc(cairo_t*cc,float x,float y,float r,float t0,float t1) {float 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)); } static void arcn(cairo_t*cc,float x,float y,float r,float t0,float t1) {float 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)); } static void setrgbcolor24(cairo_t*cc,int c) {setrgbcolor(cc,((c>>16)&255)/255.0,((c>>8)&255)/255.0,(c&255)/255.0);} static void ltext(cairo_t*cc,char*s,float 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 static void ctext(cairo_t*cc,char*s,float x,float y,float h,int fs,int ocm) {int i,m; char t[2]; float 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,x,y,0); vxcoords(&x1,&y1,x,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; } void addvxcoordsbbox(float*x0,float*y0,float*x1,float*y1,float x,float y,int d) {float vx,vy; 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; } void addsqbbox(float*x0,float*y0,float*x1,float*y1,int x,int y) {int d; float vx,vy; 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) {float x0=0,y0=0; switch(gshape[gtype]) { case 0: vxcoords(&x0,&y0,x,y,3);break; case 1: vxcoords(&x0,&y0,x,y,5);break; case 2: vxcoords(&x0,&y0,x,y,4);break; case 3:case 4: vxcoords(&x0,&y0,x,y,1); break; } moveto(cc,x0,y0); } static void movetomgcentre(cairo_t*cc,int x,int y,int d,int l) {float 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;float 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)&&(j&1)) { t=2*PI*((gshape[gtype]==4)?x-0.5:x)/width-PI/2; if(j==1) arcn(cc,height,height,height-y-.999, t+2*PI/width,t); else arc (cc,height,height,height-y ,t,t+2*PI/width); } else { 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;float x0=0,y0=0,t; vxcoords(&x0,&y0,x,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;jl) l=strlen(ct[i]); h=0.7/nd; if(h>1.0/l) h=1.0/l; 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 void drawsqfg(cairo_t*cc,int x,int y,int sq,int ba,int lf,int ocm) { 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; float sc[MXSZ],u,v,xc[MXSZ],yc[MXSZ]; setlinecap(cc,1); memset(vis,0,sizeof(vis)); for(i=0;i=ndir[gtype]) refreshsel2(cc); cairo_destroy(cc); invaldaall(); } void refreshcur() { float 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(dir=0) { // draw a number? sprintf(s,"%d",n); movetonum(cc,x,y,textwidth(cc,s,.25*sc)+.1*sc,.27*sc); rmoveto(cc,.05*sc,-.05*sc); setrgbcolor(cc,0,0,0); ltext(cc,s,.25*sc,0); } } } void refreshnum() {cairo_t*cc; cc=cairo_create(sfn); 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); cairo_translate(cc,0.5,0.5); cairo_scale(cc,pxsq,pxsq); setrgbcolor(cc,0,0,0); drawnums(cc); cairo_destroy(cc); invaldaall(); } void refreshhin() { int de,nd,f,i,j,k,l,md,x,y; struct entry*e; ABM m; char c; char s[SLEN]; char ct[MAXNDIR][MXSZ+1]; float sc,u,v; cairo_t*cc; cc=cairo_create(sfh); 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); cairo_translate(cc,0.5,0.5); cairo_scale(cc,pxsq,pxsq); setrgbcolor(cc,0,0,0); for(y=0;yflbm;k=cbits(m); if(gsq[x][y].ct[i][j]!=' ') c=' '; else if(k==0) c='?'; else if(k==1) c=ltochar[logbase2(m)]; else c='.'; ct[i][j]=c; e++; } ct[i][j]=0; } setrgbcolor(cc,0.75,0.75,0.75); drawct(cc,x,y,ct,1); } 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,unsigned int dmask) { struct answer*a; int ld; ld=strlen(lemdesc[lts[ln].em]); if(ld>0) { // special entry method? give grid form too strcpy(t0,lts[ln].s); strcat(t0,": "); } else t0[0]=0; a=ansp[lts[ln].ans]; for(;;) { if(a->cfdmask&dmask) { if(strlen(t0)+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(ld>0) strcat(t0," "),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 void panswers(FILE*fp,int dir,int html,int cf) { int d,f,i,j,k,n,vlf; char t[MXSZ+1]; char t0[MXSZ*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; getword(i,j,d,t); 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 (html==0) fprintf(fp,"%d ",n); else if(html==1) fprintf(fp,"%d ",n); else if(html==2) fprintf(fp,"%d ",n); else if(html==3) fprintf(fp,"%d\n",n); 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->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 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 cf,d,fl,i,j,i0,j0,k,l,md,u,v,x,y,bg,fg,fs,fh; int de,nd; char s[MXSZ*2+1]; int hbawd; FILE*fp; 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)&&gsq[i][j].number>-1) fprintf(fp,"
%d
\n",i*hpxsq+3,j*hpxsq+1,gsq[i][j].number); } 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(strlen(gsq[i][j].ct[k])>l) l=strlen(gsq[i][j].ct[k]); fh=(int)floor(hpxsq/l+.5); fprintf(fp,"
%s%s%s%s%s
\n", x,y-fh,fg,fh, (fs&1)?"":"",(fs&2)?"":"",gsq[i][j].ct[0],(fs&2)?"":"",(fs&1)?"":""); fprintf(fp,"
%s%s%s%s%s
\n", x,y,fg,fh, (fs&1)?"":"",(fs&2)?"":"",gsq[i][j].ct[1],(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 // f0 b1: include answers in grid // f0 b2: include numbers in grid // f1: 0=EPS 1=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]; FILE*fp=0; cairo_status_t fwr(FILE*fp,unsigned char*p,unsigned int n) { return (fwrite(p,1,n,fp)==n)?CAIRO_STATUS_SUCCESS:CAIRO_STATUS_WRITE_ERROR; } switch(f1) { case 0: sq=eptsq; break; case 1: sq=hpxsq; break; } 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)fwr,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: sf=cairo_image_surface_create(CAIRO_FORMAT_ARGB32,px,py); break; } cc=cairo_create(sf); if(f1==1) { 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 void a_exporthp(int f) {char url[SLEN+200]; strcpy(url,filename); strcat(url,"_files"); mkdir(url,0777); strcat(url,f?"/solution.png":"/grid.png"); a_exportgh(f?0x30:0x28,url); a_exportg(url,f?2:4,1); } qxw-20110923/common.h0000644000175000017500000001740511637123110012047 0ustar momo// $Id: common.h -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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 #define RELEASE "20110923" #define NGTYPE 10 #define MXSZ 63 // max grid size X and Y #define MAXNDIR 3 #define NVL (MXSZ*4) // max number of virtual lights /* big float that can be represented exactly: 2^100 */ #define BIGF (1267650600228229401496703205376.0) #define PI 3.14159265359 #define ODD(x) ((x)&1) #define EVEN(x) (!((x)&1)) #define FREEX(p) if(p) {free(p);p=0;} #define SLEN 256 // maximum length for filenames, strings etc. #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) // 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. // A `cell' corresponds to one letter of one of the `word's to be found to fill the grid. // A checked grid square `entry' will // have two or more corresponding cells, whose letters are constrained to agree. // bldstructs() constructs the cells, words and entries from the square data produced // by the editor. #define MAXNL 63 // maximum number of letters in alphabet typedef unsigned long long int ABM; // alphabet bitmap type struct sprop { // square properties unsigned int bgcol; // background colour unsigned int fgcol; // foreground colour unsigned char ten; // treatment enable: flag to plug-in unsigned char spor; // global square property override (not used in dsp) unsigned char fstyle; // font style: b0=bold, b1=italic unsigned char dech; // dechecked: 0=normal, 1=contributions stacked atop one another, 2=contributions side-by-side }; struct lprop { // light properties unsigned int dmask; // mask of allowed dictionaries unsigned int emask; // mask of allowed entry methods unsigned char ten; // treatment enable unsigned char lpor; // global light property override (not used in dlp) }; struct cell { struct entry*e; // corresponding entry struct word*w; // corresponding word int wp; // position within word float score[MAXNL]; char upd; // updated flag }; struct word { int length; int gx0,gy0; // start position (not necessarily mergerep) int ldir; int*flist; // start of feasible list int flistlen; // length of feasible list int commit; // committed this word during fill? struct cell*c[MXSZ]; // list of cells making up this word struct lprop*lp; // applicable properties for this word char upd; // updated flag int fe; // fully-entered flag int goi[MXSZ]; // grid order indices for each cell }; struct entry { ABM flbm; // feasible letter bitmap ABM flbmh; // copy of flbm provided by solver to running display int gx,gy; // corresponding grid position (indices to gsq) of representative int checking; // count of corresponding cells float score[MAXNL]; float crux; // priority char ch; // entered letter if any char sel; // selected flag char upd; // updated flag unsigned char fl; // flags copied from square }; struct square { // source grid information (saved in undo buffers) unsigned int bars; // bar presence in each direction unsigned int merge; // merge flags in each direction unsigned char fl; // flags: b0=blocked, b3=not part of grid (for irregular shapes); b4=cell selected unsigned char dsel; // one light-selected flag for each direction char ct[MAXNDIR][MXSZ+1]; // square contents strings in each direction struct sprop sp; struct lprop lp[MAXNDIR]; // derived information from here on struct entry*e0; // first entry int ne; // number of entries (consecutive) struct word*w[MAXNDIR]; unsigned int vflags[MAXNDIR]; // violation flags: b0=double unch, b1=triple+ unch, b2=underchecked, b3=overchecked int number; // number in square (-1 for no number) int goi; // grid order index of treated squares (-1 in untreated squares) }; struct vl { // virtual light int l; // number of constituent cells int x[MXSZ],y[MXSZ]; // constituent cell coords struct lprop lp; // properties char sel; // is selected? struct word*w; }; extern struct cell*cells; extern struct word*words; extern struct entry*entries; extern struct square gsq[MXSZ][MXSZ]; extern struct vl vls[NVL]; extern int nw,nc,ne,ns,nvl; // DICTIONARY extern int chartol[256]; extern char ltochar[MAXNL]; extern int nl; #define NLEM 4 // number of light entry methods #define NATREAT 10 // number of treatments #define NMSG 2 // number of messages passed to treatment code struct answer { // a word found in one or more dictionaries int ahlink; // hash table link for isword() unsigned int dmask; // mask of dictionaries where word found unsigned int cfdmask; // mask of dictionaries where word found with this citation form // int light[NLEM]; // light indices of treated versions double score; char*cf; // citation form of the word in dstrings struct answer*acf; // alternative citation form (linked list) char*ul; // untreated light in dstrings: ansp is uniquified by this }; struct light { // a string that can appear in the grid, the result of treating an answer; not uniquified int hashslink; // hash table (s only) linked list int hashaeslink; // hash table (a,e,s) linked list int ans; // answer giving rise to this light int em; // mode of entry giving rise to this light char*s; // the light in dstrings, containing only chars in alphabet int uniq; // uniquifying number (by string s only), used as index into lused[] }; extern int*llist; // buffer for word index list extern int*llistp; // ptr to matching word indices extern int llistn; // number of matching words extern unsigned int llistdm; // dictionary mask applicable to matching lights extern int lcount[MXSZ+1]; // number of words of each length extern struct answer**ansp; extern struct light*lts; extern int atotal; // total answers in dict extern int ultotal; // total unique lights in dict extern char*aused; // answer already used extern char*lused; // light already used extern char tpifname[SLEN]; extern int treatmode; extern char treatmsg[NMSG][SLEN]; extern char treatmsgAZ[NMSG][SLEN]; extern int tambaw; // treated answer must be a word extern int clueorderindex; extern int gridorderindex[MXSZ]; extern int lightx; extern int lighty; extern int lightdir; // PREFERENCES #define NPREFS 9 extern int prefdata[NPREFS]; #define clickblock (prefdata[0]) #define clickbar (prefdata[1]) #define shownums (prefdata[2]) #define mincheck (prefdata[3]) #define maxcheck (prefdata[4]) #define afunique (prefdata[5]) #define afrandom (prefdata[6]) #define eptsq (prefdata[7]) #define hpxsq (prefdata[8]) // FILLER extern int fillmode; // 0=stopped, 1=filling all, 2=filling selection, 3=word lists only #define UNDOS 50 #endif qxw-20110923/qxw.desktop0000644000175000017500000000026511637123104012617 0ustar momo[Desktop Entry] Version=1.0 Type=Application Name=Qxw GenericName=Crossword editor Comment=Construct crossword puzzles Exec=/usr/games/qxw Terminal=false Categories=Game;LogicGame; qxw-20110923/examples/0000755000175000017500000000000011637123110012215 5ustar momoqxw-20110923/examples/diagonal-filled.qxw0000644000175000017500000000546311637123105016005 0ustar momo#QXW2v1 http://www.quinapalus.com GP 0 5 5 2 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 0 0 0 Q SQ 1 0 0 0 0 B SQ 2 0 0 0 0 O SQ 3 0 0 0 0 A SQ 4 0 0 0 0 T SQ 0 1 0 0 0 O SQ 1 1 0 0 0 U SQ 2 1 0 0 0 T SQ 3 1 0 0 0 D SQ 4 1 0 0 0 O SQ 0 2 0 0 0 P SQ 1 2 0 0 0 R SQ 2 2 0 0 0 A SQ 3 2 0 0 0 A SQ 4 2 0 0 0 M SQ 0 3 0 0 0 H SQ 1 3 0 0 0 E SQ 2 3 0 0 0 R SQ 3 3 0 0 0 G SQ 4 3 0 0 0 E SQ 0 4 0 0 0 S SQ 1 4 0 0 0 T SQ 2 4 0 0 0 Y SQ 3 4 0 0 0 E SQ 4 4 0 0 0 S 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 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 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 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 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 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 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 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 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 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 VL 0 0 0 0 VL 0 1 1 1 VL 0 2 2 2 VL 0 3 3 3 VL 0 4 4 4 VLP 0 1 1 0 0 SQCT 0 0 0 Q SQCT 1 0 0 B SQCT 2 0 0 O SQCT 3 0 0 A SQCT 4 0 0 T SQCT 0 1 0 O SQCT 1 1 0 U SQCT 2 1 0 T SQCT 3 1 0 D SQCT 4 1 0 O SQCT 0 2 0 P SQCT 1 2 0 R SQCT 2 2 0 A SQCT 3 2 0 A SQCT 4 2 0 M SQCT 0 3 0 H SQCT 1 3 0 E SQCT 2 3 0 R SQCT 3 3 0 G SQCT 4 3 0 E SQCT 0 4 0 S SQCT 1 4 0 T SQCT 2 4 0 Y SQCT 3 4 0 E SQCT 4 4 0 S END qxw-20110923/examples/letters-latent.qxw0000644000175000017500000002751411637123106015743 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_message.c -o plugin_behead_message.so -shared #include int treat(const char*answer) { if(clueorderindex>=strlen(treatmessageAZ[0])) return 0; if(answer[0]!=treatmessageAZ[0][clueorderindex]) return 0; strcpy(light,answer+1); return treatedanswer(light); } qxw-20110923/examples/circle.qxw0000644000175000017500000005425311637123105014234 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. */ // 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-20110923/examples/mobius-filled.qxw0000644000175000017500000001637211637123106015527 0ustar momo#QXW2v1 http://www.quinapalus.com GP 7 12 6 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 0 0 0 E SQ 1 0 0 0 0 A SQ 2 0 2 0 0 C SQ 3 0 0 0 0 T SQ 4 0 0 0 0 O SQ 5 0 3 0 0 R SQ 6 0 0 0 0 A SQ 7 0 0 0 0 D SQ 8 0 2 0 0 V SQ 9 0 0 0 0 A SQ 10 0 0 0 0 N SQ 11 0 2 0 0 C SQ 0 1 0 0 0 T SQ 1 1 1 0 0 O SQ 2 1 2 0 0 V SQ 3 1 0 0 0 A SQ 4 1 0 0 0 P SQ 5 1 2 0 0 O SQ 6 1 0 0 0 R SQ 7 1 0 0 0 E SQ 8 1 2 0 0 T SQ 9 1 0 0 0 T SQ 10 1 1 0 0 O SQ 11 1 2 0 0 A SQ 0 2 0 0 0 H SQ 1 2 0 0 0 U SQ 2 2 2 0 0 M SQ 3 2 0 0 0 P SQ 4 2 0 0 0 E SQ 5 2 3 0 0 D SQ 6 2 0 0 0 I SQ 7 2 0 0 0 N SQ 8 2 2 0 0 V SQ 9 2 0 0 0 O SQ 10 2 0 0 0 I SQ 11 2 2 0 0 C SQ 0 3 0 0 0 E SQ 1 3 1 0 0 D SQ 2 3 2 0 0 D SQ 3 3 0 0 0 E SQ 4 3 0 0 0 R SQ 5 3 2 0 0 I SQ 6 3 0 0 0 S SQ 7 3 0 0 0 I SQ 8 3 2 0 0 O SQ 9 3 0 0 0 N SQ 10 3 1 0 0 S SQ 11 3 2 0 0 T SQ 0 4 0 0 0 N SQ 1 4 0 0 0 A SQ 2 4 2 0 0 T SQ 3 4 0 0 0 T SQ 4 4 0 0 0 A SQ 5 4 3 0 0 S SQ 6 4 0 0 0 T SQ 7 4 0 0 0 R SQ 8 4 2 0 0 E SQ 9 4 0 0 0 C SQ 10 4 0 0 0 E SQ 11 4 2 0 0 N SQ 0 5 0 0 0 E SQ 1 5 0 0 0 D SQ 2 5 0 0 0 G SQ 3 5 0 0 0 A SQ 4 5 0 0 0 S SQ 5 5 0 0 0 C SQ 6 5 0 0 0 O SQ 7 5 0 0 0 O SQ 8 5 0 0 0 L SQ 9 5 0 0 0 E SQ 10 5 0 0 0 D SQ 11 5 0 0 0 R 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 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 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 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 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 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 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 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 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 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 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 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 SQCT 0 0 0 E SQCT 1 0 0 A SQCT 2 0 0 C SQCT 3 0 0 T SQCT 4 0 0 O SQCT 5 0 0 R SQCT 6 0 0 A SQCT 7 0 0 D SQCT 8 0 0 V SQCT 9 0 0 A SQCT 10 0 0 N SQCT 11 0 0 C SQCT 0 1 0 T SQCT 1 1 0 O SQCT 2 1 0 V SQCT 3 1 0 A SQCT 4 1 0 P SQCT 5 1 0 O SQCT 6 1 0 R SQCT 7 1 0 E SQCT 8 1 0 T SQCT 9 1 0 T SQCT 10 1 0 O SQCT 11 1 0 A SQCT 0 2 0 H SQCT 1 2 0 U SQCT 2 2 0 M SQCT 3 2 0 P SQCT 4 2 0 E SQCT 5 2 0 D SQCT 6 2 0 I SQCT 7 2 0 N SQCT 8 2 0 V SQCT 9 2 0 O SQCT 10 2 0 I SQCT 11 2 0 C SQCT 0 3 0 E SQCT 1 3 0 D SQCT 2 3 0 D SQCT 3 3 0 E SQCT 4 3 0 R SQCT 5 3 0 I SQCT 6 3 0 S SQCT 7 3 0 I SQCT 8 3 0 O SQCT 9 3 0 N SQCT 10 3 0 S SQCT 11 3 0 T SQCT 0 4 0 N SQCT 1 4 0 A SQCT 2 4 0 T SQCT 3 4 0 T SQCT 4 4 0 A SQCT 5 4 0 S SQCT 6 4 0 T SQCT 7 4 0 R SQCT 8 4 0 E SQCT 9 4 0 C SQCT 10 4 0 E SQCT 11 4 0 N SQCT 0 5 0 E SQCT 1 5 0 D SQCT 2 5 0 G SQCT 3 5 0 A SQCT 4 5 0 S SQCT 5 5 0 C SQCT 6 5 0 O SQCT 7 5 0 O SQCT 8 5 0 L SQCT 9 5 0 E SQCT 10 5 0 D SQCT 11 5 0 R END qxw-20110923/examples/hex.qxw0000644000175000017500000001472711637123105013561 0ustar momo#QXW2 http://www.quinapalus.com GP 1 9 7 2 0 0 TTL + AUT + 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. all: qxw COPTS := -O9 -Wall LFLAGS := -lgtk-x11-2.0 -lgdk-x11-2.0 -lm -lcairo -lgobject-2.0 -lpcre qxw: qxw.o filler.o dicts.o gui.o draw.o gcc -rdynamic -Wall -ldl qxw.o filler.o dicts.o gui.o draw.o $(LFLAGS) -o qxw # gcc -rdynamic -Wall -ldl qxw.o filler.o dicts.o gui.o draw.o `pkg-config --libs gtk+-2.0` -o qxw qxw.o: qxw.c qxw.h filler.h dicts.h draw.h gui.h common.h gcc $(COPTS) -c qxw.c `pkg-config --cflags gtk+-2.0` -o qxw.o gui.o: gui.c qxw.h filler.h dicts.h draw.h gui.h common.h gcc $(COPTS) -c gui.c `pkg-config --cflags gtk+-2.0` -o gui.o filler.o: filler.c qxw.h filler.h dicts.h common.h gcc $(COPTS) -c filler.c -o filler.o dicts.o: dicts.c dicts.h gui.h common.h gcc $(COPTS) -fno-strict-aliasing -c dicts.c -o dicts.o draw.o: draw.c qxw.h draw.h gui.h common.h gcc $(COPTS) -c draw.c `pkg-config --cflags gtk+-2.0` -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 ## REL- qxw-20110923/dicts.c0000644000175000017500000005550411637123101011662 0ustar momo// $Id: dicts.c -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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 #include "common.h" #include "gui.h" #include "dicts.h" // default dictionaries #define NDEFDICTS 4 char*defdictfn[NDEFDICTS]={ "/usr/dict/words", "/usr/share/dict/words", "/usr/share/dict/british-english", "/usr/share/dict/american-english"}; // 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"; int chartol[256]; char ltochar[MAXNL]; int nl=36; // needs to be initialised before call to resetstate() #define MEMBLK 100000 // unit of memory allocation struct memblk { struct memblk*next; int ct; char s[MEMBLK]; }; static struct memblk*dstrings=0; // dictionary string pool struct answer*ans=0,**ansp=0; struct light*lts=0; // int lcount[MXSZ+1]={0}; // number of lights of each length int atotal=0; // total answers int ltotal=0; // total lights int ultotal=0; // total uniquified lights char*aused=0; // answer already used while filling char*lused=0; // light already used while filling 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"}; #define HTABSZ 1048576 static int ahtab[HTABSZ]; // answer pool is linked list of `struct memblk's containing // strings: // char 0 of string is word score*10+128 (in range 28..228); // char 1 of string is dictionary number // char 2 onwards: // (0-terminated) citation form, in UTF-8 // (0-terminated) untreated light form, in chars int loaddicts(int sil) { // load (or reload) dictionaries from dfnames[] // sil=1 suppresses error reporting // returns: 0=success; 4=no success (out of memory) FILE *fp=0; char s0[SLEN*2]; // citation form wchar_t ws[SLEN]; // citation form const wchar_t*wp; char s1[SLEN]; // light form char t[SLEN+1]; // input buffer const char*u; struct memblk*mp,*p,*q; struct answer*ap; int at,c,dn,i,j,k,l,l0,l1,m,ml,mode,owd,r; unsigned int h; int f0,f1,f2,f3; //int lc[MXSZ+1]; float f; mbstate_t ps; pcre*sre,*are; const char*pcreerr; int pcreerroff; int pcreov[120]; char sfilter[SLEN+1]; char afilter[SLEN+1]; freedicts(); at=0; r=0; ml=MEMBLK; // number of bytes stored so far in current memory block mp=NULL; // current memblk being filled dstrings=NULL; // linked list anchor for(dn=0;dn=0&&t[j]>=0&&t[j]<=' ') t[j--]=0; // UKACD has DOS line break; cope with this inter alia 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 spaces, tabs etc. j++; if(j<1) goto ew0; if(mode) for(i=0;i<=j;i++) ws[i]=(unsigned char)t[i]; // do ISO-8859-1 `conversion' else { memset(&ps,0,sizeof(ps)); u=t;j=mbsrtowcs(ws,&u,SLEN,&ps); // do UTF-8 conversion if(j==-1) {DEB1 printf("File does not appear to be UTF-8 encoded\n");r=2;goto ew1;} if(j==SLEN) goto ew0; // string too long: discard } ws[j]=0; // now have citation form as wide char string for(i=l1=0;i255) c='#'; else c=chmap[m]; if(c=='#') {DEB1 printf("[%d=%02x]\n",m,m);f0=1;goto ew0;} // reject words with invalid characters if((c<'A'||c>'Z')&&(c<'0'||c>'9')) continue; // skip other non-alphanumeric if(m>127) f1=1; // remember if we saw any non-7-bit codes s1[l1++]=c; // copy to temporary string } s1[l1]=0; // terminate: now have untreated light form in s1 if(l1==0) goto ew0; // empty: skip if(l1>MXSZ) {f2=1;goto ew0;} // too long: skip memset(&ps,0,sizeof(ps)); wp=ws; l0=wcsrtombs(s0,&wp,SLEN*2,&ps); // now have citation form in s0 if(l0==-1) {f3=1;goto ew0;} 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) goto ew0; // 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) goto ew0; // failed match } if(ml+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) goto ew4; q->next=NULL; if(mp==NULL) dstrings=q; else mp->next=q; // link into list mp=q; mp->ct=0; ml=0;} *(mp->s+ml++)=(char)floor(f*10.0+128.5); // score with rounding *(mp->s+ml++)=dn; strcpy(mp->s+ml,s0);ml+=l0+1; strcpy(mp->s+ml,s1);ml+=l1+1; mp->ct++;at++; // count words of each length, words in this memblk, and total words ew0:; if(owd) break; } DEB1 if(f0) printf("Discarded entries in dictionary file %s containing symbols\n",dfnames[dn]); DEB1 if(f1) printf("Removed accents from entries in dictionary file %s\n",dfnames[dn]); DEB1 if(f2) printf("Discarded entries in dictionary file %s more than %d letters long\n",dfnames[dn],MXSZ); DEB1 if(f3) printf("Discarded entries in dictionary file %s with erroneous UTF-8\n",dfnames[dn]); ew1: if(!owd) if(fp) fclose(fp); if(r==0) break; // read successfully } ew2: if(sre) pcre_free(sre); if(are) pcre_free(are); } if(r) { freedicts(); return r; } // 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 p=dstrings; k=0; while(p!=NULL) { for(i=0,l=0;ict;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; } assert(k==atotal); for(i=0;iul,(*(struct answer**)q)->ul); if(u) return u; u=strcmp( (*(struct answer**)p)->cf,(*(struct answer**)q)->cf); return u; } qsort(ansp,atotal,sizeof(struct answer*),cmpans); ap=0; // ap points to first of each group of matching citation forms for(i=0,j=-1;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); aused=(char*)malloc(atotal*sizeof(char)); if(aused==NULL) goto ew4; return 0; ew4: freedicts(); reperr("Out of memory loading dictionaries"); return 4; } void freedicts(void) { // free all memory allocated by loaddicts, even if it aborted mid-load struct memblk*p; while(dstrings) {p=dstrings->next;free(dstrings);dstrings=p;} FREEX(ans); FREEX(ansp); FREEX(aused); atotal=0; } // Run through some likely candidates for default dictionaries. // Return // 0: found something // !=0: nothing found int loaddefdicts() {int i; 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 while(l!=-1) { if(!strcmp(s,lts[l].s)) {u=lts[l].uniq;break;} // match found l=lts[l].hashslink; } if(u==-1) { 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; } u=ultotal++; // allocate new uniquifying number 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].hashslink =hstab [h0]; hstab [h0]=ltotal; // insert into hash tables lts[ltotal].hashaeslink=haestab[h1]; haestab[h1]=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 int addlight(const char*s,int a,int e) { int l; int*p; l=findlight(s,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[MXSZ+1]; int j,k,l,u; l=strlen(s); if(l!=lightlength) return 0; if(tambaw&&!isword(s)) return 0; if(curem&1) { // forwards entry method u=addlight(s,curans,0);if(u) return u; } if(curem&2) { for(j=0;j='A'&&s[i]<='Z'); 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=clueorderindex=l0) return treatedanswer(s); if(l!=lightlength+1) return 0; c0=treatmsgAZ[0][clueorderindex]; for(i=0;s[i];i++) if(s[i]==c0) { for(j=0;j=l0) return treatedanswer(s); if(l<=lightlength) return 0; c0=treatmsgAZ[0][clueorderindex]; for(i=0,j=0;s[i];i++) if(s[i]!=c0) t[j++]=s[i]; t[j]=0; if(j!=lightlength) return 0; // not necessary, but improves speed slightly return treatedanswer(t); case 8: // insert single letter l0=strlen(treatmsg[0]); if(clueorderindex>=l0) return treatedanswer(s); if(l!=lightlength-1) return 0; c0=treatmsgAZ[0][clueorderindex]; 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; FREEX(lused); for(i=0;idmask,curem=lp->emask,curten=lp->ten; lightlength=llen; DEB2 printf("getinitflist(%08x) llen=%d dmask=%08x emask=%08x ten=%d: ",(int)lp,llen,curdm,curem,curten); 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; } *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-20110923/draw.h0000644000175000017500000000360211637123102011507 0ustar momo// $Id: draw.h -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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__ extern void ansform(char*t0,int t0l,int ln,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); extern void edgecoords(float*x0,float*y0,float*x1,float*y1,int x,int y,int d); extern void addsqbbox(float*x0,float*y0,float*x1,float*y1,int x,int y); extern void getsqbbox(float*x0,float*y0,float*x1,float*y1,int x,int y); extern void getmgbbox(float*x0,float*y0,float*x1,float*y1,int x,int y); extern void mgcentre(float*u,float*v,int x,int y,int d,int l); extern char*dname[NGTYPE][MAXNDIR]; #endif qxw-20110923/qxw.c0000644000175000017500000013564011637123103011375 0ustar momo// $Id: qxw.c -1 $ /* Qxw is a program to help construct and publish crosswords. Copyright 2011 Mark Owen 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 #include #include #include #include #include #include "common.h" #include "qxw.h" #include "filler.h" #include "dicts.h" #include "gui.h" #include "draw.h" // forward declarations static void stop_compute(void); // 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 char bname[SLEN]="untitled"; // name without file extensions char filename[SLEN+50]=""; // name with file extension int curx=0,cury=0,dir=0; // cursor position, and direction: 0=to right, 1=down int unsaved=0; // edited-since-save flag int fillmode=0; // mode flag for filler: 0=stopped, 1=filling all, 2=filling selection, 3=just generating word lists 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} }; 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*llist=0; // buffer for light index list // GRID struct square gsq[MXSZ][MXSZ]; struct vl vls[NVL]; extern int getnumber(int x,int y) { return gsq[x][y].number; } extern int getflags(int x,int y) {int xr,yr; getmergerep(&xr,&yr,x,y); return gsq[xr][yr].fl; } extern 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; } extern 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; } extern 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; } extern 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; } // 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; } // 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 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 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 0 if light bigger than MXSZ // lp: character ptrs // lx: mergerep square x // ly: mergerep square y // ls: index of contributing string // lo: offset in contributing string // le: entry number int getlightdat(char**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[MXSZ*2],ty[MXSZ*2],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;ddmask==0) return 0; // skip if dictionary mask empty l=getlightdat(0,0,0,0,0,0,i,j,d); if(l<1) return 0; // too long: ignore return l; } // 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 int addwordfd(int i,int j,int d) { int f,k,l,le[MXSZ],lx[MXSZ],ly[MXSZ]; char *lcp[MXSZ]; 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;kchecking++; st_ce=0; for(i=0;i1) st_ce++; // number of checked squares st_sc=0;st_2u=0;st_3u=0; assert(MXSZ<64); // (we are about to use a bitmap of checking patterns) for(i=0;ie->checking>1) k++; // count checked cells in word else m|=1<l*maxcheck) st_locc[l]++,v|=8; st_lsc[l]+=k; if(kst_lmxc[l]) st_lmxc[l]=k; st_sc+=k; if(m&(m<<1)) st_2u++,v|=1; // double+ unch? if(m&(m<<1)&(m<<2)) st_3u++,v|=2; // triple+unch? if(words[i].ldir=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[MXSZ],gy[MXSZ]; 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[MXSZ],gy[MXSZ]; 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[MXSZ],gy[MXSZ],tgx[MXSZ],tgy[MXSZ]; 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"); 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]; 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"); DEB1 printf("saveprefs %s\n",s); fp=fopen(s,"w");if(!fp) return; for(i=0;ibgcol=0xffffff; p->fgcol=0x000000; p->fstyle=0; p->dech=0; p->ten=0; p->spor=0; } void resetlp(struct lprop*p) { p->dmask=1; p->emask=1; p->ten=0; p->lpor=0; } void resetstate(void) {int i,j,k; for(i=0;ibgcol&=0xffffff; sp->fgcol&=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; } // tidy up light properties structure static void fixlp(struct lprop*lp) { lp->dmask&=(1<emask&=(1<ten=!!lp->ten; lp->lpor=!!lp->lpor; } // FILE SAVE/LOAD void a_filenew(void) { resetstate(); setbname("untitled"); syncgui(); compute(); undo_push(); unsaved=0; } #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,b,m,f,wf; char *p,s[SLEN*4],s0[SLEN*4],*t,c; struct sprop sp; struct lprop lp; FILE*fp; fp=fopen(filename,"r"); if(!fp) {fserror();return;} resetstate(); *gtitle=0; *gauthor=0; setbname(filename); 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].ct[0][0]=c; gsq[i][j].ct[0][1]=0; } if(fgetc(fp)!='\n') goto ew1; } } else { // #QXW2 load if(s[5]=='v'&&atoi(s+6)>1) 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; NEXTL; if(strcmp(s,"AUT")) goto ew1; NEXTL; if(s[0]!='+') goto ew1; strncpy(gauthor,s+1,SLEN-1); gauthor[SLEN-1]=0; DEB1 printf("L0\n"); NEXTL; if(!strncmp(s,"GLP ",4)) { if(sscanf(s,"GLP %d %d %hhd %hhd\n",&dlp.dmask,&dlp.emask,&dlp.ten,&dlp.lpor)!=4) goto ew1; dlp.lpor=0; fixlp(&dlp); NEXTL; } while(!strncmp(s,"GSP ",4)) { resetsp(&dsp); if(sscanf(s,"GSP %x %x %hhd %hhd %hhd %hhd\n",&dsp.bgcol,&dsp.fgcol,&dsp.ten,&dsp.spor,&dsp.fstyle,&dsp.dech)<4) goto ew1; dsp.spor=0; fixsp(&dsp); NEXTL; } while(!strncmp(s,"TM ",3)) { if(sscanf(s,"TM %d %d %d\n",&j,&t0,&t1)!=3) goto ew1; NEXTL; if(j==0&&s[0]=='+') { if(t0<0||t0>=NATREAT) continue; treatmode=t0,tambaw=!!t1; if(treatmode==NATREAT-1) { 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,"DFN ",4)) { if(sscanf(s,"DFN %d\n",&j)!=1) goto ew1; NEXTL; if(j<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<='A'&&c<='Z')||(c>='0'&&c<='9'))?c:' '; gsq[i][j].ct[0][1]=0; NEXTL; } DEB1 printf("L2\n"); while(!strncmp(s,"SQSP ",5)) { resetsp(&sp); if(sscanf(s,"SQSP %d %d %x %x %hhd %hhd %hhd %hhd\n",&i,&j,&sp.bgcol,&sp.fgcol,&sp.ten,&sp.spor,&sp.fstyle,&sp.dech)<6) goto ew1; if(i<0||i>=MXSZ||j<0||j>=MXSZ) continue; fixsp(&sp); gsq[i][j].sp=sp; NEXTL; } while(!strncmp(s,"SQLP ",5)) { if(sscanf(s,"SQLP %d %d %d %d %d %hhd %hhd\n",&i,&j,&d,&lp.dmask,&lp.emask,&lp.ten,&lp.lpor)!=7) goto ew1; if(i<0||i>=MXSZ||j<0||j>=MXSZ||d<0||d>=ndir[gtype]) continue; 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>=MXSZ||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)) { if(sscanf(s,"VLP %d %d %d %hhd %hhd\n",&d,&lp.dmask,&lp.emask,&lp.ten,&lp.lpor)!=5) goto ew1; if(d<0||d>=NVL) continue; 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; s0[MXSZ]=0; for(k=0;s0[k];k++) if(!(s0[k]>='A'&&s0[k]<='Z')||(s0[k]>='0'&&s0[k]<='9')) s0[k]=' '; strcpy(gsq[i][j].ct[d],s0); NEXTL; } } if(fclose(fp)) goto ew3; if(treatmode==NATREAT-1) { if((p=loadtpi())) { sprintf(s,"Error loading custom plug-in\n%.100s",p); reperr(s); } } else unloadtpi(); donumbers(); loaddicts(0); undo_push();unsaved=0;syncgui();compute(); if(wf) reperr("File was saved using\na newer version of Qxw.\nSome features may be lost."); return; // no errors ew1:fclose(fp);syncgui();reperr("File format error");goto ew2; ew3:fserror(); ew2: a_filenew(); loaddefdicts(); } // write state to file void a_save(void) { int d,i,j,k; char c,s0[MXSZ+1]; FILE*fp; setbname(filename); fp=fopen(filename,"w"); if(!fp) {fserror();return;} if(fprintf(fp,"#QXW2v1 http://www.quinapalus.com\n")<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\n",dlp.dmask,dlp.emask,dlp.ten,dlp.lpor)<0) goto ew0; if(fprintf(fp,"GSP %06x %06x %d %d %d %d\n",dsp.bgcol,dsp.fgcol,dsp.ten,dsp.spor,dsp.fstyle,dsp.dech)<0) goto ew0; if(fprintf(fp,"TM 0 %d %d\n+%s\n",treatmode,tambaw,tpifname)<0) goto ew0; for(i=0;i='A'&&c<='Z')||(c>='0'&&c<='9')) 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;j='A'&&s0[k]<='Z')||(s0[k]>='0'&&s0[k]<='9')) s0[k]='.'; if(fprintf(fp,"SQCT %d %d %d %s\n",i,j,d,s0)<0) goto ew0; } if(fprintf(fp,"END\n")<0) goto ew0; if(ferror(fp)) goto ew0; if(fclose(fp)) goto ew0; unsaved=0;return; // saved successfully ew0: fserror(); fclose(fp); } char*titlebyauthor(void) {static char t[SLEN*2+100]; if(gtitle[0]) strcpy(t,gtitle); else strcpy(t,bname); if(gauthor[0]) strcat(t," by "),strcat(t,gauthor); return t; } // MAIN char*optarg; int optind,opterr,optopt; int main(int argc,char*argv[]) {int i,nd; for(i=0;i<26;i++) ltochar[i]=i+'A', chartol[i+'A']=i; for(i=0;i<10;i++) ltochar[i+26]=i+'0',chartol[i+'0']=i+26; resetstate(); for(i=0;i]* [qxw_file]\n",argv[0]); printf("This is Qxw, release %s.\n\n\ Copyright 2011 Mark Owen\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; } gtk_init(&argc,&argv); startgtk(); a_filenew(); // reset grid loadprefs(); // load preferences file (silently failing to defaults) if(optind",i); if(i) break; } DEB1 printf("\n"); if(i<0) return 1; return 0; } void postexport(void) { DEB1 printf("postexport()\n"); filler_stop(); fillmode=0; compute(); // restore everything } static void stop_compute(void) { filler_stop(); } // comparison function for sorting feasible word list by score static int cmpscores(const void*p,const void*q) {float f,g; f=ansp[lts[*(int*)p].ans]->score; g=ansp[lts[*(int*)q].ans]->score; if(fg) return -1; return 0; } // 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;llistdm=w->lp->dmask; // copy list across 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 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.