C_TALK/VIEWS

Recently ported to Microsoft Windows, this unique language adds a powerful class library for dealing with graphical user interfaces

Noel Bergman

Noel Bergman is the president of Development Technologies, Inc., a software development consulting firm with special interests in OS/2, Windows, and OOP. He can be reached on CompuServe (CIS ID: 76704,34) where he is a volunteer Sysop on Microsoft's CompuServe forum. Development Technologies can be reached at 8329 High School Road, Elkins Park, PA 19117 or 215-386-9599.


With object-oriented programming emerging as the major new programming paradigm for the 1990s, and C being the darling language of professional software developers in the PC and minicomputer arenas, what could be more natural than object-oriented extensions to C? C_talk is one such object-oriented extension to C and it is very much in the mold pioneered by Brad Cox's Objective-C language. While there are a number of such derivative languages, they all nonetheless have similar differences from the other great object-oriented C dialect, AT&T's C++.

C_talk is extended from C with two basic constructs: classes and messages. C_talk messages take the form:

[< 1value > =] @< receiver > < message >@

The optional assignment is an enhancement from earlier versions of C_talk, and allows a method to return a value. (Remember that a message is what you send to an object, and a method is the actual code that handles a message.)

C_talk uses a more Smalltalk-like syntax with named parameters, unlike the C++ method/message syntax. (For more information on the C_talk syntax, see "A Class-ier C" by Ernie Tello, DDJ December 1988, page 74.) As with Smalltalk, C_talk's class hierarchy has a single class object, from which all other classes are derived. There is no multiple inheritance, though the true polymorphism in C_talk cuts down on the need for multiple inheritance, unlike C++.

If a language has true polymorphism, it is possible to have an arbitrary collection of objects, and to send a message to each member object without knowing its type. This is not possible in C++. Although virtual functions do provide limited polymorphism, each object in a collection must be descended from a common ancestor who declared the virtual function. One way of getting around this in C++ is to use multiple inheritance, but a detailed explanation of how is beyond the scope of this article.

C++ supports static, or early binding; this means that the function to be called at run time is known at compile time. In the case of a virtual function, the index in an array of pointers to functions is known. True polymorphism includes dynamic, or late binding, in which the method is looked up for the specific object and executed. But there is a run-time penalty for late binding. Objective-C now offers the option of mixed early and late binding. C_talk only offers late binding in the language, but a new tool, C_talk/Views, performs some early binding at compile time.

The C_talk/Views Package

C_talk/Views is a package made up from a number of components. The C_talk Browser is a specialized editor for working with object classes and methods; the C_talk preprocessor converts C_talk into C; class libraries provide a base set of fundamental object-oriented classes; and a special class library is provided for working with graphical user interfaces. C_talk also includes a Make facility and a Streamliner. The latter is a specialized tool that attempts to determine when early binding can be used and optimizes the code accordingly.

Most of these tools were covered in Ernie Tello's earlier C_talk review, so I'll focus on what is new in the C_talk/Views package: Windows Browser, Streamliner, Interface Generator, and Views.

The C_talk Browser

The C_talk Browser is the tool you'll use for most, if not all, of your C_talk editing. It is possible to use a normal text editor, but the C_talk-to-C preprocessor needs certain specially formatted information, so it is likely that you'll use a standard text editor only to edit some methods on occasion.

The C_talk Browser is functionally the same as the earlier Browser, which was based on text windows. In fact, a text-based Browser is included in the same package with the Microsoft Windows Browser. Figure 1 and Figure 2 show the text-windows-based browser and the Microsoft Windows Browser, respectively. The differences are in the manipulation of menus and windows.

Figure 1: Text windows based browser

   ____________________________________________________________
  |  __  |                                             | | | ^ |
  | |__| |        C_talk/Views Browser - (untitled)    | v | | |
  |______|_____________________________________________|___|___|
  |File                 |  MENU BAR  |                         |
  |_____________________|____________|_________________________|
  |                           |^|                          | ^ |
  |                           |||                          | | |
  |   CLASS LIST WINDOW       |_|   METHODS LIST WINDOW    |___|
  |                           | |                          |   |
  |                           | |                          |___|
  |                           | |                          | | |
  |                           |_|                          | v |
  |                           |||   __________    _________|___|
  |                           |v|  | instance |  |  class    | |
  |___________________________|_|__|__________|__|___________|_|
  |                                                 ^      | ^ |
  |                                           ______|____  | | |
  |                                          |  METHODS  | |___|
  |                                          | SELECTION | |   |
  |                                          |   WINDOW  | |   |
  |                                          |___________| |   |
  |                                                        |   |
  |                  __________________________            |   |
  |                  | CONTENTS / EDIT WINDOW |            |   |
  |                  --------------------------            |___|
  |                                                        | | |
  |                                                        | v |
  |________________________________________________________|___|
  | <-- |   |                                    |   | --> |   |
  |_____|___|____________________________________|___|_____|___|

Figure 2: The Microsoft Windows Browser

  _______________________________________________________________________
 |  __  |                                                        |   |   |
 | |__| |             C_talk/Views Browser - GENAPP              | v | ^ |
 |______|________________________________________________________|___|___|
 |File   Edit   Search   Classes   Methods    Make                       |
 |_______________________________________________________________________|
 |                                 |^| create ViewOf                 | ^ |
 | Region...                       |||-------------------------------| | |
 | Window                          |_|  initialize                   |___|
 |  ControlWindow...               | |                               |   |
 |__View___________________________| |                               |___|
 |___AppView_______________________| |                               | | |
 |   PopupWindow                   |_|                               | v |
 |    Dialog                       |||  __________    _____________  |___|
 |     FileSelect                  |v| | instance |  |    class    | |   |
 |_________________________________|_|_|__________|__|_____________|_|___|
 |create_name ViewOf_model                                           | ^ |
 |/* Return a new instance of the receiver with the default size.    | | |
 |   title and model attributes of the new AppView object.  Send the |___|
 |   message to the new object before returning.  */                 |   |
 |                                                                   |___|
 |                                                                   |   |
 |char *name: /* pointer to title (caption) */                       |   |
 |id model;   /* a model for the view */                             |   |
 |{                                                                  |   |
 |   id a View;                                                      |   |
 |                                                                   |___|
 |   aView = @self newRatioX_ 0.10 Y_ 0.10 W_ 0.75 H_ 0.75 of_ NIL@  | | |
 |                                                                   | v |
 |___________________________________________________________________|___|
 | <-- |   |                                                   | --> |   |
 |_____|___|___________________________________________________|_____|___|

Streamlining: Early Binding for Speed

The Streamliner, CTSW, is used when preparing a product for final testing. It analyzes your application, strips out classes and methods that the analysis reports cannot possibly use, and performs static binding of messages. The result is code that may be half the size of non-streamlined code, and that typically has two-thirds of the messages statically bound.

The Streamliner does have some quirks, but these are minor and are actually documented in the manual.

The Interface Generator

C_talk/Views includes a CTIG utility that translates the .DLG output from the Microsoft Windows Dialog Editor into a C_talk/Views class. Basically, it writes the class method CreateOf_and instance method initialize. You edit the new class, adding instance variables and methods to flesh it out into a complete dialog subclass that is ready for use in applications. In essence, CTIG takes a description of the visual portion of a dialog and codes it for you, leaving you to code only the dialog's function.

Actually, CTIG takes care of one of the Windows programmer's least favorite dialog characteristics -- how the code looks on-screen. CTIG generates code that will look correct on the display, regardless of the display's aspect ratio and resolution.

Currently, CTIG is limited to generating subclasses of Views' DIALOG class, which are framed dialogs. But those dialogs are the majority of dialogs used today.

The New Classes

OK, enough with the tools. These are just delivery vehicles for the true power of OOP, the object classes themselves. C_talk comes with a full complement of foundation object classes in the Small-talk model. To this has been added Views, a rich set of classes for working with graphical user interfaces. Most of these classes are shown in Figure 3.

Figure 3: A subset of the C_talk/Views classes

              __________     ____________     __________    _____________
            _| Assoc    |  _| PointArray |  _|OrdCollect|__|    Stack    |
           | |__________| | |____________| | |__________|  |_____________|
           |              |                |
           |  __________  |  ____________  |  __________    _____________
           |_| Container|_|_| Collection |_|_|   Set    |__|  Dictionary |
           | |__________| | |____________|   |__________|  |_____________|
           |              |
           |  __________  |  ____________     __________    _____________
           |_| Clipboard|_|_|   String   |___|  Stream  |__| FileStream  |
           | |__________|   |____________|   |__________|  |_____________|
           |                                            ______________
           |  __________                              _|  CheckBox    |
           |_|  File    |                            | |______________|
           | |__________|                            |
           |                                         |  ______________
           |  __________     ___________             |_|  PushButton  |
           |_|    Menu  |___| PopupMenu |            | |______________|
           | |__________|   |___________|            |
           |                                         |  ______________
           |  __________                             |_|  RadioButton |
           |_| Menultem |                            | |______________|
 ________  | |__________|                            |
| Object |_|                                         |  ______________
|________| |  __________                __________   |_|   TriState   |
           |_| Notifier |             _| Button   |    |______________|
           | |__________|            | |__________|
           |                         |                  ______________
           |  __________             |                _|   EditLine   |
           |_| Port     |          |                 | |______________|
           | |__________|          |                 |
           |                         |  __________     |  ______________
           |  __________   _______   |_| EditBox  |  |_|  TextEditor  |
           |_| Region   |_| Rect  |  | |__________|    |______________|
           | |__________| |_______|  |
           |                         |                  ______________
           |  __________             |                _|  CheckGroup  |
           |_| Printer  |          |               | |______________|
           | |__________|          |               |
           |                         |  __________   |  ______________
           |  __________             |_| Group    |  |_|  RadioGroup  |
           |_| BitMap   |            | |__________|    |______________|
           | |__________|            |
           |                         |  __________      ______________
           |  __________    ________ |_| ListBox  |    |  FileSelect  |_
           |_| Archive  | _|Control/|| |__________|    |______________| |
           | |__________| ||Window  ||                                  |
           |              ||________||  __________      ______________  |
           |  __________  |          |_|SchrollBar|    |    Input     |_|
           |_| Window   |_|          | |__________|    |______________| |
             |__________| |          |                                  |
                          |          |  __________      ______________  |
                          |          |_| TextBox  |    |   Report     |_|
                          |            |__________|    |______________| |
                          |                                             |
                          |             ____________    ______________  |
                          |           _| AppView    |  |    YesNo     |_|
                          |          | |____________|  |______________| |
                          |          |                                  |
                          |  ______  |  ____________    ______________  |
                          |_| View |_|_|BarChartView|  | YesNoCancel  |_|
                            |______| | |____________|  |______________| |
                                     |                                  |
                                     |  ____________    ______________  |
                                     |_|PopupWindow |__|   Dialog     |_|
                                       |____________|  |______________|

The earlier C_talk article discussed the foundation classes, so I will again focus on what is new in C_talk/Views.

Additional Foundation Classes

In addition to the new Views classes, there is a new foundation class in the C_talk/Views package that provides support for persistent objects. [Editor's note. See "Persistent Objects" by Charles-A. Rovira, Dr. Dobb's Macintosh Journal, Fall 1989.] A future release, which should be available by the time this article reaches print, is expected to add support for communications, timers, and debugging.

Persistence is implemented through an Archiver object class. Archiver objects take care of most of the details involved in storing and loading archives. Objects that are to be kept in the archives, however, must implement the methods putTo_ and getFrom_, which are responsible for storing and loading the subclass' instance variables. This form of persistence is suitable for many applications, but does not directly support random access to objects.

Views: Classes for Windowing Systems

Previous versions of C_talk also included windowing classes, albeit text-based. The new Views classes should not be confused with those earlier windowing packages, although there are some similarities.

Model/View/Controller

The paradigm chosen to be implemented in the Views class library is a modification of the classic Smalltalk Model/View/Controller (MVC) metaphor. This framework formalizes program structure and places classes into one of three categories:

Models -- Virtualize a concept without concern for its representation. For example, the hierarchy of classes in a program, or a network of workstations on a LAN.

Views -- Provide a visual representation of a model. One such representation is the list of classes in the C_talk browser. Another representation of the same information would be the directed graph in Figure 3.

Controllers -- Provide the interface between the other classes and input devices. The interactions between models, views, and controllers are more involved in the Smalltalk MVC model. In the Views adaptation of MVC, the notifier is the only kind of controller. This simplification works well with the Windows/PM application model, because it is analogous to the message queue. In fact, the OS/2 version of Views will probably use one notifier for each message queue thread.

The Views implementation of the MVC paradigm differs from the classic MVC paradigm (see The Journal of Object-Oriented Programming, August/September 1988) in a number of ways. In the latter, Controllers talk directly to both views and models. Models can be active when talking to controllers and views. The Views paradigm has a single controller that talks only to views, and the models are passive (only responding to messages from view objects).

The Views controller is essentially a virtualization of the event system. Therefore, it does not talk directly to models. The events are processed by a view which gives meaning to the events in that view's context.

The Views paradigm is simpler, but does have a few drawbacks. You can have, and often want, multiple views to the same model. The problem is that all the views that use the model have to know about each other, in order to let each other know when a change is made. Future versions of Views may change models, so that a model keeps track of all views using it and notifies them when there is a change in the model.

Using MVC

It takes a little extra development time to use MVC. You have to decide upon formal models for the concepts your program works with. Then you will create views that use the formal interface to the model. Windows/PM programmers tend to intertwine windows (views) and the concepts they represent (models). Even the developers of C_talk/Views have done this. Only a couple of the sample programs use the MVC model, and even then use it somewhat loosely. All of the rest handle model issues as part of the view, making it more difficult to provide alternate views to the same information, change the model without changing the view, and vice versa.

A C_talk/Views Example

C_talk/Views comes with a range of sample programs, from a simple doodle application to a couple of simulations. One of those programs, Sketch (an object-oriented sketch pad), has been a staple of mine at lectures, and in fact has undergone a number of changes either at my hand or at my request. It has never used MVC, however, or the Archiver class. Until now, that is!

Sketch is made up of a few key classes (not counting dialogs or normal foundation classes, such as collections, used in most OO programs): SketchApp (the MVC model), Sketch View (the MVC View), Notifier (the MVC controller), and a small GraphObj family.

SketchApp is the model, and is where I've put the main method (see Listing One, page 109), that current versions of C_talk/Views use as a dummy to hold the main( ) function. It is important to remember that main( ) is not a C_talk method -- there is no self to refer to during its execution. Accordingly, main creates a new instance of SketchApp, then uses SketchView's create_ViewOf_ class method to create a new SketchView with the new SketchApp as the model.

In the current implementation, SketchApp models the behavior of the image memory for the sketch pad. New objects are drawn on the SketchView, which sends an add_ message to the model. The model records all of this information, is queried when the view wants to repaint the entire image, and handles the archiving of the image.

SketchView handles the user interface for the sketch program. It is rather simple in structure, primarily because of Views' menu handling and C_talk's polymorphism. The initialize method (see Listing Two, page 110) tells the model to clear (initializing it to a known, empty state), create a Port object (the Views class, which virtualizes the drawing surface), and set up a menu of functions -- not in that order. A Views menu is made up of 3-tuples [name, accelerator, method]. When a menu item is selected, the associated method is activated for the object associated with the menu (in our case, the SketchView object).

The three graphic "shapes" in the current Sketch program are lines, boxes, and free-hand drawing, each of which is selected from a menu item. The method associated with each menu item is almost identical and is simple. In general, the method unchecks any checked items on the menu (in order to clear the current shape indicator), sets the drawClass instance variable to the class for subsequent shapes, and checks the menu item for that shape. (See Example 1.) When a mouse-down event occurs, the view will create a new instance from drawClass and send mouse messages to it; drawing is virtualized by the classes installed in drawClass.

Example 1: A generic shape method

  *** Generic "shape" method for SketchView ***
  <shape>_ aMenuItem
  id aMenuItem; /* id of selected menu item */
  {
    @self->fMenu uncheckAll@;
    self->drawClass = <ShapeClass>;
    @aMenuItem check@;
  }

SketchView, which is the Views object in our MVC model, receives events from the controller. The Views notifier gives us control over a number of things, one of which is the ability to capture the mouse. SketchView's mouseDnX_Y_ method is sent mouse-down events from the notifier. If there is a current drawing class, we will turn mouse capture on, and enable tracking of mouse movement (otherwise Views doesn't waste time dispatching those events). Then a new drawing object is created, added to the model, associated with the drawing port for our window, and given the mouse-down event for class-specific handling. Similarly, mouse movement and mouse button up events will first be processed by the SketchView, then passed to the drawing object. SketchView handles the interaction with the controller, but leaves all of the drawing to the drawing object. In order to add a new shape, all you have to do is create a class for it and add it to the menu.

All of these drawing classes are grouped under the GraphObj class. Each subclass specializes the mouseDnX_Y_, drawOn_, mouseMvX_Y_Bt_, mouseUpX_Y_, and setPort_methods, which are invoked from SketchView. Also added are the getFrom_ and putTo_ methods for the Archiver class.

Archiving is rather straightforward, once you get the hang of it. Each class must implement getFrom_ and putTo_ methods that are only to be called from the archiver -- never directly! These methods take an instance of Archiver as the parameter and are usually responsible for restoring or saving the classes instances variables. (If there are no new instance variables, the methods inherited from the parent class are usually sufficient). A typical method first sends the message to super to affect inherited instance variables, then handles those specific to its subclass.

SketchView has been enhanced with menu items and methods (open, save, and saveAs_) for interacting with the user and sending the user's request to the model. SketchApp implements load-From_ and save methods (saveAs just sets the file name and invokes save), which it uses to archive the collection of GraphObj instances.

Archivers are easy to use. There are two primary methods, getObject and putObject_, which are sent to an instance of Archiver to get the next object from the archive or to put another one in. SketchApp uses them to save and restore the objects instance variable. Some of the GraphObj classes have objects as instance variables; those, too, are sent to the Archiver with putObject_ and retrieved with getObject. Other instance variables are integers, longs, or other base types. There are Archiver methods for these, too, such as putInt_ and getInt.

This covers the high points in the implementation of the new Sketch program. The key changes in this new version are the move towards MVC, and the use of the Archiver class.

One major change in the Sketch program was made prior to the first public release, but it bears mentioning here. The first version I saw, prior to adopting the program for my own uses, didn't use GraphObj to virtualize the drawing, and didn't have a persistent image -- if you moved the window, the image was erased. Instead, the menu items set an instance variable ivar to indicate which shape was being drawn, and SketchView's mouse handling methods used if statements to check the ivar and do the appropriate things. The change to using GraphObj and an ivar that pointed to the current class not only provided better polymorphism, but led the way to a persistent image, MVC, and archived graphics. Polymorphism and virtualization are key concepts and cannot be too highly emphasized.

On the Downside

C_talk/Views is certainly not a perfect tool. For one thing, it is not written in C++. That's both good and bad. C++ has a lot of inertia behind it as it rolls towards being a standard, but it has limitations, some of which I discussed at the beginning of this article.

C_talk does not allow you to build DLLs from C_talk code, although you can use DLLs written in C or MASM. Views neither implements nor provides help for implementing MDI. These are not expected to change in the near future.

Another problem, which is expected to be fixed in the version available before this article is published, is that all C_talk source files currently have to be located in the same directory. The new version of C_talk will allow you to keep standard classes in one directory and application-specific classes in another.

Finally, there are some annoying little quirks in the browser. For example, there is a lack of keyboard accelerators for some of the menu items. Also, the .DEF file generated by the browser doesn't have the EXETYPE WINDOWS entry required by current versions of the Linker. These are expected to be addressed in the next release.

On the Upside

C_talk/Views permits rapid development of commercial quality Windows programs. This is partially because of the nature of the language, but is mostly due to the robust class library. Even such things as printing are standard in the C_talk/Views package. The Views classes simplify many Windows-related tasks, particularly menu handling and the repositioning of child windows when resizing their parent.

What's Next?

Part of the promise of OOP is portability, not just ease of development. Future versions of C_talk/Views are targeted for other platforms besides Microsoft Windows. Presentation Manager, Macintosh, and X Windows are all being considered. Also, Views will be ported to other languages, such as C++ and Actor.

Personally, I like the possibilities of starting work in Actor/Views, working in a completely interactive environment, then porting the code to C_talk/Views to take advantage of improved memory and CPU utilization. The languages provide similar constructs; the major reason not to do it today is that you would have to change your windowing model, but an Actor version of Views would eliminate that problem. Smalltalk/Views or Object/1 Views would be similarly interesting.

All in all, programmers who harness the power of the C_talk/Views package should reap today's all-important harvest, with even better yields in the future.

Products Mentioned

C_talk/Views CNS, Inc. Software Products Dept. 7090 Shady Oak Road Eden Prairie, MN 55344 Price: $450.00 Requirements: MS-DOS 2.0, MS Windows Microsoft C 5.0 MS Windows Software Development Kit 2.0. Suggested: 80286-based computer, minimum 1-Mbyte RAM

_C_TALK/VIEWS_ by Noel Bergman [LISTING ONE]



!SketchApp subclass of: Object !
className: SketchApp
superClass: Object
header:/* Class name: SketchApp */
/* Super name: Object */
/* File  name: Sketchpp */
/* header */
#include "objtypes.h"
extern id Object;
extern id Archiver;
extern id Notifier;
classVariables:/* class variables */instanceVariables:id objects;
id filename;     /* Filename string */
int changed;     /* changed flag */
fileName:Sketchpp !

!SketchApp class methods !

!SketchApp instance methods !

add_ drawObj
id drawObj;
{
    @self->objects add_ drawObj@;     /* add to collection */
    @self changed@;
}
!

asCollect
{
  return self->objects;
}
!

changed
{
  self->changed = TRUE;
}
!

clear
/* Erase the objects in our collection.  */
{
  extern id OrdCollect;

  if (self->filename) self->filename = @self->filename free@;
  if (self->objects) @self->objects freeAll@;
  self->objects = @OrdCollect new@;
  @self changed@;
  if (self->objects) return TRUE; else return FALSE;
}
!

getFname
{
  return self->filename;
}
!

loadFrom_ fname
id fname;
{
   id fstr, arc;

   if (fname)
   {
      arc = @Archiver openForLoad_ fname@;
      if (arc)
      {
         if (@self clear@) @self->objects freeAll@;
         self->objects = @arc getObject@;
         @self changed@;
         @arc free@;
         self->filename = fname;
         return TRUE;
      }
   }
   return FALSE;
}
!

main
{
}

main()
{
   id aView, aModel;
   extern id SketchView;

   aModel = @SketchApp new@;
   aView = @SketchView create_ "Sketch" ViewOf_ aModel@;
   if (aView)
   {
      @aView show@;
      @Notifier start@;
   }
}

!

save
{
   id arc;

   if (arc = @Archiver openForStore_ self->filename@)
    {
      @Notifier beginWait@;
      @arc putObject_ self->objects@;
      @arc free@;
      self->changed = FALSE;
      @Notifier endWait@;
    }
}
!

saveAs_ filename
id filename;
{
   if(self->filename) @self->filename free@;
   self->filename = filename;
   @self save@;
}

!






[LISTING TWO]



*** New PointArray methods:

getFrom_ anArchive
/* restore the receiver's size, recSize and size objects from the archive. */
id anArchive;     /* id of an Archiver object to store the receiver to */
{
   id anId;
   unsigned i;

   @super getFrom_ anArchive@;
   self->howMany = @anArchive getInt@;
}

putTo_ anArchive
/* Store the receiver's size, recSize and size objects to the archive. */
id anArchive;     /* id of an Archiver object to store the receiver to */
{
   id anId;
   unsigned i;

   @super putTo_ anArchive@;
   @anArchive putInt_ self->howMany@;
}


*** New Rect methods:

getFrom_ anArchiver
{
  self->left   = @anArchiver getInt@;
  self->top    = @anArchiver getInt@;
  self->right  = @anArchiver getInt@;
  self->bottom = @anArchiver getInt@;
}

putTo_ anArchiver
{
  @anArchiver putInt_ self->left@;
  @anArchiver putInt_ self->top@;
  @anArchiver putInt_ self->right@;
  @anArchiver putInt_ self->bottom@;
}


*** New PolyLine methods:

getFrom_ anArchiver
{
  self->ptArray = @anArchiver getObject@;
}

putTo_ anArchiver
{
  @anArchiver putObject_ self->ptArray@;
}


*** New Line methods:

getFrom_ anArchiver
{
  self->x0 = @anArchiver getInt@;
  self->y0 = @anArchiver getInt@;
  self->x1 = @anArchiver getInt@;
  self->y1 = @anArchiver getInt@;
}

putTo_ anArchiver
{
  @anArchiver putInt_ self->x0@;
  @anArchiver putInt_ self->y0@;
  @anArchiver putInt_ self->x1@;
  @anArchiver putInt_ self->y1@;
}


*** New Box methods:

getFrom_ anArchiver
{
  self->rec = @anArchiver getObject@;
}

putTo_ anArchiver
{
  @anArchiver putObject_ self->rec@;
}

*** New or changed SketchView methods:
*
* You must add external ids for SketchApp, String, and FileSelect to the
* SketchView class.  Also remove the instance variable "objects" and the
* main method.
*

erase
/* Erase the objects drawn on the receiver.  */
{
  if (self->model)
    {
     @self->model clear@;
     @self update@;
    }
}


initialize
/* Initialize receiver by creating a port to scribble on.   */
{
  id     aMenu;
  extern id    Port, Menu, PopupMenu;
  extern char  *scribNmes[];
  extern int   scribSels[], scribKeys[];

  if (aMenu = @Menu new@)
   {
    if (self->fMenu = @PopupMenu new_ "&Functions"@)
     {
      @self->fMenu setNames_ scribNmes sels_ scribSels keys_ scribKeys for_ self@;
      @aMenu append_ self->fMenu@;
      @self setMenu_ aMenu@;
      if (@self->model clear@)
       if (self->aPort = @Port createOn_ self@)  /* create graphics port on receiver */
         return self;
     }
   }
 return NIL;
}

char  *scribNmes[] = {"S&ketch\tF1", "&Line\tF2", "&Box\tF3", "E&rase\tF4", "&Save\tF5", "Save&As\tF6", "&Open\tF7", NULL};
int   scribKeys[]  = { K_F1,          K_F2,        K_F3,       K_F4,         K_F5,        K_F6,          K_F7,     K_NULL};
int   scribSels[]  = {`sketch_`,     `line_`,     `box_`,     `erase`,      `save`,      `saveAs`,      `open`,         0};


mouseDnX_ x Y_ y
/* Method to respond to mouse DOWN events.    */
int x,y;    /* position in receiver where mouse down event occured  */
{
  if (self->drawClass)
   {
    @Notifier captureMouseFor_ self@; /* capture all mouse events to this window */
    @Notifier mouseTrkOn@;            /* enable mouse traking => mouse movement events */

    self->drawObj = @self->drawClass new@;  /* new instance of draw object */
    @self->model add_ self->drawObj@;     /* add to model */
    @self->drawObj setPort_ self->aPort@;

    return @self->drawObj mouseDnX_ x Y_ y@; /* pass forward mouse event */
   }
  else return FALSE;
}

open
{
   id fstr, str;

   fstr = @String newCStr_ "*.skh"@;
   str = @FileSelect ask_ "Select a Sketch file" for_ fstr of_ self@;
   if (str)
   {
      if (!!@self->model loadFrom_ str@) @str free@;
      @self update@;
   }
   @fstr free@;
}

paint
/* Draw all graphic objects contained by receiver in the objects ordered  */
/* collection.                                                             */
{
  int  sel = `drawOn_`;
  id retainedObjs;

  if (retainedObjs = @self->model asCollect@)
    @retainedObjs do_ sel with_ self->aPort@;

  return TRUE;
}

save
{
   if (@self->model getFname@) @self->model save@;
   else @self saveAs@;
}

saveAs
{
   id  fstr, str;

   fstr = @String newCStr_ "*.skh"@;
   str = @FileSelect ask_ "Select a Sketch file" for_ fstr of_ self@;
   if (str)
     {
      @self->model saveAs_ str@;
     }
   @fstr free@;
}


Example 1:  A generic shape method

*** Generic "shape" method for SketchView ***
<shape>_ aMenuItem
id  aMenuItem;  /* id of selected menu item */
{
  @self->fMenu uncheckAll@;
  self->drawClass = <ShapeClass>;
  @aMenuItem check@;
}