home *** CD-ROM | disk | FTP | other *** search
Wrap
/* File Crossword.m A crossword is a matrix of squares. The user can adjust the squares that are empty and filled using the mouse. When resized, a crossword automatically adjusts the number of squares in the frame to match. It is possible to extract the words from a crossword with the -getWords method. */ #import <appkit/appkit.h> #import "Crossword.h" #import "CrosswordSquare.h" /* ———————————————————————————————————————————————————————————————————————————— */ #define VERSION 1 #define OLDHILIGHTS 4 #define spaceWidth (squareWidth + INTERCELLWIDTH) #define cells(n) (((n) + 1) / spaceWidth) static BOOL doSquare (int inWhite, id square, id words); /* ———————————————————————————————————————————————————————————————————————————— */ @implementation Crossword - (float) getSquareWidth { return squareWidth; } - (BOOL) getFill { return fill; } - (BOOL) getNumbered { return numbered; } - getInspector { return inspector; } - setInspector: (id) theInspector { inspector = theInspector; return self; } /* ———————————————————————————————————————————————————————————————————————————— */ /* There are currently two different versions of the crossword object that need to be unarchived. */ + initialize { [Crossword setVersion: VERSION]; return self; } /* The frame will have no line along its left and top edges. A crossword matrix should be shown against an appropriate background that clarifies these edges. */ - initFrame: (NXRect *) frameRect { [self initFrame: frameRect squareWidth: DEFAULTSQUAREWIDTH]; return self; } - initFrame: (NXRect *) frameRect squareWidth: (float) squareSize { NXSize size, between; squareWidth = size.width = size.height = squareSize; between.width = between.height = INTERCELLWIDTH; numbered = NO; [super initFrame: frameRect mode: NX_TRACKMODE cellClass: [CrosswordSquare class] numRows: cells(frameRect->size.height) numCols: cells(frameRect->size.width) ]; [self setCellSize: &size]; [self setIntercell: &between]; [self setBackgroundGray: 0.0]; [self setCellBackgroundGray: -1.0]; [self changeFont: [FontManager new]]; return self; } - write: (NXTypedStream *) stream { [super write: stream]; NXWriteTypes(stream, "fc", &squareWidth, &numbered); return self; } - read: (NXTypedStream *) stream { [super read: stream]; switch (NXTypedStreamClassVersion(stream, "Crossword")) { case 1: NXReadTypes(stream, "fc", &squareWidth, &numbered); break; default: break; } return self; } /* ———————————————————————————————————————————————————————————————————————————— */ /* Here are the standard view methods that take care of user interactions. Clicking within the frame allows squares to be filled and erased. */ - superviewSizeChanged: (NXSize *) old { [super superviewSizeChanged: old]; [self renewRows: cells(frame.size.height) cols: cells(frame.size.width)]; [inspector update: self]; return self; } - changeFont: sender { [self setFont: [sender convertFont: font]]; [inspector update: self]; return self; } - print: sender { [superview printPSCode: self]; return self; } - mouseDown: (NXEvent *) event { NXPoint location; location = event->location; [self convertPoint: &location fromView: nil]; fill = ![[self cellAt: location.y / spaceWidth : location.x / spaceWidth] isOpaque]; [super mouseDown: event]; if (numbered) [self unnumber]; else [inspector update: self]; return self; } - (BOOL) acceptsFirstMouse { return NO; } /* ———————————————————————————————————————————————————————————————————————————— */ - clear: (squareColor) color; { CrosswordSquare * square; int i; i = [cellList count]; while (i--) { square = [cellList objectAt: i]; [[square setColor: color] setLetter: EMPTY]; } [self display]; return self; } - putLetter: (char) letter inSquare: (id) square { [square setLetter: letter]; [self drawCellInside: square]; return self; } - number { int i, j, n; id square; for (n = 1, i = 0; i < numRows; i++) for (j = 0; j < numCols; j++) if ( [square = [self cellAt: i: j] isOpaque] && ( ![[self cellAt: (i - 1): j] isOpaque] || ![[self cellAt: i: (j - 1)] isOpaque] ) ) [square setNumber: n++]; numbered = YES; [self display]; [inspector update: self]; return self; } - unnumber { int i, j; for (i = 0; i < numRows; i++) for (j = 0; j < numCols; j++) [[self cellAt: i: j] setNumber: EMPTY]; numbered = NO; [self display]; [inspector update: self]; return self; } /* ———————————————————————————————————————————————————————————————————————————— */ - getWords { int i, j; BOOL inWhite; id words; words = [[List alloc] init]; for (i = 0; i < numRows; i++) for (inWhite = NO, j = 0; j < numCols; j++) inWhite = doSquare(inWhite, [self cellAt: i: j], words); for (j = 0; j < numCols; j++) for (inWhite = NO, i = 0; i < numRows; i++) inWhite = doSquare(inWhite, [self cellAt: i: j], words); return words; } @end /* ———————————————————————————————————————————————————————————————————————————— */ /* The routine below processes an individual square when extracting words. It exists as a separate function to avoid duplication in the across and down directions. */ static BOOL doSquare (inWhite, square, words) BOOL inWhite; id square; id words; { BOOL opaque = [square isOpaque]; if (!inWhite && opaque) [words addObject: [[List alloc] init]]; if (opaque) [[words lastObject] addObject: square]; return opaque; }