--- /dev/null
+/******************************************************************************
+
+ Cross-Words applet v1.5 (c) Francois FLEURET
+
+ This small applet is an interactive crosswords grid for web
+ pages. Mail <francois.fleuret@inria.fr> for bug reports and
+ questions.
+
+ In the past, I put his applet under a pure GPL licence. So, what was
+ expected to happen happened : commercial sites used it and I saw
+ people earning money with my work without giving me a single
+ buck. This is bad. So, now this software is distributed under the FFL
+ (Francois Fleuret Licence) which is the same as GPL version 2, plus
+ the following paragraph:
+
+ *
+ * RESTRICTION OF USE FOR APPLET SOFTWARES
+ *
+ * 13 The site the applet is used on must be a NON-PROFIT site, which
+ * implies it does not contain banners or any other mean to make money
+ * (including indirectly, for example as an ISP portal). The name of
+ * all the authors of the software and a link to the URL they provide
+ * in the source code must appear on any web page that uses the
+ * applet. If the source code was modified, the modified source must
+ * be downloadable from the site the applet is used on.
+ *
+
+ So, if you use this applet on a non-profit site, please add a link to
+ http://www-rocq.inria.fr/~fleuret and a short note indicating my name
+ as the author of the applet. For any other usage, please contact
+ me. Be sure we can make a deal for a reasonnable price.
+
+ 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.
+
+ Sept 11th 2000, François Fleuret
+
+******************************************************************************/
+
+import java.applet.Applet;
+import java.awt.*;
+import java.awt.event.*;
+import java.lang.Object;
+
+class Square {
+ char solution;
+ char found;
+ boolean ajour;
+ boolean holdCursor;
+ boolean locked;
+ Color color;
+
+ Square(char c) {
+ solution = c;
+ found = ' ';
+ ajour = false;
+ locked = false;
+ color = Color.black;
+ }
+
+ void setFound(char c) { if(c != found) { found = c; ajour = false; } }
+
+ void putCursor() { if(!holdCursor) ajour = false; holdCursor = true; }
+
+ void remCursor() { if(holdCursor) ajour = false; holdCursor = false; }
+
+ void lock(Color c) {
+ if(!locked) {
+ ajour = false;
+ locked = true;
+ color = c;
+ }
+ }
+}
+
+class Grid extends Panel implements KeyListener, MouseListener {
+ int gridWidth, gridHeight;
+ int xCursor, yCursor;
+ int nbLetters, nbFound;
+ boolean cursorHorizontal;
+
+ int xMargin, yMargin;
+ Square grid[][];
+ Font font, smallFont;
+ FontMetrics fontMetrics, smallFontMetrics;
+ int squareSize;
+
+ TextArea definitionArea;
+ String[][][] definitions;
+
+ Grid(Font f, Font sf,
+ FontMetrics fm, FontMetrics sfm,
+ int w, int h,
+ String content,
+ TextArea ta, String def) {
+
+ int x, y, xx, yy, k;
+
+ font = f; fontMetrics = fm;
+ smallFont = sf; smallFontMetrics = sfm;
+ squareSize = fontMetrics.getHeight();
+
+ gridWidth = w;
+ gridHeight = h;
+ grid = new Square[gridWidth][gridHeight];
+
+ yMargin = smallFontMetrics.getHeight();
+ xMargin = yMargin;
+
+ nbLetters = 0; nbFound = 0;
+ k = 0;
+ for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++) {
+ if(k<content.length())
+ grid[x][y] = new Square(content.charAt(k++));
+ else
+ grid[x][y] = new Square('.');
+ if(grid[x][y].solution != '.') nbLetters++;
+ }
+
+ xCursor = 0; yCursor = 0;
+ cursorHorizontal = true;
+ grid[xCursor][yCursor].putCursor();
+
+ if(ta != null) {
+ definitionArea = ta;
+ definitions = new String[gridWidth][gridHeight][2];
+
+ String direction, sd;
+ int i, j;
+
+ direction="h";
+
+ for(y=0; y<gridHeight; y++) for(x=0; x<gridWidth; x++) {
+ definitions[x][y][0] = "";
+ definitions[x][y][1] = "";
+ }
+
+ j = 0;
+ while(j < def.length()-1) {
+
+ i = j; j = def.indexOf('/', i);
+ if((j > 0) && ((def.indexOf(':', i) > j) || (def.indexOf(':', i) < 0))) {
+ direction = def.substring(i, j).toLowerCase();
+ j++;
+ } else j = i;
+
+
+ i = j;
+ y = 0;
+ while((def.charAt(i) >= 'a') && (def.charAt(i) <= 'z'))
+ y = y*26 + ((int) def.charAt(i++)) - 'a';
+
+ x = 0;
+ while((def.charAt(i) >= '0') && (def.charAt(i) <= '9'))
+ x = x*10 + ((int) def.charAt(i++)) - '0';
+ x--;
+ i++;
+
+ j = def.indexOf('/', i);
+ if(j < 0) j = def.length();
+ sd = def.substring(i, j);
+ j++;
+
+ if(direction.equals("horizontal")) {
+ for(xx = x; (xx>=0) && (grid[xx][y].solution != '.'); xx--)
+ definitions[xx][y][0] = sd;
+ for(xx = x; (xx<gridWidth) && (grid[xx][y].solution != '.'); xx++)
+ definitions[xx][y][0] = sd;
+ } else if(direction.equals("vertical")) {
+ for(yy = y; (yy>=0) && (grid[x][yy].solution != '.'); yy--)
+ definitions[x][yy][1] = sd;
+ for(yy = y; (yy<gridHeight) && (grid[x][yy].solution != '.'); yy++)
+ definitions[x][yy][1] = sd;
+ } else System.out.println("Unknown orientation '" + direction + "'");
+ }
+ }
+
+ addKeyListener(this);
+ addMouseListener(this);
+ }
+
+ public void giveWord() {
+ int xx, yy;
+
+ int x = xCursor;
+ int y = yCursor;
+
+ if(cursorHorizontal) {
+ for(xx = x; (xx>=0) && (grid[xx][y].solution != '.'); xx--) {
+ grid[xx][y].setFound(grid[xx][y].solution);
+ grid[xx][y].lock(Color.red);
+ }
+
+ for(xx = x; (xx<gridWidth) && (grid[xx][y].solution != '.'); xx++) {
+ grid[xx][y].setFound(grid[xx][y].solution);
+ grid[xx][y].lock(Color.red);
+ }
+ } else {
+ for(yy = y; (yy>=0) && (grid[x][yy].solution != '.'); yy--) {
+ grid[x][yy].setFound(grid[x][yy].solution);
+ grid[x][yy].lock(Color.red);
+ }
+
+ for(yy = y; (yy<gridHeight) && (grid[x][yy].solution != '.'); yy++) {
+ grid[x][yy].setFound(grid[x][yy].solution);
+ grid[x][yy].lock(Color.red);
+ }
+ }
+
+ nbFound = 0;
+ for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
+ if(grid[x][y].solution == grid[x][y].found) nbFound++;
+
+ if(nbFound == nbLetters) {
+ for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
+ grid[x][y].lock(Color.blue);
+ }
+
+ repaint();
+ }
+
+ public void checkWord() {
+ boolean ok;
+ int xmin, xmax, xx;
+ int ymin, ymax, yy;
+
+ int x = xCursor;
+ int y = yCursor;
+
+ if(cursorHorizontal) {
+ ok = true;
+ for(xmin = x; ok && (xmin>=0) && (grid[xmin][y].solution != '.'); xmin--)
+ ok &= (grid[xmin][y].solution == grid[xmin][y].found);
+
+ for(xmax = x; ok && (xmax<gridWidth) && (grid[xmax][y].solution != '.'); xmax++)
+ ok &= (grid[xmax][y].solution == grid[xmax][y].found);
+
+ xmin++; xmax--;
+
+ if(ok) for(xx = xmin; xx<=xmax; xx++) grid[xx][y].lock(Color.green);
+
+ } else {
+ ok = true;
+ for(ymin = y; ok && (ymin>=0) && (grid[x][ymin].solution != '.'); ymin--)
+ ok &= (grid[x][ymin].solution == grid[x][ymin].found);
+
+ for(ymax = y; ok && (ymax<gridHeight) && (grid[x][ymax].solution != '.'); ymax++)
+ ok &= (grid[x][ymax].solution == grid[x][ymax].found);
+
+ ymin++; ymax--;
+
+ if(ok) for(yy = ymin; yy<=ymax; yy++) grid[x][yy].lock(Color.green);
+ }
+
+ repaint();
+ }
+
+ public synchronized void drawSquare(Graphics g, int x, int y) {
+ int arrowMargin = 2;
+ int xe, ye, deltaX;
+ int xf[], yf[];
+ char tmp[];
+ tmp = new char[1];
+ xe = x*squareSize+xMargin; ye = y*squareSize+yMargin;
+ g.setFont(font);
+
+ if(grid[x][y].solution != '.') {
+ g.setColor(Color.white);
+ g.fillRect(xe, ye, squareSize, squareSize);
+
+ if(grid[x][y].holdCursor) {
+ xf = new int[5]; yf = new int[5];
+ g.setColor(Color.gray);
+ if(cursorHorizontal) {
+ xf[0] = xe + 1 + arrowMargin;
+ yf[0] = ye + 1 + arrowMargin;
+ xf[1] = xe + squareSize/2;
+ yf[1] = ye + 1 + arrowMargin;
+ xf[2] = xe + squareSize - 1 - arrowMargin;
+ yf[2] = ye + squareSize/2;
+ xf[3] = xe + squareSize/2;
+ yf[3] = ye + squareSize - arrowMargin;
+ xf[4] = xe + 1 + arrowMargin;
+ yf[4] = ye + squareSize - arrowMargin;
+ g.fillPolygon(xf, yf, 5);
+ }
+ else
+ {
+ xf[0] = xe + squareSize - arrowMargin;
+ yf[0] = ye + 1 + arrowMargin;
+ xf[1] = xe + squareSize - arrowMargin;
+ yf[1] = ye + squareSize/2;
+ xf[2] = xe + squareSize/2;
+ yf[2] = ye + squareSize - 1 - arrowMargin;
+ xf[3] = xe + 1 + arrowMargin;
+ yf[3] = ye + squareSize/2;
+ xf[4] = xe + 1 + arrowMargin;
+ yf[4] = ye + 1 + arrowMargin;
+ g.fillPolygon(xf, yf, 5);
+ }
+ }
+
+ g.setColor(Color.black);
+ g.drawRect(xe, ye, squareSize, squareSize);
+ tmp[0] = grid[x][y].found;
+ deltaX = (squareSize - fontMetrics.charWidth(tmp[0]))/2;
+
+ if(grid[x][y].locked) g.setColor(grid[x][y].color);
+ g.drawChars(tmp, 0, 1, xe + deltaX, ye+fontMetrics.getAscent());
+
+ } else {
+ g.setColor(Color.black);
+ g.fillRect(xe, ye, squareSize+1, squareSize+1);
+ }
+ grid[x][y].ajour = true;
+ }
+
+ public synchronized void update(Graphics g) {
+ int x, y;
+ for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
+ if(!grid[x][y].ajour) drawSquare(g, x, y);
+ }
+
+ public synchronized void paint(Graphics g) {
+ int x, y;
+ char tmp[];
+
+ g.setFont(smallFont);
+ g.setColor(Color.black);
+
+ tmp = new char[1];
+ for(y = 0; y<gridHeight; y++) {
+ tmp[0] = (char) (y + (int) 'a');
+ g.drawChars(tmp, 0, 1,
+ xMargin - smallFontMetrics.charWidth(tmp[0]) - 3,
+ y*squareSize + yMargin + smallFontMetrics.getAscent());
+ }
+
+ for(x = 0; x<gridWidth; x++)
+ g.drawString("" + (x+1),
+ xMargin + x*squareSize + 3,
+ yMargin - smallFontMetrics.getDescent() - 1);
+
+ for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
+ drawSquare(g, x, y);
+ }
+
+ public void keyTyped(KeyEvent keyEvent) {}
+ public void keyReleased(KeyEvent keyEvent) {}
+
+ public void keyPressed(KeyEvent keyEvent) {
+ int x, y;
+ int code = keyEvent.getKeyCode();
+ char key = keyEvent.getKeyChar();
+
+ if(code == KeyEvent.VK_UP) {
+ y = yCursor-1;
+ while((y > 0) && (grid[xCursor][y].solution == '.')) y--;
+ if((y >= 0) && (grid[xCursor][y].solution != '.')) {
+ grid[xCursor][yCursor].remCursor();
+ yCursor = y;
+ grid[xCursor][yCursor].putCursor();
+ }
+ }
+ else if(code == KeyEvent.VK_DOWN) {
+ y = yCursor+1;
+ while((y < gridHeight-1) && (grid[xCursor][y].solution == '.')) y++;
+ if((y < gridHeight) && (grid[xCursor][y].solution != '.')) {
+ grid[xCursor][yCursor].remCursor();
+ yCursor = y;
+ grid[xCursor][yCursor].putCursor();
+ }
+ }
+ else if(code == KeyEvent.VK_LEFT) {
+ x = xCursor-1;
+ while((x > 0) && (grid[x][yCursor].solution == '.')) x--;
+ if((x >= 0) && (grid[x][yCursor].solution != '.')) {
+ grid[xCursor][yCursor].remCursor();
+ xCursor = x;
+ grid[xCursor][yCursor].putCursor();
+ }
+ }
+ else if(code == KeyEvent.VK_RIGHT) {
+ x = xCursor+1;
+ while((x < gridWidth-1) && (grid[x][yCursor].solution == '.')) x++;
+ if((x < gridWidth) && (grid[x][yCursor].solution != '.')) {
+ grid[xCursor][yCursor].remCursor();
+ xCursor = x;
+ grid[xCursor][yCursor].putCursor();
+ }
+ }
+ else if(code == Event.ENTER) {
+ cursorHorizontal = !cursorHorizontal;
+ grid[xCursor][yCursor].ajour = false;
+ }
+ else if(code == Event.BACK_SPACE) {
+ if((grid[xCursor][yCursor].found == ' ') || grid[xCursor][yCursor].locked) {
+ if(cursorHorizontal) {
+ if(xCursor > 0)
+ if(grid[xCursor - 1][yCursor].solution != '.') {
+ grid[xCursor][yCursor].remCursor();
+ xCursor--;
+ grid[xCursor][yCursor].putCursor();
+ }
+ } else {
+ if(yCursor > 0)
+ if(grid[xCursor][yCursor - 1].solution != '.') {
+ grid[xCursor][yCursor].remCursor();
+ yCursor--;
+ grid[xCursor][yCursor].putCursor();
+ }
+ }
+ }
+
+ if(!grid[xCursor][yCursor].locked) {
+ if(grid[xCursor][yCursor].found == grid[xCursor][yCursor].solution)
+ nbFound--;
+ grid[xCursor][yCursor].setFound(' ');
+ }
+ } else if(key == ' ') {
+ if(definitionArea != null) {
+ if(cursorHorizontal) definitionArea.setText(definitions[xCursor][yCursor][0]);
+ else definitionArea.setText(definitions[xCursor][yCursor][1]);
+ }
+ } else if((key >= 'a') && (key <= 'z')) {
+ if(!grid[xCursor][yCursor].locked) {
+ if(grid[xCursor][yCursor].found == grid[xCursor][yCursor].solution)
+ nbFound--;
+ grid[xCursor][yCursor].setFound((char) key);
+ if(grid[xCursor][yCursor].found == grid[xCursor][yCursor].solution)
+ nbFound++;
+ }
+
+ if(cursorHorizontal) {
+ if(xCursor < gridWidth-1)
+ if(grid[xCursor + 1][yCursor].solution != '.') {
+ grid[xCursor][yCursor].remCursor();
+ xCursor++;
+ grid[xCursor][yCursor].putCursor();
+ }
+ } else {
+ if(yCursor < gridHeight-1)
+ if(grid[xCursor][yCursor + 1].solution != '.') {
+ grid[xCursor][yCursor].remCursor();
+ yCursor++;
+ grid[xCursor][yCursor].putCursor();
+ }
+ }
+
+ if(nbFound == nbLetters) {
+ for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
+ grid[x][y].lock(Color.blue);
+ }
+ }
+
+ repaint();
+ }
+
+ public void mousePressed(MouseEvent mouseEvent) {
+ int xc, yc;
+ xc = (mouseEvent.getX()-xMargin)/squareSize;
+ if(xc < 0) xc = 0;
+ if(xc >= gridWidth) xc = gridWidth-1;
+ yc = (mouseEvent.getY()-yMargin)/squareSize;
+ if(yc < 0) yc = 0;
+ if(yc >= gridHeight) yc = gridHeight-1;
+
+ if(grid[xc][yc].solution != '.') {
+ grid[xCursor][yCursor].remCursor();
+ xCursor = xc; yCursor = yc;
+ grid[xCursor][yCursor].putCursor();
+ }
+
+ repaint();
+ }
+
+ public void mouseClicked(MouseEvent mouseEvent) {}
+ public void mouseReleased(MouseEvent e) {}
+ public void mouseEntered(MouseEvent e) {}
+ public void mouseExited(MouseEvent e) {}
+
+ public Dimension getPreferredSize() {
+ return new Dimension(xMargin + gridWidth*squareSize + 1,
+ yMargin + gridHeight*squareSize + 1);
+ }
+
+}
+
+public class Words extends Applet implements ActionListener {
+ Grid grid;
+ Button checkButton, giveButton;
+ TextArea definitionArea;
+
+ public void init() {
+ String fontName, s, definitionString;
+ int fontSize;
+
+ Font f, sf;
+ FontMetrics fm, sfm;
+ int w, h, wd, hd;
+
+ s = getParameter("bgcolor");
+ if(s != null)
+ setBackground(new Color(Integer.parseInt(s)));
+
+ fontName = getParameter("font");
+ if(fontName == null) fontName = "Courier";
+
+ s = getParameter("fontsize");
+ if(s == null) fontSize = 48; else fontSize = Integer.parseInt(s);
+
+ f = new Font(fontName, Font.PLAIN, fontSize);
+
+ fm = getGraphics().getFontMetrics(f);
+
+ fontName = getParameter("smallfont");
+ if(fontName == null) fontName = "Courier";
+
+ s = getParameter("smallfontsize");
+ if(s == null) fontSize = 20; else fontSize = Integer.parseInt(s);
+
+ sf = new Font(fontName, Font.ITALIC, fontSize);
+ sfm = getGraphics().getFontMetrics(sf);
+
+ s = getParameter("gridwidth");
+ if(s == null) w = 1; else w = Integer.parseInt(s);
+
+ s = getParameter("gridheight");
+ if(s == null) h = 1; else h = Integer.parseInt(s);
+
+ definitionString = null;
+
+ s = getParameter("definitions");
+
+ if(s != null) {
+ int i, j;
+ wd = -1; hd = -1;
+
+ // Find the width and the height
+ i = 0; j = s.indexOf('x', i);
+ wd = Integer.parseInt(s.substring(i, j));
+ i = j+1; j = s.indexOf('/', i);
+ hd = Integer.parseInt(s.substring(i, j));
+
+ i = j+1; definitionString = s.substring(i, s.length());
+
+ if((wd > 0) && (hd > 0)) {
+ definitionArea = new TextArea("", hd, wd, TextArea.SCROLLBARS_NONE);
+ definitionArea.setEditable(false);
+ }
+ }
+
+ s = getParameter("content"); if(s == null) s = ".";
+ grid = new Grid(f, sf, fm, sfm, w, h, s, definitionArea, definitionString);
+ add("north", grid);
+
+ s = getParameter("checkwordlabel");
+ if(s != null) {
+ checkButton = new Button(s);
+ checkButton.addActionListener(this);
+ }
+
+ s = getParameter("givewordlabel");
+ if(s != null) {
+ giveButton = new Button(s);
+ giveButton.addActionListener(this);
+ }
+
+ if((checkButton != null) || (giveButton != null)) {
+ Panel buttonPanel;
+ buttonPanel = new Panel();
+ if(checkButton != null) buttonPanel.add(checkButton);
+ if(giveButton != null) buttonPanel.add(giveButton);
+ if(definitionArea != null) add("south", definitionArea);
+ add("south", buttonPanel);
+ }
+ }
+
+ public void actionPerformed(ActionEvent event) {
+ Object source = event.getSource();
+ if(source == checkButton) grid.checkWord();
+ else if(source == giveButton) grid.giveWord();
+ }
+
+ public String getAppletInfo() {
+ return "Word applet v1.5\n" +
+ "Written and (c) Francois Fleuret, mail to <francois.fleuret@inria.fr>\n" +
+ "Check http://www-rocq.inria.fr/~fleuret\n";
+ }
+
+ public String[][] getParameterInfo() {
+ String chose[][] = {
+ { "bgcolor", "integer", "Background Color (optional)" },
+ { "font", "font name", "Grid character font name (optional)" },
+ { "fontsize", "integer", "Grid character font size (optional)" },
+ { "smallfont", "font name", "Row and column index font name (optional)" },
+ { "smallfontsize", "integer", "Row and column index font size (optional)" },
+ { "gridwidth", "integer", "Grid width" },
+ { "gridheight", "integer", "Grid height" },
+ { "content", "string", "Grid content" },
+ { "definitions", "string", "Definitions (optional)" },
+ { "checkwordlabel", "string", "Label for the 'check word' button (optional)" },
+ { "givewordlabel", "string", "Label for the 'give word' button (optional)" }
+ };
+
+ return chose;
+ }
+}
+