1 /******************************************************************************
3 Cross-Words applet v1.5 (c) Francois FLEURET
5 This small applet is an interactive crosswords grid for web
6 pages. Mail <francois.fleuret@inria.fr> for bug reports and
9 In the past, I put his applet under a pure GPL licence. So, what was
10 expected to happen happened : commercial sites used it and I saw
11 people earning money with my work without giving me a single
12 buck. This is bad. So, now this software is distributed under the FFL
13 (Francois Fleuret Licence) which is the same as GPL version 2, plus
14 the following paragraph:
17 * RESTRICTION OF USE FOR APPLET SOFTWARES
19 * 13 The site the applet is used on must be a NON-PROFIT site, which
20 * implies it does not contain banners or any other mean to make money
21 * (including indirectly, for example as an ISP portal). The name of
22 * all the authors of the software and a link to the URL they provide
23 * in the source code must appear on any web page that uses the
24 * applet. If the source code was modified, the modified source must
25 * be downloadable from the site the applet is used on.
28 So, if you use this applet on a non-profit site, please add a link to
29 http://www-rocq.inria.fr/~fleuret and a short note indicating my name
30 as the author of the applet. For any other usage, please contact
31 me. Be sure we can make a deal for a reasonnable price.
33 This program is distributed in the hope that it will be useful, but
34 WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36 General Public License for more details.
38 Sept 11th 2000, François Fleuret
40 ******************************************************************************/
42 import java.applet.Applet;
44 import java.awt.event.*;
45 import java.lang.Object;
63 void setFound(char c) { if(c != found) { found = c; ajour = false; } }
65 void putCursor() { if(!holdCursor) ajour = false; holdCursor = true; }
67 void remCursor() { if(holdCursor) ajour = false; holdCursor = false; }
78 class Grid extends Panel implements KeyListener, MouseListener {
79 int gridWidth, gridHeight;
81 int nbLetters, nbFound;
82 boolean cursorHorizontal;
87 FontMetrics fontMetrics, smallFontMetrics;
90 TextArea definitionArea;
91 String[][][] definitions;
94 FontMetrics fm, FontMetrics sfm,
97 TextArea ta, String def) {
101 font = f; fontMetrics = fm;
102 smallFont = sf; smallFontMetrics = sfm;
103 squareSize = fontMetrics.getHeight();
107 grid = new Square[gridWidth][gridHeight];
109 yMargin = smallFontMetrics.getHeight();
112 nbLetters = 0; nbFound = 0;
114 for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++) {
115 if(k<content.length())
116 grid[x][y] = new Square(content.charAt(k++));
118 grid[x][y] = new Square('.');
119 if(grid[x][y].solution != '.') nbLetters++;
122 xCursor = 0; yCursor = 0;
123 cursorHorizontal = true;
124 grid[xCursor][yCursor].putCursor();
128 definitions = new String[gridWidth][gridHeight][2];
130 String direction, sd;
135 for(y=0; y<gridHeight; y++) for(x=0; x<gridWidth; x++) {
136 definitions[x][y][0] = "";
137 definitions[x][y][1] = "";
141 while(j < def.length()-1) {
143 i = j; j = def.indexOf('/', i);
144 if((j > 0) && ((def.indexOf(':', i) > j) || (def.indexOf(':', i) < 0))) {
145 direction = def.substring(i, j).toLowerCase();
152 while((def.charAt(i) >= 'a') && (def.charAt(i) <= 'z'))
153 y = y*26 + ((int) def.charAt(i++)) - 'a';
156 while((def.charAt(i) >= '0') && (def.charAt(i) <= '9'))
157 x = x*10 + ((int) def.charAt(i++)) - '0';
161 j = def.indexOf('/', i);
162 if(j < 0) j = def.length();
163 sd = def.substring(i, j);
166 if(direction.equals("horizontal")) {
167 for(xx = x; (xx>=0) && (grid[xx][y].solution != '.'); xx--)
168 definitions[xx][y][0] = sd;
169 for(xx = x; (xx<gridWidth) && (grid[xx][y].solution != '.'); xx++)
170 definitions[xx][y][0] = sd;
171 } else if(direction.equals("vertical")) {
172 for(yy = y; (yy>=0) && (grid[x][yy].solution != '.'); yy--)
173 definitions[x][yy][1] = sd;
174 for(yy = y; (yy<gridHeight) && (grid[x][yy].solution != '.'); yy++)
175 definitions[x][yy][1] = sd;
176 } else System.out.println("Unknown orientation '" + direction + "'");
180 addKeyListener(this);
181 addMouseListener(this);
184 public void giveWord() {
190 if(cursorHorizontal) {
191 for(xx = x; (xx>=0) && (grid[xx][y].solution != '.'); xx--) {
192 grid[xx][y].setFound(grid[xx][y].solution);
193 grid[xx][y].lock(Color.red);
196 for(xx = x; (xx<gridWidth) && (grid[xx][y].solution != '.'); xx++) {
197 grid[xx][y].setFound(grid[xx][y].solution);
198 grid[xx][y].lock(Color.red);
201 for(yy = y; (yy>=0) && (grid[x][yy].solution != '.'); yy--) {
202 grid[x][yy].setFound(grid[x][yy].solution);
203 grid[x][yy].lock(Color.red);
206 for(yy = y; (yy<gridHeight) && (grid[x][yy].solution != '.'); yy++) {
207 grid[x][yy].setFound(grid[x][yy].solution);
208 grid[x][yy].lock(Color.red);
213 for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
214 if(grid[x][y].solution == grid[x][y].found) nbFound++;
216 if(nbFound == nbLetters) {
217 for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
218 grid[x][y].lock(Color.blue);
224 public void checkWord() {
232 if(cursorHorizontal) {
234 for(xmin = x; ok && (xmin>=0) && (grid[xmin][y].solution != '.'); xmin--)
235 ok &= (grid[xmin][y].solution == grid[xmin][y].found);
237 for(xmax = x; ok && (xmax<gridWidth) && (grid[xmax][y].solution != '.'); xmax++)
238 ok &= (grid[xmax][y].solution == grid[xmax][y].found);
242 if(ok) for(xx = xmin; xx<=xmax; xx++) grid[xx][y].lock(Color.green);
246 for(ymin = y; ok && (ymin>=0) && (grid[x][ymin].solution != '.'); ymin--)
247 ok &= (grid[x][ymin].solution == grid[x][ymin].found);
249 for(ymax = y; ok && (ymax<gridHeight) && (grid[x][ymax].solution != '.'); ymax++)
250 ok &= (grid[x][ymax].solution == grid[x][ymax].found);
254 if(ok) for(yy = ymin; yy<=ymax; yy++) grid[x][yy].lock(Color.green);
260 public synchronized void drawSquare(Graphics g, int x, int y) {
266 xe = x*squareSize+xMargin; ye = y*squareSize+yMargin;
269 if(grid[x][y].solution != '.') {
270 g.setColor(Color.white);
271 g.fillRect(xe, ye, squareSize, squareSize);
273 if(grid[x][y].holdCursor) {
274 xf = new int[5]; yf = new int[5];
275 g.setColor(Color.gray);
276 if(cursorHorizontal) {
277 xf[0] = xe + 1 + arrowMargin;
278 yf[0] = ye + 1 + arrowMargin;
279 xf[1] = xe + squareSize/2;
280 yf[1] = ye + 1 + arrowMargin;
281 xf[2] = xe + squareSize - 1 - arrowMargin;
282 yf[2] = ye + squareSize/2;
283 xf[3] = xe + squareSize/2;
284 yf[3] = ye + squareSize - arrowMargin;
285 xf[4] = xe + 1 + arrowMargin;
286 yf[4] = ye + squareSize - arrowMargin;
287 g.fillPolygon(xf, yf, 5);
291 xf[0] = xe + squareSize - arrowMargin;
292 yf[0] = ye + 1 + arrowMargin;
293 xf[1] = xe + squareSize - arrowMargin;
294 yf[1] = ye + squareSize/2;
295 xf[2] = xe + squareSize/2;
296 yf[2] = ye + squareSize - 1 - arrowMargin;
297 xf[3] = xe + 1 + arrowMargin;
298 yf[3] = ye + squareSize/2;
299 xf[4] = xe + 1 + arrowMargin;
300 yf[4] = ye + 1 + arrowMargin;
301 g.fillPolygon(xf, yf, 5);
305 g.setColor(Color.black);
306 g.drawRect(xe, ye, squareSize, squareSize);
307 tmp[0] = grid[x][y].found;
308 deltaX = (squareSize - fontMetrics.charWidth(tmp[0]))/2;
310 if(grid[x][y].locked) g.setColor(grid[x][y].color);
311 g.drawChars(tmp, 0, 1, xe + deltaX, ye+fontMetrics.getAscent());
314 g.setColor(Color.black);
315 g.fillRect(xe, ye, squareSize+1, squareSize+1);
317 grid[x][y].ajour = true;
320 public synchronized void update(Graphics g) {
322 for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
323 if(!grid[x][y].ajour) drawSquare(g, x, y);
326 public synchronized void paint(Graphics g) {
330 g.setFont(smallFont);
331 g.setColor(Color.black);
334 for(y = 0; y<gridHeight; y++) {
335 tmp[0] = (char) (y + (int) 'a');
336 g.drawChars(tmp, 0, 1,
337 xMargin - smallFontMetrics.charWidth(tmp[0]) - 3,
338 y*squareSize + yMargin + smallFontMetrics.getAscent());
341 for(x = 0; x<gridWidth; x++)
342 g.drawString("" + (x+1),
343 xMargin + x*squareSize + 3,
344 yMargin - smallFontMetrics.getDescent() - 1);
346 for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
350 public void keyTyped(KeyEvent keyEvent) {}
351 public void keyReleased(KeyEvent keyEvent) {}
353 public void keyPressed(KeyEvent keyEvent) {
355 int code = keyEvent.getKeyCode();
356 char key = keyEvent.getKeyChar();
358 if(code == KeyEvent.VK_UP) {
360 while((y > 0) && (grid[xCursor][y].solution == '.')) y--;
361 if((y >= 0) && (grid[xCursor][y].solution != '.')) {
362 grid[xCursor][yCursor].remCursor();
364 grid[xCursor][yCursor].putCursor();
367 else if(code == KeyEvent.VK_DOWN) {
369 while((y < gridHeight-1) && (grid[xCursor][y].solution == '.')) y++;
370 if((y < gridHeight) && (grid[xCursor][y].solution != '.')) {
371 grid[xCursor][yCursor].remCursor();
373 grid[xCursor][yCursor].putCursor();
376 else if(code == KeyEvent.VK_LEFT) {
378 while((x > 0) && (grid[x][yCursor].solution == '.')) x--;
379 if((x >= 0) && (grid[x][yCursor].solution != '.')) {
380 grid[xCursor][yCursor].remCursor();
382 grid[xCursor][yCursor].putCursor();
385 else if(code == KeyEvent.VK_RIGHT) {
387 while((x < gridWidth-1) && (grid[x][yCursor].solution == '.')) x++;
388 if((x < gridWidth) && (grid[x][yCursor].solution != '.')) {
389 grid[xCursor][yCursor].remCursor();
391 grid[xCursor][yCursor].putCursor();
394 else if(code == Event.ENTER) {
395 cursorHorizontal = !cursorHorizontal;
396 grid[xCursor][yCursor].ajour = false;
398 else if(code == Event.BACK_SPACE) {
399 if((grid[xCursor][yCursor].found == ' ') || grid[xCursor][yCursor].locked) {
400 if(cursorHorizontal) {
402 if(grid[xCursor - 1][yCursor].solution != '.') {
403 grid[xCursor][yCursor].remCursor();
405 grid[xCursor][yCursor].putCursor();
409 if(grid[xCursor][yCursor - 1].solution != '.') {
410 grid[xCursor][yCursor].remCursor();
412 grid[xCursor][yCursor].putCursor();
417 if(!grid[xCursor][yCursor].locked) {
418 if(grid[xCursor][yCursor].found == grid[xCursor][yCursor].solution)
420 grid[xCursor][yCursor].setFound(' ');
422 } else if(key == ' ') {
423 if(definitionArea != null) {
424 if(cursorHorizontal) definitionArea.setText(definitions[xCursor][yCursor][0]);
425 else definitionArea.setText(definitions[xCursor][yCursor][1]);
427 } else if((key >= 'a') && (key <= 'z')) {
428 if(!grid[xCursor][yCursor].locked) {
429 if(grid[xCursor][yCursor].found == grid[xCursor][yCursor].solution)
431 grid[xCursor][yCursor].setFound((char) key);
432 if(grid[xCursor][yCursor].found == grid[xCursor][yCursor].solution)
436 if(cursorHorizontal) {
437 if(xCursor < gridWidth-1)
438 if(grid[xCursor + 1][yCursor].solution != '.') {
439 grid[xCursor][yCursor].remCursor();
441 grid[xCursor][yCursor].putCursor();
444 if(yCursor < gridHeight-1)
445 if(grid[xCursor][yCursor + 1].solution != '.') {
446 grid[xCursor][yCursor].remCursor();
448 grid[xCursor][yCursor].putCursor();
452 if(nbFound == nbLetters) {
453 for(y = 0; y<gridHeight; y++) for(x = 0; x<gridWidth; x++)
454 grid[x][y].lock(Color.blue);
461 public void mousePressed(MouseEvent mouseEvent) {
463 xc = (mouseEvent.getX()-xMargin)/squareSize;
465 if(xc >= gridWidth) xc = gridWidth-1;
466 yc = (mouseEvent.getY()-yMargin)/squareSize;
468 if(yc >= gridHeight) yc = gridHeight-1;
470 if(grid[xc][yc].solution != '.') {
471 grid[xCursor][yCursor].remCursor();
472 xCursor = xc; yCursor = yc;
473 grid[xCursor][yCursor].putCursor();
479 public void mouseClicked(MouseEvent mouseEvent) {}
480 public void mouseReleased(MouseEvent e) {}
481 public void mouseEntered(MouseEvent e) {}
482 public void mouseExited(MouseEvent e) {}
484 public Dimension getPreferredSize() {
485 return new Dimension(xMargin + gridWidth*squareSize + 1,
486 yMargin + gridHeight*squareSize + 1);
491 public class Words extends Applet implements ActionListener {
493 Button checkButton, giveButton;
494 TextArea definitionArea;
497 String fontName, s, definitionString;
504 s = getParameter("bgcolor");
506 setBackground(new Color(Integer.parseInt(s)));
508 fontName = getParameter("font");
509 if(fontName == null) fontName = "Courier";
511 s = getParameter("fontsize");
512 if(s == null) fontSize = 48; else fontSize = Integer.parseInt(s);
514 f = new Font(fontName, Font.PLAIN, fontSize);
516 fm = getGraphics().getFontMetrics(f);
518 fontName = getParameter("smallfont");
519 if(fontName == null) fontName = "Courier";
521 s = getParameter("smallfontsize");
522 if(s == null) fontSize = 20; else fontSize = Integer.parseInt(s);
524 sf = new Font(fontName, Font.ITALIC, fontSize);
525 sfm = getGraphics().getFontMetrics(sf);
527 s = getParameter("gridwidth");
528 if(s == null) w = 1; else w = Integer.parseInt(s);
530 s = getParameter("gridheight");
531 if(s == null) h = 1; else h = Integer.parseInt(s);
533 definitionString = null;
535 s = getParameter("definitions");
541 // Find the width and the height
542 i = 0; j = s.indexOf('x', i);
543 wd = Integer.parseInt(s.substring(i, j));
544 i = j+1; j = s.indexOf('/', i);
545 hd = Integer.parseInt(s.substring(i, j));
547 i = j+1; definitionString = s.substring(i, s.length());
549 if((wd > 0) && (hd > 0)) {
550 definitionArea = new TextArea("", hd, wd, TextArea.SCROLLBARS_NONE);
551 definitionArea.setEditable(false);
555 s = getParameter("content"); if(s == null) s = ".";
556 grid = new Grid(f, sf, fm, sfm, w, h, s, definitionArea, definitionString);
559 s = getParameter("checkwordlabel");
561 checkButton = new Button(s);
562 checkButton.addActionListener(this);
565 s = getParameter("givewordlabel");
567 giveButton = new Button(s);
568 giveButton.addActionListener(this);
571 if((checkButton != null) || (giveButton != null)) {
573 buttonPanel = new Panel();
574 if(checkButton != null) buttonPanel.add(checkButton);
575 if(giveButton != null) buttonPanel.add(giveButton);
576 if(definitionArea != null) add("south", definitionArea);
577 add("south", buttonPanel);
581 public void actionPerformed(ActionEvent event) {
582 Object source = event.getSource();
583 if(source == checkButton) grid.checkWord();
584 else if(source == giveButton) grid.giveWord();
587 public String getAppletInfo() {
588 return "Word applet v1.5\n" +
589 "Written and (c) Francois Fleuret, mail to <francois.fleuret@inria.fr>\n" +
590 "Check http://www-rocq.inria.fr/~fleuret\n";
593 public String[][] getParameterInfo() {
595 { "bgcolor", "integer", "Background Color (optional)" },
596 { "font", "font name", "Grid character font name (optional)" },
597 { "fontsize", "integer", "Grid character font size (optional)" },
598 { "smallfont", "font name", "Row and column index font name (optional)" },
599 { "smallfontsize", "integer", "Row and column index font size (optional)" },
600 { "gridwidth", "integer", "Grid width" },
601 { "gridheight", "integer", "Grid height" },
602 { "content", "string", "Grid content" },
603 { "definitions", "string", "Definitions (optional)" },
604 { "checkwordlabel", "string", "Label for the 'check word' button (optional)" },
605 { "givewordlabel", "string", "Label for the 'give word' button (optional)" }