TweetFollow Us on Twitter

Beginning OOPs
Volume Number:7
Issue Number:8
Column Tag:Beginning OOPs

Object Programming With TCL

By John Lengyel, St. Louis, MO

[John Lengyel is a software engineer for Digital Systems Consultants of St. Louis, Missouri. He is currently developing applications on VAX/VMS and Apollo/Unix platforms. He is looking for Macintosh software development opportunities.]

The Objective

Over the past several years numerous articles have appeared in MacTutor espousing the virtues of MacApp. Not being a Macintosh programmer by profession and already owning and enjoying THINK Pascal and THINK C, I managed to suppress my desire to purchase MacApp. So when my THINK C 4.0 upgrade arrived I wanted to immediately dig into its object-oriented capabilities.

After reading the well written manual discussing object-oriented programming and the THINK Class Library (THINK C’s version of MacApp) and looking over the sample projects provided with the upgrade I thought it would be a good idea to do one or two MacApp to THINK Class Library (TCL) conversions before jumping off on my own. The first program I converted is the topic of this article. The program is based upon a similar program that was written by Larry Rosenstein and presented in the December, 1986 issue of MacTutor titled “Dots Game Shows MacApp”.

TCL and MacApp

At this point, I considered writing the “What is TCL” section of the article. But anyone who has read or can read past articles on MacApp will have a very good idea of what TCL is all about. While converting Dots to TCL, the similarity of TCL to MacApp became increasingly noticeable.

For example, in the MacApp version of Dots, the classes requiring redefinition are TApplication, TDocument, TView and TCommand. In the TCL version they are respectively CApplication, CDocument, CView (actually subclasses of CView are redefined) and CMouseTask (similar to MacApp’s TCommand). The functionality provided by these corresponding classes is nearly identical. This similarity in name and function also extends, to a slightly lesser degree, down into the method level.

Studying MacApp articles and code should be helpful to anyone trying to learn how to write programs in TCL. As an additional TCL specific reference, an overview of TCL was provided by Alastair Dallas in the October, 1989 issue of MacTutor. Rather than restating what these people have said about MacApp and TCL I will concentrate my efforts on the classes that needed to be redefined for Dots and in particular on their overridden methods.

About Dots...

For those of you who while in high school actually studied during study hall and do not know how to play Dots, here is a description of the game. Dots is played on a matrix of dots. Players take turns drawing lines connecting adjacent dots. When a player draws a line that completes a square in the matrix, that player is awarded the square. A mark is placed in the square to indicate player ownership. The player completing the square may then take another turn. The game ends when all lines in the matrix are drawn. The player awarded the most squares wins the game.

In the TCL version of Dots, which has essentially the same design as its MacApp counterpart, the game is played in a window. The number of squares awarded each player is displayed in the top portion of the window. These numbers are hereafter referred to as the player’s scores. The dot matrix over which the game is played is displayed in the bottom portion of the window and is scrollable. Lines between adjacent dots are drawn with mouse clicks. When a square is completed, a graphic pattern associated with the moving player is used to fill an awarded square to indicate that player’s ownership of the square. The pattern is also used to display a frame around the appropriate player’s score. See Figure 1 for a view of the window used in Dots. As for as the playing of Dots, a correction to the original version was made so that when a player completes a square it is still that player’s turn.

Figure 1. Views of Dots' Document

Many useful features are exhibited in the Dots program. A multi-view window that can be scrolled, auto-scrolled, resized, zoomed and printed. Support for the undoing and redoing of moves. Multifinder compatibility and desk accessory support. Games can be saved and restored. Multiple windows can be opened and hence games played. All this and more in a fairly small amount of new, Dot specific code thanks to TCL.

Table 1 shows the five TCL classes that were redefined for Dots. These five subclasses and the “main” routine comprise all of the new code that was written for Dots. All other functionality is provided by the standard TCL code. The rest of this article will discuss each of the redefined classes and “main”.

TCL Subclass Abridged Description

CApplication cDotsApp Supervises program

CDocument cDotsDoc Manages window and its data

CPane cScorePane Displays scores

CPanorama cDotsPane Displays dot matrix

CMouseTask cDotsTask Handles mouse clicks in matrix

Table 1: TCL Redefines

Source Descriptions

The source files for Dots are organized in the manner recommended in the THINK C manual. Dots subclasses are defined in a “.h” header file and their methods in a “.c” file each having the name of the class. As an example, the subclass cDotsApp’s associated source files are cDotsApp.c and cDotsApp.h. I prefixed the Dots subclasses with a lower case “c” in order to distinguish them from the standard TCL classes which all start with an upper case “C”. There does not appear to be a standard for prefixing the names of class methods or variables in TCL. For class variables I followed the MacApp practice of preceding the variable name with a lower case “f”. As for class methods, standard TCL methods start with an upper case letter while Dot specific methods start in lower case.

The resources used in Dots were copied from the Starter Project supplied by THINK. A few modifications and additions were then made. A new string was added to STR# “Common” to supply text for displaying the “About...” alert. The “General” ALRT and DITL which are used to display the “About...” were then enlarged to accommodate the text. A DITL and DLOG were added to display a “Press CMD-. to stop printing” message. A WIND resource was modified for the document window. Finally, the string “Move” was added to STR# “Task Names”. This string is used by TCL to display the proper undo/redo command in the edit menu (“Undo Move” or “Redo Move”).


The main routine when using TCL is rather trivial. It creates an application object and sends it an initialization message. Main then tells the application to start running. Finally, when the application finishes running, Main sends an exit message to the application. Exit() gives the application a last chance to clean up before the application terminates. For instance, temporary files may need to be deleted. Dots does not do anything in the Exit() routine. It was left in for illustrative purposes. All very neat, very orderly. Enough said.


Only one application is created for the entire project. As previously stated, the Main routine of the project creates the application object, sends the application an initialization request and then tells the application to begin running. While running, the application directs the processing of events, gives MultiFinder support, takes some wrinkles out of memory management, updates menus, handles DAs and is also capable of handling some of the basic File and Edit menu commands. All of this runtime support is provided in Dots by the standard TCL code.

To understand program flow using TCL it is important to know about the chain of command. A chain of command is used to determine which object handles a direct command. A direct command is a request that an object perform some action. They usually come about from menu selections. Direct commands that cannot be handled by an object move up the chain of command. The application is at the top of the chain of command. By the time the application receives the command it has been passed on by the views and the document. If the application does not handle the command, the command will not be handled. A chain of command for Dots is depicted in Figure 2.

Figure 2. The Chain of Command in Dots

The application’s inherited initialization method must be overridden. The Dots override for this method calls the inherited application’s initialization method. This must always be done because the inherited method initializes the Macintosh Toolbox, sets up menus, initializes application specific TCL globals and does a host of other very helpful but tedious things. The Dots application initialization method also sets up two global variables each of which are the patterns associated with a particular player. The use of these patterns is discussed in the cDotsPane and cScorePane sections of this article. These are application globals because they apply to all games being played and are not game dependent.

The application method SetUpFileParameters() was overridden in order to identify the types of files the Dots application recognizes. These types are used for the Macintosh Toolbox Standard File routines when opening a file. The application method DoCommand() was overridden so that a simple “About” alert could be posted when the “About Dots...” command was selected from the Apple menu. Unlike MacApp, TCL does not support an inherent about box. The application methods CreateDocument() and OpenDocument() were overridden so that Dots documents could be created whenever a “New...” or “Open...” was chosen from the File menu.


The document associates a window with the data that is to be displayed in the window. The data contained in the document is displayed to the user in the window and may be stored on disk as a file. A subclass of CDocument or its superclass CDirector must be created for every type of window the application supports (CDirector does not have methods for filing or printing). A document object is created whenever “New” or “Open...” is chosen from the File menu.

Documents are in the chain of command. They manage the communication between windows, files and menu commands. If a document cannot handle a command, it passes it up to the application. Methods in the document class are provided to create, initialize and dispose of documents, open and close windows, read and write data to disk, access the document data, support printing, support the undoing and redoing of tasks and update menus.

cDotsDoc is the only document subclass in Dots. Each cDotsDoc object that is created associates the window and data for a particular game. The data needed to store the state of a Dots game must contain the following information: whether or not a line is to be drawn between each adjacent dot in the matrix, the ownership of each completed square, the player who is to move next.

The inherited document’s initialization method must be overridden. The Dots override for this method calls the inherited initialization method in order to put the document in the chain of command and initialize some document variables for filing, printing, recording moves, and menu updating. The Dots data is then initialized to a new game state.

The inherited document methods for creating a new file or opening an existing file must be overridden because they do not do anything. The code for these routines comes out of the sample programs provided with THINK C. Adaptations were made for Dots. The main adaptation involves storing the data read in from an opened file into the document. This is accomplished in the auxiliary method storeData() (Auxiliary means it is called by other methods within the object in which it is located but not by methods of other objects. THINK C 4.0 does not support private methods).

Both the new file and open file methods call an auxiliary method, BuildWindow(), to create a window and the views contained in the window. There are actually four views in the window. A subclass of CPane is used to display the score and current player turn. A subclass of CPanorama displays the view of the dot matrix. These subclasses, cScorePane and cDotsPane, are described in their respective sections. The two other views in the window are objects of CScrollPane and CSizeBox.

The dot matrix view must be scrollable which is why it is a subclass of CPanorama. CPanorama has methods that support scrolling. CScrollPane implements a view with scroll bars to control a panorama. Both CScrollPane and CPanorama are descendants of CPane which is itself a descendent of CView. They work in tandem to provide the scrolling capabilities of Dots.

CSizeBox, also a descendant of CPane, is used by CScrollPane to draw a grow icon in the lower right corner of any pane. By using an icon for the size box, TCL gains some flexibility in its placement of the size box. The size box no longer has to appear in the lower right corner of the window. However, in the case of Dots, that’s where it’s at man.

The code for BuildWindow() is also adapted for Dots from the sample programs. However, I spent a great deal of time reworking the BuildWindow() code for Dots. This was in large part caused by my lack of knowledge as to how some of the standard TCL code worked.

CDecorator is a TCL class that is used to arrange windows on the screen. One of its methods, PlaceNewWindow(), is used by BuildWindow() to implement window staggering. Some of the sample programs have BuildWindow() calling PlaceNewWindow() immediately after the window is created. I followed their example and got some bad results. The scroll bars for the dot matrix view were being placed beneath the window. It turned out that PlaceNewWindow() was shortening the window. Then when the scroll bars were created, they were placed according to the original size of the window. An easy solution was to make the call to PlaceNewWindow() at the end of BuildWindow() after all the views and scroll bars were in place.

Another problem I had involved the use of the inherited document variable itsMainPane. itsMainPane is a variable of type CPane. As stated in the THINK C manual, itsMainPane should point to the document’s main pane and if the document has no main pane, itsMainPane should be NULL. Many of the document’s print routines check to see if itsMainPane is NULL. If it is, they bypass their print code.

Since the Dots window is broken up into a score view and a dots matrix view it did not have what I considered to be a main view. But I wanted to be able to print and so did not want to leave itsMainPane NULL. My initial solution was to use itsMainPane as a view of the entire window and attach all of the other views directly or indirectly to it. This worked fine until I tried to print. All that printed was a page eject. After much digging, I found the problem.

In preparation for printing, the printer object was setting the current port to the printer. Then when the draw message went out to itsMainPane, which had no functionality of its own, it passed the message on to all of its attached views. CScrollPane was the first view that attempted to print. However, the scroll pane has scroll bars attached to it and scroll bars are not supposed to be printed. When the scroll bars received their draw message they set the current port to the window and left it there. Because of this, all of the other views poured themselves onto the screen as well.

After several other arrangements of views in BuildWindow() failed for various reasons I finally decided to just override all of the document’s print routines that used itsMainPane (there were only a few). In the override print routines, two variables were used to replace itsMainPane. One pointed to the score view object and the other to the dot matrix view object. Problem solved.

While I am on the topic of printing I may as well discuss a couple of problems that occurred when attempting to print from the Finder. Both problems were caused by errors in the standard TCL code. In correcting the problems I did not want to modify the TCL source code files in which the errors occurred. This should only be done by THINK. So the simplest solution, which occurred at 3 am after four hours of debugging, was to embed the corrections in a couple of document methods.

The first printing problem involved the use of the global variable gGopher. gGopher points to the first object in the chain of command that gets a chance to handle a command. If the gopher cannot handle the command it sends the command up the chain of command on to its supervisor. The document stores in one of its instance variables a pointer to a view that it encloses. That view is to be used as the gopher when the document is activated. When no document is active the application is the gopher.

When printing a document from the Finder, the application method Preload() sends the document an open file message and then tells the gopher to print. But when I tried to do this in Dots, the documents opened but did not attempt to print. The problem was that gGopher was pointing to the application object at this point rather than the document’s gopher as it should have been. The document’s OpenFile() method had sent a Select() message to its window in order to activate the window. However, Select() only sets gGopher to the document’s gopher if the application was in the background. Since Dots was not in the background gGopher remained pointing at the application. My solution was to force an activate after Select() was called from the document’s OpenFile() method. This sets gGopher to the document’s gopher which when told by Preload() to print passes the print message on to the document where it is finally handled.

The second printing problem was aesthetic in nature. While a document is printing the cursor is set to the watch. When the printing is finished the cursor gets set back to an arrow. Using TCL this arrow reset occurs during the main event loop when DispatchCursor() is called. However, when printing multiple documents from the Finder control does not return to the main event loop between print jobs. Succeeding print job dialogs appear with the watch cursor still in use. For a fix I set the cursor back to an arrow during the document’s DonePrinting() method.

The inherited document methods for handling “Save”, “Save As...” and “Revert” commands do nothing and must be overridden. Again, the code for these routines was easily adapted for Dots from the sample programs. No problem.

Finally, document methods are provided for accessing the Dots-specific instance variables for line states, square states and player turn. These are the only new methods that needed to be developed for the document. Admittedly, there were quite a few code changes that had to be made in the override methods. But the bulk of those changes involved that wonderful programming technique called “cut and paste”. Minor modifications were then made to the “pasted” code.


As stated in the TCL manual, a pane is a view that defines a drawing area within a window or another pane. All drawing takes place within a pane. Each pane has its own drawing environment and coordinate system. Panes are used for the drawing of non-scrolling views. Panes may also handle mouse clicks.

To understand how objects are drawn using TCL it is important to know about the visual hierarchy. The visual hierarchy is composed of all the visual objects in the application. At the top of the visual hierarchy is the desktop. Except for the desktop, all of the visual objects appearing on the screen are enclosed by other visual objects. The desktop encloses all of the windows. A window encloses all of its views. Views may enclose other views.

Figure 3 shows the visual hierarchy for a Dots application that currently has two games in progress. Note that the visual hierarchy changes as the application runs. Whenever a new game is started a document is created. The document’s window is added to the desktop and the views are added to the window. When a game is closed the window and its enclosed views are removed from the visual hierarchy.

Figure 3. Visual Hierarchy

When a window update or activate event occurs the message moves down the visual hierarchy from the desktop to the appropriate window. Each view attached to the window is passed a draw message. If any part of the view is contained in the area to be redrawn the view will call its own draw routine and then pass the draw message on to each of its subviews. The subviews will continue this process until the bottom of the visual hierarchy is reached.

Panes have draw methods, print methods, mouse methods, cursor methods, methods for changing their appearance, coordinate transformation methods and more. Because panes are views, they are part of the chain of command as well as the visual hierarchy. A pane’s supervisor in the chain of command is usually its director or document.

In Dots, the score view is a subclass of CPane. One score view object is created by the document’s BuildWindow() method whenever a document is created. The score view displays each player’s score in the document’s window and highlights the score of the player who is currently moving. The currently highlighted score is tracked by an instance variable contained in the score view object. The score view does not handle mouse clicks.

The inherited score view’s initialization method must be overridden. The Dots override for this method, IScorePane(), calls the inherited initialization method in order to put the score view in the chain of command and in the visual hierarchy and to initialize inherited instance variables. IScorePane() initializes its instance variable for the currently highlighted score to that of the player who is currently moving.

The only other inherited method to be overridden is the draw method. The code for drawing the line between the score view and the dot matrix view did not appear in the source listings of the MacApp version of Dots. My assumption is that MacApp was somehow tasked to draw the line. TCL has a similar capability through the use of a subclass of CPane called CBorder. Objects of CBorder are panes with the inherent capability of drawing a border around their perimeter. Rather than use CBorder for the score view’s superclass I chose to draw the line myself in the draw method. This is the only difference between the MacApp and TCL versions of Draw();

Four new methods were needed in the score view (they appeared in the MacApp version but are new in the sense that they are not inherent CPane class methods). The new methods are used to calculate the current score, obtain the rectangle around a player’s score, invalidate the area around a player’s score so that it will be redrawn with a window update event and switch the highlighted score when a player moves.

A final note about drawing. All drawing in a pane takes place in the pane’s own coordinate system. This simplifies the drawing of a pane since the pane may now be repositioned or modified possibly in other ways without affecting the drawing code. Panes become independent entities. During an update or activate event, before the pane’s draw routine is called, another inherent pane class method is called by the standard TCL code to prepare the pane for drawing. This prepare method sets up the port and coordinates for the pane.

Some of the new methods for the score view may be called directly from other objects. When this happens the preparation method for the score view has not been called because an update or activate is not involved. This means that the current port and coordinate system are quite possibly not that of the score view. This means that erroneous drawing is quite possible. This means I was having a drawing problem until I started putting calls to prepare the score view for drawing before the calls for invalidating and erasing rectangles in the score view were made.


A panorama is a pane with the added capability of scrolling. CPanorama is a subclass of a CPane. Panoramas inherit all of the qualities of a pane that were described in the section discussing the score view.

A panorama must be installed in a scroll pane to make scrolling possible. A scroll pane is a pane with a panorama and scroll bars to control what is being displayed in the pane. The scroll bars and panorama communicate through the scroll pane to scroll or shift an image. These objects, if they are to be used, should be created when the document is created. Attach the scroll pane to the window and the panorama to the scroll pane. The scroll bars are created and attached to the scroll pane by the inherent TCL code during the scroll pane’s initialization process.

The dot matrix view in Dots is a subclass of CPanorama. One dot matrix object and its scroll pane are created by the document’s BuildWindow() method whenever a document is created. The dot matrix view displays the dot matrix, the lines connecting adjacent dots and the awarded squares. Mouse clicks are handled by the dot matrix view so that players may select the lines connecting the dots.

The inherited dot matrix view’s initialization method must be overridden. The Dots override for this method, IDotsPane(), calls the inherited initialization method in order to put the score view in the chain of command and in the visual hierarchy and to initialize inherited instance variables. IDotsPane() sets the scrolling scale to the pixel distance between the dots in the matrix as opposed to the default distance of one dot. When a click is made in one of the scroll bars, the image will now scroll by the dot spacing amount rather than one pixel at a time.

The inherited draw method must be overridden. The override method is the same as that appearing in the MacApp version of Dots. In fact, all of the new methods for the dot matrix view are essentially the same as in the MacApp version. The only modification is that some of the routines required that the drawing preparation method be used to set the port and coordinate system to that of the dot matrix view. The reasons for this were discussed in the score view section of this article.

Mouse tracking is handled a little differently in TCL than MacApp. The inherited method DoClick() must be overridden to enable mouse tracking. In Dots, the DoClick() method creates a mouse task to handle the mouse down. The inherited method TrackMouse() is called to follow the mouse down until the mouse button is released. DoClick() must notify the document of the move so that the previous move may be disposed of and the current move saved for the undo process.

The TrackMouse() method called by DoClick() sends the newly created mouse task a BeginTracking() message to give the mouse task an opportunity to make any adjustments before mouse tracking starts. TrackMouse() then repeatedly calls the mouse task method KeepTracking() while the mouse button remains down. When the button is finally released, TrackMouse() sends an EndTracking() message to the mouse task. It is up to the mouse task to initiate any action based upon the mouse click.


Mouse tasks are used to track mouse downs within a pane. A mouse task is a subclass of a task. Mouse tasks therefore inherit the task’s methods for implementing undoable actions.

Dots uses a mouse task to track mouse downs in the dot matrix view and to provide for the undoing and redoing of moves. A mouse task is created by the dot matrix view’s DoClick() method whenever there is a mouse down in the dot matrix view. The Dot’s process for handling the mouse down is discussed in the section on the dot matrix view.

The mouse task’s initialization method is a good place to initialize the instance variables that will be saved in the document for undoing the mouse click. The inherent initialization method should be called to store the undo/redo string in the edit menu.

The inherent mouse tracking methods BeginTracking(), KeepTracking() and EndTracking() do not do anything. BeginTracking() should be overridden if the starting point for the mouse down needs to be altered or some initial actions need to be performed for the mouse down. KeepTracking() and EndTracking() must always be overridden. EndTracking() should store the information needed to undo the mouse task in the instance variables of the mouse task.

Dots overrides all three of these mouse tracking methods in order to provide autoscrolling, line selection, the highlighting and unhighlighting of selectable lines as the cursor moves across the dot matrix view and the storage of the move for undoing the mouse task. The TCL code for handling mouse clicks in Dots varies considerably from the code in the MacApp version. However, the functionality is the same and the steps that must be taken to achieve that functionality are fairly easy to follow.

The methods for doing, undoing and redoing a mouse task in Dots are all overrides that mimic their MacApp counterparts. The only new method in the mouse task is used by the document to check on the validity of the move. If the just completed mouse task performed a valid move then the mouse task is saved in the document for undo. Invalid moves are disposed of.


Overall, working with TCL was an enjoyable experience. The inherited code for menu updating, scrolling, Multifinder and DA support, window manipulation and memory management is quite extensive and greatly simplifies the programming process. Good job THINK.

As pointed out in the article, there were a few TCL glitches that caused me hours of consternation. These problems will clear up over time. But one thing I noticed as I tried to track down the errors was that TCL programs and probably object-oriented programs in general can be hard to debug.

Sometimes it is difficult to tell, by looking at the source code, what object is being passed a message and hence where a problem may be occurring. For example, the TCL global variable gGopher is a handle to an object that is a subclass of CBureaucrat. These objects may be applications, documents, views or instances of other classes that inherit bureaucratic traits. By looking at a line like ‘gGopher->DoCommand(theCommand)’ it is impossible to tell which type of bureaucrat is being referenced. The code must be stepped through at runtime to identify the bureaucrat. Program flow can also be a bit confusing to follow because it jumps around a lot from method to method and object to object (at least it was confusing to me).

To help overcome these difficulties the TCL code is well commented and the manual well written. The THINK C runtime debugger is an excellent tool for stepping through code and tracking down errors. It is also a good tool with which to learn exactly how TCL operates.

Types and constants used throughout Dots
#include <Constants.h>

 /* Include this file only once*/
#define _H_dotsTypes
 /* Rows and columns are 0 thru n. Size of dot
 ** matrix should be even to avoid ties
 ** (n+1 should be even) */
#define kMaxCol  5
#define kMaxRow  5
 /* Player square IDs */
#define  kBoxPlayer1 4
#define kBoxPlayer25
 /* Player IDs */
#define kPlayer1 0
#define kPlayer2 1
 /* Number of sides completed. 4 means completed
 ** by player 1, 5 means completed by player 2 */
typedef short  tBoxState;
 /* Line State - true if line drawn */
typedef Boolean  tLineState;
 /* Line directions (horiz, vert) */
typedef enum {hLine, vLine} tLineDir;

/****** myGlobals.c
Declarations of Global Variables for Dots ******************************************/

 /* The patterns of the 2 players */
Pattern dgPlayerPats[2];

The main file for Dots.
Uses the THINK Class Library

#include “cDotsApp.h”

extern  CApplication *gApplication;

void main()
 /* Create Dots application */
 gApplication = new(cDotsApp);
 /* Init Dots application */
 ((cDotsApp *)gApplication)->IDotsApp();     

Application class for Dots game

#define _H_cDotsApp
#include <CApplication.h>

struct cDotsApp : CApplication
 /* Initialization Methods */
 void IDotsApp(void);
 /* Override Methods */ 
 void SetUpFileParameters(void);
 void DoCommand(long theCommand);
 void CreateDocument(void);
 void OpenDocument(SFReply *macSFReply);

SUPERCLASS = CApplication
Dots application methods

#include <CError.h>
#include <Commands.h>
#include <Constants.h>
#include “cDotsApp.h”
#include “cDotsDoc.h”
#include “dotsTypes.h”

 /*** Class Constants ***/
 /* About... string index in STRcommon */
#define I_ABOUT  8
 /*** TCL Defined Globals ***/
extern  CError *gError;
extern  OSType gSignature;
 /*** Dots Defined Globals ***/
extern  Pattern  *dgPlayerPats;

/***** I N I T I A L I Z A T I O N *****/

/*** IDotsApp
Initialize the application. At least call the inherited method. Initialize 
application instance variables or globals.
void cDotsApp::IDotsApp(void)
 /*Set moreMasters, rainyDayFund, creditLimit*/
 CApplication::IApplication(4, 20480L, 2048L);
 /* Set patterns of each player */
 BlockMove(ltGray, &dgPlayerPats[kPlayer1],
 BlockMove(dkGray, &dgPlayerPats[kPlayer2],

/*** SetUpFileParameters {OVERRIDE}
Specify the kinds of files the application opens
void cDotsApp::SetUpFileParameters(void)
 gSignature = ‘dots’;/* File creator */
 sfNumTypes = 1;
 sfFileTypes[0] = ‘DATA’;

/********** C O M M A N D ********/

/*** DoCommand {OVERRIDE}
Last chance to respond to menu selection. Call the default method for 
standard commands.
void cDotsApp::DoCommand(long theCommand)
 switch (theCommand) {
 case cmdAbout:
 /* Show the ‘about’ alert */
 gError->PostAlert(STRcommon, I_ABOUT);

/********** D O C U M E N T ********/

/*** CreateDocument{OVERRIDE}
New was chosen from the File menu. Create a document and send it a NewFile() 
void cDotsApp::CreateDocument()
 cDotsDoc *theDocument;
 theDocument = new(cDotsDoc);
 theDocument->IDotsDoc(this, TRUE);

/*** OpenDocument{OVERRIDE}
Open  was chosen from the File menu. Create a document and send it an 
OpenFile() message. The macSFReply is a good SFReply record that contains 
name and vRefNum of file user chose to open.
void cDotsApp::OpenDocument(SFReply *macSFReply)
 cDotsDoc *theDocument;
 theDocument = new(cDotsDoc);
 theDocument->IDotsDoc(this, TRUE);

Document class for Dots game

#define _H_cDotsDoc
#include <CDocument.h>

#include “cDotsPane.h”
#include “cScorePane.h”
#include “dotsTypes.h”

 /*** Class Types ***/
 /* Types used to store state of game */
typedef tLineState
typedef tBoxState tBoxes[kMaxRow+1][kMaxCol+1];

struct cDotsDoc : CDocument
 /* Instance Variables */
 cDotsPane*fDotsPane;/* Matrix view */
 cScorePane *fScorePane;  /* Score view */
 BooleanfPlayerTurn; /* Whose turn */
 tLines fLines;  /* State of every line */
 tBoxes fBoxes;  /* State of every box */
 DialogPtrprintBanner;  /* Print box */

 /* Initialization Methods */
 void   IDotsDoc(CBureaucrat *aSupervisor,
 Boolean printable);

 /* Override Methods */
 void   NewFile(void);
 void   OpenFile(SFReply *macSFReply);
 BooleanDoSaveAs(SFReply *macSFReply);
 void   DoRevert(void);
 short  PageCount(void);
 void   AboutToPrint(short *firstPage,
 short *lastPage);
 void   PrintPageOfDoc(short pageNum);
 void   DonePrinting(void);
 /* Auxiliary Methods */
 void BuildWindow(Handle theData);

 /* New Methods */
 void   storeData(Handle theData);
 tBoxStategetBoxState(int row, int col);
 BooleangetLineState(tLineDir direction,
 int row, int col);
 void setLineState(tLineDir direction, int row,
 int col, Boolean thePlayer, Boolean newState);
 BooleanchangeBoxState(int row, int col,
 Boolean thePlayer, Boolean addingLine);

Methods for the Dots Document. The document has one window. The window 
containts a scrollable dots pane where the matrix is displayed and a 
non-scrolling score pane which displays the score and current player 

#include <CApplication.h>
#include <CBartender.h>
#include <CDataFile.h>
#include <CDecorator.h>
#include <CDesktop.h>
#include <CError.h>
#include <Commands.h>
#include <Constants.h>
#include <CPanorama.h>
#include <CPrinter.h>
#include <CScrollPane.h>
#include <TBUtilities.h>

#include “cDotsDoc.h”
#include “cDotsPane.h”
#include “cScorePane.h”
#include “dotsTypes.h”

 /*** Class Constants ***/
#define DLOG_PRINT 2000 /* Print dialog*/
#define kBoxFree 0 /* Initial box state */
#define kDotsTop   44
#define kScoreBottom (kDotsTop)
#define strABORTPRINT9
#define WINDDots 128

 /*** TCL Defined Globals ***/
extern  CApplication *gApplication;
extern  CBartender *gBartender;
extern  CDecorator *gDecorator;
extern  CDesktop *gDesktop;
extern  OSType   gSignature;
extern  CError   *gError;

/***** C O N S T R U C T I O N *****/

/*** IDotsDoc
The document’s initialization method. Initialize documents instance variables. 
Always invoke the default method.
void cDotsDoc::IDotsDoc(
 Boolean printable)
 int    row;
 int    col;
 /* Init doc and make printable */
 CDocument::IDocument(aSupervisor, printable);

 fPlayerTurn = kPlayer1;  /* Player 1 first */

 /* Clear matrix and boxes */
 for (row = 0; row <= kMaxRow; row++)
 for (col = 0; col <= kMaxCol; col++)
 fLines[hLine][row][col] = false;
 fLines[vLine][row][col] = false;
 fBoxes[row][col] = kBoxFree;


/********** F I L E **********/

/*** NewFile{OVERRIDE}
When the user chooses New from the File menu, the CreateDocument() method 
in the Application class will send a newly created document this message. 
This method needs to create a new window, ready to work on a new document.
void cDotsDoc::NewFile()
 Str255 wTitle;  /* Window title */
 short  wCount;  /* Index of new window*/
 Str63  wNumber; /* Index as a string */

 /* Create empty window */
 /* Append index to window name */
 wCount = gDecorator->GetWCount();
 NumToString((long)wCount, wNumber);
 ConcatPStrings (wTitle, (StringPtr) “\p-”);
 ConcatPStrings(wTitle, wNumber);
 itsWindow->Select();/* active window */

/*** OpenFile  {OVERRIDE}
When Open  is chosen from the File menu, the OpenDocument() method in 
the Application class will let the user choose a file and then send a 
newly created document this message. The information about the file is 
in the SFReply record. In this method, open the file and display it in 
a window.
void cDotsDoc::OpenFile(SFReply *macSFReply)
 Handle theData;
 Str63  theName;
 OSErr  theError;
 /* Create a file and send it an SFSpecify()
 **   message to set up the name, volume, and
 ** directory */
 theFile = new(CDataFile);
 itsFile = theFile;

 theError = theFile->Open(fsRdWrPerm);

 /* If open error, CheckOSError reports
 ** error in alert and returns false */
 if (!gError->CheckOSError(theError)) {
 Dispose(); /* Don’t need doc */

 /* Read in the data - don’t use rainy
 ** day fund, its ok to fail */
 gApplication->RequestMemory(FALSE, TRUE);
 gApplication->RequestMemory(FALSE, FALSE);

 /* If not enough memory to open,
 ** post error and dispose doc */
 if (theData == NULL) {
 /* Create empty window */

 /* Store data as instance variables in
 ** document class */

 /* Put file name into window title */
 itsWindow->Select(); /* Activate window*/
 /* Select only activates if application
 ** was in background. This may cause
 ** problems (see cDotsDoc section of
 ** article on finder print problem). So
 ** let’s force activate here. */

/*** BuildWindow
Create window  */
void cDotsDoc::BuildWindow (Handle theData)
 Rect   r;
 /* Create window */
 itsWindow = new(CWindow);
 itsWindow->IWindow(WINDDots, FALSE,
 gDesktop, this);
 /* Set windows max and min sizes */
 r.right - r.left, r.bottom -;

 /* Create scroll pane in the window */
 theScrollPane = new(CScrollPane);
 this, 0, 0, 0, 0, sizELASTIC,
 theScrollPane->FitToEnclFrame(TRUE, TRUE);
 /* Leave space at top of window for
 ** score pane, it does not scroll */
 SetRect(&r, 0, kDotsTop, 0, 0);
 theScrollPane->ChangeSize(&r, FALSE);
 /* Create dots pane in scroll pane */
 fDotsPane = new(cDotsPane);
 fDotsPane->IDotsPane(theScrollPane, this,
 0, 0, 0, 0, sizELASTIC, sizELASTIC);
 /* Fit dots pane to interior of scroll
 **pane Interior excludes scroll bars */
 fDotsPane->FitToEnclosure(TRUE, TRUE);
 /* Bounds were initialized to 0’s so
 ** must set bounds to new frame size */
 fDotsPane->GetFrameSpan(&r.right, &r.bottom);
 SetRect(&r, 0, 0, r.right, r.bottom);
 /* Put dots pane in panorama */
 itsGopher = fDotsPane;
 /* Create score pane in the window */
 fScorePane = new(cScorePane);
 fScorePane->IScorePane(itsWindow, this,
 0, 0, 0, 0, sizELASTIC, sizFIXEDTOP);
 fScorePane->FitToEnclFrame(TRUE, TRUE);
 /*  Position above dots pane */
 SetRect(&r, 0, 0, 0, kScoreBottom - r.bottom);
 fScorePane->ChangeSize(&r, FALSE);
 /* Allow for staggered window placement (NOTE:          ** If this routine 
is to be used at all, it
 ** must come after subviews are placed in
 ** window or strange things happen) */

/*** DoSave {OVERRIDE}
Save doc data to disk. ‘Save’ chosen from menu. */
Boolean cDotsDoc::DoSave()
 OSErr  errCode;
 long   theLength;
 long   totalLength;
 int    x;
 if (itsFile == NULL)
 /* Go to beginning of file */
 errCode = ((CDataFile *)itsFile)->
 SetMark(0, fsFromStart);
 if (errCode != noErr)
 /* Write player turn */
 if (fPlayerTurn == kPlayer1)
 x = 1;
 x = 2;
 errCode = ((CDataFile*)itsFile)->
 WriteSome((Ptr)&x, 2L);
 if (errCode != noErr)
 totalLength = 2;
 /* Write line states */
 theLength = sizeof(tLines);
 errCode = ((CDataFile*)itsFile)->
 WriteSome((Ptr)fLines, theLength);
 if (errCode != noErr)
 totalLength += theLength;
 /* Write box states */
 theLength = (long)sizeof(tBoxes);
 errCode = ((CDataFile*)itsFile)->
 WriteSome((Ptr)fBoxes, theLength);
 if (errCode != noErr)
 totalLength += theLength;
 /* Set file length in case previous
 ** contents was bigger. Can’t happen
 ** in Dots but left in for example*/
 errCode = ((CDataFile *)itsFile)->
 if (errCode == noErr)    /* Force write*/
 errCode = FlushVol(“”, itsFile->volNum);
 if (errCode != noErr)  

 dirty = FALSE;  /* Document now clean */

/*** DoSaveAs {OVERRIDE}
Save As  chosen from File menu. The default DoCommand() method for documents 
sends a DoSaveFileAs() message which displays a standard put file dialog 
and sends this message. The SFReply record contains all the information 
about the file to create.
Boolean cDotsDoc::DoSaveAs(SFReply *macSFReply)
 if (itsFile != NULL)/*Close already open file*/ 

 /* Display standard file dialog and
 ** create new file */
 itsFile = new(CDataFile);
 ((CDataFile *)itsFile)->IDataFile();
 itsFile->CreateNew(gSignature, ‘DATA’);

 /* Set window title to new file name */

 return( DoSave() ); /* save normally */

/*** DoRevert {OVERRIDE}
Close current file (without writing out) and read the last saved version 
of file. */
void cDotsDoc::DoRevert()
 Point  homePos;
 Handle theData;
 /* Make sure file is open */
 if (itsFile == NULL)
 return;/* Could use an error message here */

 /* Close file and reopen */
 if (!gError->CheckOSError(itsFile->

 /* Get rid of last undo */
 if (lastTask != NULL) {
 lastTask = NULL;
 /* Read in the data */
 /* Don’t tap fund, can fail */
 gApplication->RequestMemory(FALSE, TRUE);
 ((CDataFile *)itsFile)->ReadAll(&theData);
 gApplication->RequestMemory(FALSE, FALSE);

 /* Check to see if data was read */
 if (theData == NULL) {
 /* Store the data into the document */

 /* Set panoramas to home position */
 ScrollTo(homePos, FALSE);
 /* Force redraw of panes on update */
 dirty = FALSE;

/*** storeData
Store the file data as as instance variables in the document */
void cDotsDoc::storeData(Handle theData)
 int    x;
 long count;

 /* read whose turn it is */
 BlockMove(*theData, &x, 2);
 if (x == 1)
 fPlayerTurn = kPlayer1;
 fPlayerTurn = kPlayer2;
 /* read the line states */
 x = sizeof(tLines);
 count = (long)x;
 BlockMove(*theData+2, fLines, count);
 /* read the box states */
 count = (long)sizeof(tBoxes);
 BlockMove(*theData+x+2, fBoxes, count);

/******* P R I N T I N G ********/
/*** PageCount {OVERRIDE}
Return the number of pages in a Document */
short cDotsDoc::PageCount()
 long pixWidth;
 long pixHeightDots;
 long pixHeightScore;
 /* Get dimensions of dots pane */
 /* Get dimensions of score pane */
 /* Pages have a fixed pixel height */
 return((pixHeightDots + pixHeightScore) /
 pageHeight + 1);
/*** AboutToPrint{OVERRIDE}
The specified range of pages is about to be printed, display print banner 
and let panes know they are about to be printed.
void  cDotsDoc::AboutToPrint(
 short  *firstPage,
 short  *lastPage)
 Str255 docName;
 inherited::AboutToPrint(firstPage, lastPage);
 /* Let panes know they’re to print */
 fDotsPane->AboutToPrint(firstPage, lastPage);
 fScorePane->AboutToPrint(firstPage, lastPage);
 /* Let user know about printing */
 PositionDialog(‘DLOG’, DLOG_PRINT);
 printBanner = GetNewDialog(DLOG_PRINT,
 NULL, (WindowPtr) -1L);
 /* Doc name for LaserWriter status */
 SetWTitle(printBanner, docName);
/*** PrintPageOfDoc {OVERRIDE}
Print a single page of the document. */
void  cDotsDoc::PrintPageOfDoc(short pageNum)
 fDotsPane->PrintPage(pageNum, pageWidth,
 fScorePane->PrintPage(pageNum, pageWidth,
/***  DonePrinting {OVERRIDE}
Print loop has been completed. */
void  cDotsDoc::DonePrinting()
 short  itemType;
 Handle item;
 Rect   box;
 Str63  abortStr;
 long   ticks;
 /* If print aborted, display message */
 if (PrError() == iPrAbort) {
 GetDItem(printBanner, 1, &itemType, &item, &box);
 GetIndString(abortStr, STRcommon, strABORTPRINT);
 SetIText(item, abortStr);
 Delay(120, &ticks);


 /* Changing cursor to arrow. May not
 ** be returning to main event loop */

/********* G A M E ***********/
/** getBoxState
Get state of a box. 3 or less means not yet completed, 4 means completed 
by player 1, 5 means completed by player 2. */
tBoxState cDotsDoc::getBoxState(int row, int col)

/*** changeBoxState
Change state of box */
Boolean cDotsDoc::changeBoxState(int row, 
 int col, Boolean thePlayer,
 Boolean addingLine)
 int    delta;
 changed = false; /* Assume same box owner */
 boxState = fBoxes[row][col];
 if (addingLine) {
 /* Is box completed? */
 if (boxState < kBoxPlayer1) {
 boxState = boxState + 1;
 if (boxState == kBoxPlayer1) {
 changed = true; /* box ownership changed */
 /* Did player 2 complete box? */
 if (thePlayer == kPlayer2)
 boxState = kBoxPlayer2;
 /* Change box and score */
 fDotsPane->invalBox(row, col);
 } else { /* Remove line */
 /* If box completed redraw box and score */
 if (boxState >= kBoxPlayer1) {
 changed = true; /* box ownership changed */
 fDotsPane->invalBox(row, col);
 /* Update state of box */
 if (boxState == kBoxPlayer2)
 boxState = 3;
 boxState = boxState - 1;
 fBoxes[row][col] = boxState;
 return changed;

/*** getLineState
Get state of line. True means there is a line in direction from the input 
dot, false no line. */
tLineState cDotsDoc::getLineState(tLineDir direction, int row, int col)

/*** setLineState
Change state of line. Set to true for a line or false for no line. Set 
to next player turn if
box ownership did not change. */
void cDotsDoc::setLineState(tLineDir direction,
 int row, int col, Boolean thePlayer,
 tLineState newState)
 BooleanaChange, bChange;
 oldState = getLineState(direction, row, col);
 /* Assume box ownership did not change */
 aChange = bChange = false;

 if (oldState != newState) {
 fLines[direction][row][col] = newState;
 /* must redraw line */
 fDotsPane->invalLine(direction, row, col);
 /* Change state of any box to right
 ** or under line */
 if ((row < kMaxRow) && (col < kMaxCol))
 aChange = changeBoxState (row, col,
 /* Change state of any box to left or
 ** above line */
 if (direction == hLine) {
 if (row > 0)
 bChange = changeBoxState(row-1, col,
 thePlayer, newState);
 } else {
 if (col > 0)
 bChange = changeBoxState(row, col-1,
 thePlayer, newState);
 /* If box ownership did not change it is
 ** other player’s turn */
 if (!aChange && !bChange) {
 fPlayerTurn = !fPlayerTurn;

/*** getPlayerTurn
Identify player whose move it is */
Boolean cDotsDoc::getPlayerTurn()

Class for score view
#define _H_cScorePane
#include <CPane.h>

 /*** Class Types ***/
typedef int tScores[2];

struct cScorePane : CPane
 /* Instance variables */
 BooleanfPlayerHilite;  /* Player highlighted */
 /* Initialization Methods */
 void IScorePane(CView *anEnclosure, 
 CBureaucrat *aSupervisor, short aWidth, 
 short aHeight, short aHEncl, short aVEncl,
 SizingOption aHSizing, SizingOption aVSizing);

 /* Override Methods */
 void Draw(Rect *area);

 /* New Methods */
 void calculateScore(tScores score);
 void   getPlayerRect(Boolean thePlayer, Rect *r);
 void invalScore(Boolean thePlayer);
 void setTurn(Boolean thePlayer);

Methods for the score pane. The score pane displays the current player 
turn and calculates and displays the game score.
#include “cDotsDoc.h”
#include “cScorePane.h”
#include “dotsTypes.h”
#include <Global.h>

 /*** Class Constants ***/
#define kScorePlace30
#define kHalfSpacing (kScorePlace / 2)
#define kScoreSpacing(3*kScorePlace)
#define kScoreInset6

 /*** Globals ***/
extern  Pattern  *dgPlayerPats;

/******** C O N S T R U C T I O N ********/
/*** IScorePane
Initialize instance variables */
void cScorePane::IScorePane(CView *anEnclosure,    CBureaucrat *aSupervisor, 
short aWidth,
 short aHeight, short aHEncl, short aVEncl,
 SizingOption aHSizing, SizingOption aVSizing)
 /* Highlighted score is player to move next */
 fPlayerHilite = 
 ((cDotsDoc *)aSupervisor)->getPlayerTurn();
 /* Call inherited method */
 IPane(anEnclosure, aSupervisor, aWidth, aHeight,
 aHEncl, aVEncl, aHSizing, aVSizing);

/******** D R A W I N G **********/
/*** Draw {OVERRIDE}
The area parameter gives the portion of pane that needs to be redrawn. 
Area is in frame coordinates. */
void cScorePane::Draw(Rect *area)
 Rect r;
 Str255 s;
 int    offset;
 /* Draw line across bottom of pane */
 MoveTo(r.left, r.bottom-1);
 LineTo(r.right, r.bottom-1);
 PenSize(kScoreInset, kScoreInset);
 for (player = kPlayer1; player <= kPlayer2;
 player++) {
 NumToString(score[player], &s);
 /* Center score in player’s rectangle */
 getPlayerRect(player, &r);
 offset = (r.right-r.left-StringWidth(s)) / 2;
 MoveTo(r.left + offset, r.bottom - 3);
 InsetRect(&r, -kScoreInset, -kScoreInset);
 /* Hilight/Unhighlight player score */
 getPlayerRect(fPlayerHilite, &r);

/*** calculateScore
Computes the current score */
void cScorePane::calculateScore(tScores theScore)
 int    row, col;
 theScore[kPlayer1] = 0;
 theScore[kPlayer2] = 0;
 /* Check box states for entire matrix */
 for (row = 0; row < kMaxRow; row++) {
 for (col = 0; col < kMaxCol; col++) {
 boxState = ((cDotsDoc *)
 itsSupervisor)->getBoxState(row, col);
 switch (boxState) {
 case kBoxPlayer1:
 theScore[kPlayer1] =theScore[kPlayer1]+1;
 case kBoxPlayer2:
 theScore[kPlayer2] =theScore[kPlayer2]+1;

/*** getPlayerRect
Gets rectangle in which to draw player’s score */
void cScorePane::getPlayerRect(Boolean thePlayer, Rect *r)
 SetRect(r, kScorePlace, kHalfSpacing,
 2*kScorePlace, kScorePlace);
 if (thePlayer == kPlayer2)
 OffsetRect(r, kScoreSpacing, 0);

/*** invalScore
Invalidate player score rectangle so it will redraw on update
void cScorePane::invalScore(Boolean thePlayer)
 Rect r;
 /* Get display rectangle for player score */
 getPlayerRect(thePlayer, &r);

 /* Set score pane’s port before invalidation
 ** otherwise draw in a different port */

/*** setTurn
Highlight current player’s turn. Actually draw on screen since it is 
easy to update turn indicator. */
void cScorePane::setTurn(Boolean thePlayer)
 Rect r;
 if (thePlayer != fPlayerHilite) {
 Prepare(); /* Sets up pane for drawing */
 /* Unhighlight currently highlighted score */
 getPlayerRect(fPlayerHilite, &r);
 /* Highlight other player score */
 getPlayerRect(thePlayer, &r);
 fPlayerHilite = thePlayer;

Class for Dot Matrix View
#define _H_cDotsPane
#include <CPanorama.h>
#include “dotsTypes.h”

struct cDotsPane : CPanorama
 /* Initialization Methods */
 void   IDotsPane(CView *anEnclosure,
 CBureaucrat *aSupervisor, short aWidth,
 short aHeight, short aHEncl, short aVEncl,
 SizingOption aHSizing, SizingOption aVSizing);

 /* Override Methods */
 void Draw(Rect *area);
 void DoClick(Point hitPt, short modifierKeys,
 long when);

 /* New Methods */
 void drawCorner(int row, int col);
 void box2Rect(int row, int col, Rect *r);
 void dot2Rect(int row, int col, Rect *r);
 void dotCenter(int row, int col, Point *center);
 void line2Pt(tLineDir direction, int row,
 int col, Point *pt);
 void line2Rect(tLineDir direction,
 int row, int col, Rect *r);
 Booleanpt2Line(Point pt, tLineDir
 *direction, int *row, int *col);
 void invalLine(tLineDir direction,
 int row, int col);
 void   invalBox(int row, int col);


Methods for the dot matrix view. The dot matrix view contains the grid 
where the game is actually played. Line segments are selected by clicking 
on the area between two horizontal or two vertical dots. When the four 
line segments surrounding a grid cell are selected, the pattern associated 
with the  player completing the ‘box’ is placed inside the cell.
#include <CScrollPane.h>
#include “cDotsDoc.h”
#include “cDotsPane.h”
#include “cDotsTask.h”
#include “dotsTypes.h”

 /*** Class Constants ***/
#define kHalfDotSize 3
#define kFullDotSize (2*kHalfDotSize+1)
#define kHalfLineSize1
#define kFullLineSize(2*kHalfLineSize+1)
#define kLineOffset
 ((kFullDotSize - kFullLineSize) / 2)
#define kDotSpacing(4*kFullDotSize)
#define UNDOMoveIndex1

 /*** Globals ***/
extern Pattern *dgPlayerPats;

/******* C O N S T R U C T I O N *******/
/*** IDotsPane {OVERRIDE}
Initialize instance variables */
void cDotsPane::IDotsPane(CView *anEnclosure,      CBureaucrat *aSupervisor, 
short aWidth,
 short aHeight, short aHEncl, short aVEncl,
 SizingOption aHSizing, SizingOption aVSizing)
 IPanorama(anEnclosure, aSupervisor, aWidth,
 aHeight, aHEncl, aVEncl, aHSizing, aVSizing);
 /* Set scrolling scales */
 SetScales(kDotSpacing, kDotSpacing);
 SetWantsClicks(true); /*For line select*/

/******** D R A W I N G **********/
/*** Draw {OVERRIDE}
Draw the entire Dot Matrix View. Area input paramater is ignored. */
void cDotsPane::Draw(Rect *area)
 int  row, col;

 for (row = 0; row <= kMaxRow; row++)
 for (col = 0; col <= kMaxCol; col++)
 drawCorner(row, col);

/*** drawCorner
Draws the grid dots, grid lines and fills in player boxes */
void cDotsPane::drawCorner(int row,
 int col)
 Rect   r;
 if ((row < kMaxRow) && (col < kMaxCol)) {
 box2Rect(row, col, &r);  /* Get box */
 /* first draw the box if needed */
 boxState = ((cDotsDoc*)itsSupervisor)->
 getBoxState(row, col);
 if (boxState == kBoxPlayer1) {
 FillRect(&r, &dgPlayerPats[kPlayer1]);
 else if (boxState == kBoxPlayer2) {
 FillRect(&r, &dgPlayerPats[kPlayer2]);
 dot2Rect(row, col, &r);  /* Get dot rectangle */
 PenSize(kFullLineSize, kFullLineSize);
 /* Draw line down from dot (if any) */
 if ((row < kMaxRow) &&
 getLineState(vLine, row, col))) {
 Line(0, kDotSpacing);
 /* Draw line right from dot (if any) */
 if ((col < kMaxCol) &&
 getLineState(hLine, row, col))) {
 Line(kDotSpacing, 0);
 FillOval(&r, black);/* Draw the dot */

/*** box2Rect
Returns the rectangle defining the box */
void cDotsPane::box2Rect(int row, int col,Rect *r)
 Point  center;
 dotCenter(row, col, &center);
 SetRect(r, 0, 0, kDotSpacing+1, kDotSpacing+1);
 OffsetRect(r, center.h, center.v);
 InsetRect(r, kHalfLineSize+4, kHalfLineSize+4);

/*** dot2Rect
Returns the rectangle defining a dot */
void cDotsPane::dot2Rect(int row, int col, Rect *r)
 Point  center;
 dotCenter(row, col, &center);
 SetRect(r, -kHalfDotSize, -kHalfDotSize,
 kHalfDotSize+1, kHalfDotSize+1);
 OffsetRect(r, center.h, center.v);

/*** dotCenter
Returns center coord of a given dot */
void cDotsPane::dotCenter(int row, int col,
 Point *center)
 SetPt(center, kDotSpacing*(col+1),

/*** line2Pt
Converts coord of line to a pt on grid */
void cDotsPane::line2Pt(tLineDir direction,
 int row, int col, Point *pt)
 /* Set pt to center of dot */
 dotCenter(row, col, pt);
 /* Offset it to be on requested line */
 if (direction == hLine)
 pt->h = pt->h + kHalfDotSize + 1;
 pt->v = pt->v + kHalfDotSize + 1;

/*** line2Rect
Convert coordinates of a line to a rectangle. Used for highlighting while 
tracking and for invalidating when line is chosen. */    
void cDotsPane::line2Rect(tLineDir direction,
 int row, int col, Rect *r)
 Point  center;
 /* Set pt to center of dot */
 dotCenter(row, col, &center);
 /* Offset it to be on requested line */
 if (direction == hLine)
 SetRect(r, 0, -kHalfLineSize,
 kDotSpacing+1, kHalfLineSize+1);
 SetRect(r, -kHalfLineSize, 0,
 kHalfLineSize+1, kDotSpacing+1);
 OffsetRect(r, center.h, center.v);

/*** pt2Line
Convert point in grid line coordinates */
Boolean cDotsPane::pt2Line(Point pt,
 tLineDir *direction, int *row, int *col)
 int    modH;
 int    modV;
 ok = FALSE;
 /* Offset pt by slop allowed before line */
 pt.h = pt.h + kFullDotSize;
 pt.v = pt.v + kFullDotSize;
 /* Determine values of row and col */
 *row = pt.v / kDotSpacing - 1;
 *col = pt.h / kDotSpacing - 1;

 /* Check if point selected is on grid*/
 ok = (*row >= 0) && (*row <= kMaxRow) &&
 (*col >= 0) && (*col <= kMaxCol);
 if (ok) {
 /* Consider taking pt.v % kDotSpacing. In
 ** order to be on a horizontal line, this
 ** must be in the range 0..2*kFullDotSize */
 modH = pt.h % kDotSpacing;
 modV = pt.v % kDotSpacing;
 /* Pick the closest line to point */
 if (modH < modV) {
 *direction = vLine;
 ok = (*row < kMaxRow) && 
 (modH <= 2*kFullDotSize);
 } else {
 *direction = hLine;
 ok = (*col < kMaxCol) && 
 (modV <= 2*kFullDotSize);
 if (!ok)
 *row = *col = -1;

/*** invalLine
Invalidate line so it redraws on update */
void cDotsPane::invalLine(tLineDir
 direction, int row, int col)
 Rect r;
 /* Get line’s rectangle */
 line2Rect(direction, row, col, &r); 
 /* Set to dots matrix view’s port
 ** before invalidation otherwise may be
 ** drawing in a different port */

/*** invalBox
Invalidate box so it redraws on update */
void cDotsPane::invalBox(int row, int col)
 Rect r;

 box2Rect(row, col, &r);  /* Get box’s rectangle */      
 /* Set to dots matrix view’s port
 ** before invalidation otherwise may be
 ** drawing in a different port */

/*** M O U S E    T R A C K I N G *****/
/*** DoClick {OVERRIDE}
Respond to a mouse down in the dots pane
void cDotsPane::DoClick(Point hitPt,
 short modifierKeys, long when)
 Rect   bounds;
 short  theHScale;
 short  theVScale;

 /* Set up a task both for tracking the
 ** mouse and for do/undo */
 theDotsTask = new(cDotsTask);
 this, (cDotsDoc *)itsSupervisor);
 /* Set up a pin rect for autoScroll
 ** based on panorama bounds */
 GetScales(&theHScale, &theVScale);
 GetBounds(&bounds); *= theVScale;
 bounds.left *= theHScale;
 bounds.bottom *= theVScale;
 bounds.right *= theHScale;
 TrackMouse(theDotsTask, hitPt, &bounds);

 /*Only save for undo if move is valid*/
 if (theDotsTask->validMove())
   /* Let doc save for undo */

Class for handling dot matrix view mouse downs
#define _H_cDotsTask

#include “cDotsDoc.h”
#include “cDotsPane.h”
#include “dotsTypes.h”

struct cDotsTask : CMouseTask
 /** Instance Variables **/
 cDotsDoc *fDotsDoc; /* Document of pane */
 cDotsPane*fDotsPane;/* Pane of mouse action */
 BooleanfPlayerMoved;/* Player making move */
 BooleanfValidMove;/* True if previously
 ** unselected line is selected */
 tLineDir fDirection;/* These 3 fields record */
 int    fRow;    /* the line chosen by    */
 int    fCol;    /* the user              */
 /** Initialization Methods **/
 void   IDotsTask(short aNameIndex, 
 cDotsPane *theDotsPane, cDotsDoc *theDotsDoc);
 /** Override Methods **/
 void   BeginTracking(Point *startPt);
 void   KeepTracking(Point *currPt, 
 Point *prevPt, Point *startPt);
 void   EndTracking(Point *currPt, Point *prevPt,
 Point *startPt);
 void   Do(void);
 void   Undo(void);
 void   Redo(void);

 /** New Methods **/

Handles mouse clicks in the dots pane.
Also provides undo/redo capability.
#include “cDotsTask.h”
#include “dotsTypes.h”

/******** C O N S T R U C T I O N ********/

/*** IDotsTask
Initialize a DotsTask object
void  cDotsTask::IDotsTask(short aNameIndex,
 cDotsPane*theDotsPane, cDotsDoc *theDotsDoc)

 /* Initialize instance variables */
 fDotsPane = theDotsPane;
 fDotsDoc = theDotsDoc;
 fPlayerMoved = fDotsDoc->getPlayerTurn();
 fValidMove = false;
 fDirection = hLine;
 fRow = -1;
 fCol = -1;

/*****  M O U S E   T R A C K I N G ********/
/*** BeginTracking {OVERRIDE}
Mouse tracking starting. Adjust starting pt. */
void  cDotsTask::BeginTracking(
 Point  *startPt)
 tLineDir direction;
 int    row;
 int    col;
 Rect   r;

 /* See if pt is on a line */ 
 if (fDotsPane->
 pt2Line(*startPt, &direction, &row, &col)) {
 /* Convert line coords to a pt on the grid */
 row, col, startPt);
 /* If line unoccupied then fill it */
 if (!fDotsDoc->
 getLineState(direction, row, col)) {
 fDotsPane->line2Rect(direction, row, col,&r);
 FillRect(&r, black);

/*** KeepTracking {OVERRIDE}
Continuous mouse tracking while the mouse button is down. Draw only when 
the mouse moves or the panorama autoscrolls. */
void  cDotsTask::KeepTracking(
 Point  *currPt,
 Point  *prevPt,
 Point  *startPt)
 int    col;
 tLineDir direction;
 Rect   r;
 int    row;

 /* Only track if not autoScrolling */
 if (!fDotsPane->AutoScroll(*currPt)) {
 /* assume won’t erase line previously chosen
 ** by this mouse down */
 eraseIt = false;
 /* Check for current pt on a line */
 if (fDotsPane->
 pt2Line(*currPt, &direction, &row, &col)) {
 /* Convert line coords to a pt on grid */
 line2Pt(direction, row, col, currPt);
 /* Check for new chosen line */
   if (!EqualPt(*currPt, *startPt)) {
 /* If new chosen line unoccupied 
 ** then fill it */
 if (!fDotsDoc->
 getLineState(direction, row, col)) {
 row, col, &r);
 FillRect(&r, black);
 eraseIt = true; /* erase previous line */
 } else /* Erase any previously chosen line */
 eraseIt = true;
 /* If no longer over a previously chosen line
 ** then erase it */
 if (eraseIt) {
 /* Check if start pt on a line */
 if (fDotsPane->
 pt2Line(*startPt, &direction, &row, &col)){
 /* Only erase if start line unoccupied */
 if (!fDotsDoc->
 getLineState(direction, row, col)) {
 /* erase the previouly chosen line */
 line2Rect(direction, row, col, &r);
 FillRect(&r, white);
 /* redraw both dots */
 fDotsPane->dot2Rect(row, col, &r);
 FillOval(&r, black);
 if (direction == hLine)
 fDotsPane->dot2Rect(row, col, &r);
 FillOval(&r, black);
 /* Reset startPt to new point */
 SetPt(startPt, currPt->h, currPt->v);

/*** EndTracking {OVERRIDE}
Mouse down tracking has ended because the user has released the mouse 
button. Its now time to complete the move and set up the undo. */
void  cDotsTask::EndTracking(Point *currPt, 
 Point   *prevPt, Point *startPt)
 tLineDir direction;
 int    row;
 int    col;

 /* The only valid move is selection of a previously unselected line 
so check if selected point is on line and the line is unoccupied */ 
 if ((fDotsPane->
 pt2Line(*prevPt, &direction, &row, &col)) &&
 getLineState(direction, row, col)))
 fValidMove = true;
 fDirection = direction;
 fRow = row;
 fCol = col;
 Do();  /* Act on users choice */

/*********** D O   /   U N D O ***********/
/*** Do  {OVERRIDE}
Perform a dots pane task, user chose a line */
void  cDotsTask::Do()
 fDotsDoc->setLineState(fDirection, fRow, fCol,
 fPlayerMoved, true);

/*** Undo {OVERRIDE}
Undo an action performed by a dots pane task */
void  cDotsTask::Undo()
 fDotsDoc->setLineState(fDirection, fRow, fCol,
 fPlayerMoved, false);

/*** Redo {OVERRIDE}
Redo an action performed by a dots pane task */
void  cDotsTask::Redo()
 fDotsDoc->setLineState(fDirection, fRow, fCol,
 fPlayerMoved, true);

/*********** A C C E S S ***********/
/*** validMove
Return value of fValidMove instance variable. The value will be true 
if previously unselected line was selected during this mouse task, else 
false. */
Boolean cDotsTask::validMove()


Community Search:
MacTech Search:

Software Updates via MacUpdate

Minecraft 1.20.2 - Popular sandbox build...
Minecraft allows players to build constructions out of textured cubes in a 3D procedurally generated world. Other activities in the game include exploration, gathering resources, crafting, and combat... Read more
HoudahSpot 6.4.1 - Advanced file-search...
HoudahSpot is a versatile desktop search tool. Use HoudahSpot to locate hard-to-find files and keep frequently used files within reach. HoudahSpot is a productivity tool. It is the hub where all the... Read more
coconutBattery 3.9.14 - Displays info ab...
With coconutBattery you're always aware of your current battery health. It shows you live information about your battery such as how often it was charged and how is the current maximum capacity in... Read more
Keynote 13.2 - Apple's presentation...
Easily create gorgeous presentations with the all-new Keynote, featuring powerful yet easy-to-use tools and dazzling effects that will make you a very hard act to follow. The Theme Chooser lets you... Read more
Apple Pages 13.2 - Apple's word pro...
Apple Pages is a powerful word processor that gives you everything you need to create documents that look beautiful. And read beautifully. It lets you work seamlessly between Mac and iOS devices, and... Read more
Numbers 13.2 - Apple's spreadsheet...
With Apple Numbers, sophisticated spreadsheets are just the start. The whole sheet is your canvas. Just add dramatic interactive charts, tables, and images that paint a revealing picture of your data... Read more
Ableton Live 11.3.11 - Record music usin...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
Affinity Photo 2.2.0 - Digital editing f...
Affinity Photo - redefines the boundaries for professional photo editing software for the Mac. With a meticulous focus on workflow it offers sophisticated tools for enhancing, editing and retouching... Read more
SpamSieve 3.0 - Robust spam filter for m...
SpamSieve is a robust spam filter for major email clients that uses powerful Bayesian spam filtering. SpamSieve understands what your spam looks like in order to block it all, but also learns what... Read more
WhatsApp 2.2338.12 - Desktop client for...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more

Latest Forum Discussions

See All

Square Enix commemorates one of its grea...
One of the most criminally underused properties in the Square Enix roster is undoubtedly Parasite Eve, a fantastic fusion of Resident Evil and Final Fantasy that deserved far more than two PlayStation One Games and a PSP follow-up. Now, however,... | Read more »
Resident Evil Village for iPhone 15 Pro...
During its TGS 2023 stream, Capcom showcased the Following upcoming ports revealed during the Apple iPhone 15 event. Capcom also announced pricing for the mobile (and macOS in the case of the former) ports of Resident Evil 4 Remake and Resident Evil... | Read more »
The iPhone 15 Episode – The TouchArcade...
After a 3 week hiatus The TouchArcade Show returns with another action-packed episode! Well, maybe not so much “action-packed" as it is “packed with talk about the iPhone 15 Pro". Eli, being in a time zone 3 hours ahead of me, as well as being smart... | Read more »
TouchArcade Game of the Week: ‘DERE Veng...
Developer Appsir Games have been putting out genre-defying titles on mobile (and other platforms) for a number of years now, and this week marks the release of their magnum opus DERE Vengeance which has been many years in the making. In fact, if the... | Read more »
SwitchArcade Round-Up: Reviews Featuring...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for September 22nd, 2023. I’ve had a good night’s sleep, and though my body aches down to the last bit of sinew and meat, I’m at least thinking straight again. We’ve got a lot to look at... | Read more »
TGS 2023: Level-5 Celebrates 25 Years Wi...
Back when I first started covering the Tokyo Game Show for TouchArcade, prolific RPG producer Level-5 could always be counted on for a fairly big booth with a blend of mobile and console games on offer. At recent shows, the company’s presence has... | Read more »
TGS 2023: ‘Final Fantasy’ & ‘Dragon...
Square Enix usually has one of the bigger, more attention-grabbing booths at the Tokyo Game Show, and this year was no different in that sense. The line-ups to play pretty much anything there were among the lengthiest of the show, and there were... | Read more »
Valve Says To Not Expect a Faster Steam...
With the big 20% off discount for the Steam Deck available to celebrate Steam’s 20th anniversary, Valve had a good presence at TGS 2023 with interviews and more. | Read more »
‘Honkai Impact 3rd Part 2’ Revealed at T...
At TGS 2023, HoYoverse had a big presence with new trailers for the usual suspects, but I didn’t expect a big announcement for Honkai Impact 3rd (Free). | Read more »
‘Junkworld’ Is Out Now As This Week’s Ne...
Epic post-apocalyptic tower-defense experience Junkworld () from Ironhide Games is out now on Apple Arcade worldwide. We’ve been covering it for a while now, and even through its soft launches before, but it has returned as an Apple Arcade... | Read more »

Price Scanner via

New low price: 13″ M2 MacBook Pro for $1049,...
Amazon has the Space Gray 13″ MacBook Pro with an Apple M2 CPU and 256GB of storage in stock and on sale today for $250 off MSRP. Their price is the lowest we’ve seen for this configuration from any... Read more
Apple AirPods 2 with USB-C now in stock and o...
Amazon has Apple’s 2023 AirPods Pro with USB-C now in stock and on sale for $199.99 including free shipping. Their price is $50 off MSRP, and it’s currently the lowest price available for new AirPods... Read more
New low prices: Apple’s 15″ M2 MacBook Airs w...
Amazon has 15″ MacBook Airs with M2 CPUs and 512GB of storage in stock and on sale for $1249 shipped. That’s $250 off Apple’s MSRP, and it’s the lowest price available for these M2-powered MacBook... Read more
New low price: Clearance 16″ Apple MacBook Pr...
B&H Photo has clearance 16″ M1 Max MacBook Pros, 10-core CPU/32-core GPU/1TB SSD/Space Gray or Silver, in stock today for $2399 including free 1-2 day delivery to most US addresses. Their price... Read more
Switch to Red Pocket Mobile and get a new iPh...
Red Pocket Mobile has new Apple iPhone 15 and 15 Pro models on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide service using all the major... Read more
Apple continues to offer a $350 discount on 2...
Apple has Studio Display models available in their Certified Refurbished store for up to $350 off MSRP. Each display comes with Apple’s one-year warranty, with new glass and a case, and ships free.... Read more
Apple’s 16-inch MacBook Pros with M2 Pro CPUs...
Amazon is offering a $250 discount on new Apple 16-inch M2 Pro MacBook Pros for a limited time. Their prices are currently the lowest available for these models from any Apple retailer: – 16″ MacBook... Read more
Closeout Sale: Apple Watch Ultra with Green A...
Adorama haș the Apple Watch Ultra with a Green Alpine Loop on clearance sale for $699 including free shipping. Their price is $100 off original MSRP, and it’s the lowest price we’ve seen for an Apple... Read more
Use this promo code at Verizon to take $150 o...
Verizon is offering a $150 discount on cellular-capable Apple Watch Series 9 and Ultra 2 models for a limited time. Use code WATCH150 at checkout to take advantage of this offer. The fine print: “Up... Read more
New low price: Apple’s 10th generation iPads...
B&H Photo has the 10th generation 64GB WiFi iPad (Blue and Silver colors) in stock and on sale for $379 for a limited time. B&H’s price is $70 off Apple’s MSRP, and it’s the lowest price... Read more

Jobs Board

Housekeeper, *Apple* Valley Villa - Cassia...
Apple Valley Villa, part of a 4-star senior living community, is hiring entry-level Full-Time Housekeepers to join our team! We will train you for this position and Read more
Housekeeper, *Apple* Valley Village - Cassi...
Apple Valley Village Health Care Center, a 4-star rated senior care campus, is hiring a Part-Time Housekeeper to join our team! We will train you for this position! Read more
Optometrist- *Apple* Valley, CA- Target Opt...
Optometrist- Apple Valley, CA- Target Optical Date: Sep 23, 2023 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 796045 At Target Read more
Senior *Apple* iOS CNO Developer (Onsite) -...
…Offense and Defense Experts (CODEX) is in need of smart, motivated and self-driven Apple iOS CNO Developers to join our team to solve real-time cyber challenges. Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.