Copyright: ® 1995-1996, Polymorphic Software, Inc.


Help Contents
GF/ST

Introducing GF/ST

Begin At The Beginning

Basic Concepts and Classes

More Critical Players

Portability Considerations

Licensing

User-Supplied Goodies

Starting Out

Learning by Example

Using the Demo Launcher

Understanding the Demo Launcher

Using the Drawing Editor

Understanding the Drawing Editor

Using the 3-D Figures Demo

Understanding the 3D Figures Demo

Understanding the GFAboutDialog

Understanding the Visual Inspector

GF/ST Application Design & Decisionmaking

Designing a New Application

Creating and Opening the Network Editor UI

Decisions Made in Building the Network Editor

Adding a GO for a Network Node

Creating a GO in the Network Editor

Specializing Handles in the Network Editor

Removing Nodes in the Network

How To's

How To Use the How To's

Releasing Resources

Mostly GFGraphicObject-Related How To's

Mostly Handle-Related How To's

Event-Related How To's

Class Hierarchy, Protocol, and Events

GF/ST Class Hierarchy

Description of GF/ST Classes

Summary of Events

Appendix A - The Visual Inspector

What is the Visual Inspector?

Visually Inspecting Objects

Specific Inspectors for Specific Objects

Changing Object State

Visual Inspector Menu

Object-Specific Menu Options


Begin At The Beginning

We want you to get up and going quickly with GF/ST.  We believe that GF/ST is designed in a way that makes it possible to use it quite effectively in short order.  There is also nothing like having working applications in front of you to help when you're putting your own together, and that is why we've provided so many examples with GF/ST.  Any way you slice it, though, you're going to have to learn a few new things.  If we've done our job right, you'll master them quickly.  We want you to get past the how-do-I-do-this-stage and into the that-was-so-simple-I-have-time-to-do-something-even-better stage as fast as possible.

Related Topics:

What GF/ST Does For You

Installation

Conventions Used in This Document

What You Absolutely Must Know to Use GF/ST

How to Get Up to Speed Quickly

More Tips

Tools vs. Frameworks

Support


What GF/ST Does For You

GF/ST provides the framework that you need to build Smalltalk applications that go beyond the usual tables, lists, and buttons.  Don't get us wrong - tables, lists, and buttons are wonderful - but it's hard to visualize the complex relationships between objects using them.  It's even more difficult to manipulate objects and see the results of your changes when a list is the only view you have of the world.

One thing you do get with lists and buttons is a tried-and-true definition of the protocol for their use.  All of these objects are windows of some kind, and may have child windows themselves.  Windows provide you with a pre-built set of functionality to deal with opening, re-sizing, invalidation-based redisplay, and many other features you only become aware of when you deviate from the norm.

GF/ST brings these same benefits to the world of general graphic objects.  Graphic objects (or GO's) are not windows.  They are drawn within a window, and they hold state information that has to do with the graphical representation of some object.  For example, a GFRectangleGO might be used as the graphical representation of a Person in an organizational chart or a Node in a network.  The way the GFRectangleGO appears, the actions that are taken when you click inside of it, and the menu options that are available are all easily accessible and customizable in your application.


Installation

Please refer to the README.TXT file on the installation diskette for detailed installation instructions.  GF/ST is written entirely in Smalltalk, so in typical Smalltalk fashion, you must first install a set of files from diskette, and then get into Smalltalk and load them into your image.


Conventions Used in This Document

Because GF/ST is portable across Smalltalk implementations, we've combined our manual for all platforms into one.  This approach is possible only because of the amazing amount of common source code between platforms.  However, we've still had to get down to some platform specifics at certain places.  When that happens, we've flagged them in the manual using the icons that the vendors use:

This discussion is specific to ParcPlace-Digitalk Visual Smalltalk.

This discussion is specific to ParcPlace-Digitalk VisualWorks.

This discussion is specific to IBM Smalltalk/VisualAge.  We'll use the shorthand "VisualAge" to include IBM Smalltalk also.

Warning:  Are you looking at this in a Help file?  If so, you won't be able to see the visual clues we supply in the margin of the printed manual.  Sorry, but it is a limitation of the software we use to produce Help files.  You should be able to tell from the text where we're talking about platform-specific stuff.

Whenever we have something very platform-specific to say which we do not feel is appropriate for this document, we include a separate text file on the distribution media which is specific to your GF/ST implementation.  For example, under VisualWorks, there are some very specific instructions concerning printing compatibility, since VisualWorks printing supports PostScript and other modes, and VisualWorks users need to know what we expect.  See the README.TXT file for instructions about where this supplemental documentation can be found on the distribution media.

We also set any Smalltalk code and method selectors in a distinctive font, likeThis, so you can differentiate them from more normal text.


What You Absolutely Must Know to Use GF/ST

In this manual, we assume you are familiar with the Smalltalk language, basic class hierarchy, and the tools used in your particular vendor's environment.  GF/ST is remarkably portable across different Smalltalk implementations.  For example, it runs in ParcPlace-Digitalk Visual Smalltalk and VisualWorks implementations, as well as IBM's VisualAge and IBM Smalltalk implementations.  One way we achieve such remarkable portability is through our use of a common event model across all implementations.

You will use the event model to "hook into" things that happen to GO's.  For example, when you want to know when someone double-clicks on a GO, you will simply tell the GO to let you (or any other object) know by sending a message.  Those of you who use Visual Smalltalk probably already know about its event model.  If so, you need only look at the list of events that are triggered by various GO's to be up and running (see the Summary of Events section).  If you feel you need a quick refresher or if you are a VisualWorks or VisualAge user, read on, because unless you understand events, everything in GF/ST may look like it is happening by magic.

Related Topics:

The GF/ST Event Model


The GF/ST Event Model

In its broadest sense, GF/ST provides a framework for coordinating domain model objects, graphic objects, and user interactions.  This is a familiar problem, and a generic solution for it has existed in Smalltalk for a long time in the form of Model-View-Controller programming, or MVC.  MVC provides a way to couple objects loosely (i.e., loose coupling) via a dependency mechanism.  These are two object-oriented buzzwords that you should know and understand regardless of what Smalltalk you use.  For a more generic description of the problem and solution, you might want to check out the "Observer" pattern in Gamma et al's book.  The classic treatise on MVC is Krasner and Pope's paper "A Cookbook for Using the Model-View-Controller Paradigm in Smalltalk-80."  The details are well beyond the scope of this manual, but it is extremely useful to understand the concepts, as they are closely related to the GF/ST event model.  The general idea is this:

You are an object that models some part of the problem domain (i.e., a domain model), and I am an object that depends on your state somehow.  For example, I am a visual representation of your state (e.g., a view or a GO).  It is a bad idea from the standpoint of reusability for you to know how I am displaying you, or even that I exist at all.  On the other hand, it's okay for me to know about you by holding onto you as one of my instance variables.  We solve this problem using MVC.  I tell you to add me as a dependent.  You, as a good Smalltalk citizen, announce whenever your state changes in any interesting way.  Through the MVC dependency mechanism, I will receive a message to update myself.

You may not be aware of it, but MVC is implemented in all Smalltalk versions.  Events work in much the same way -- "Hey, the mouse moved!", "Someone resized me!", and so on.  The difference is that by using events, we can be much more specific about what has changed, what object should be notified, as well as the message and arguments that should be sent when an event occurs.

There are two sides to using events: registering events as a public interface for a class, and hooking into them so you can be notified when an event occurs.  When we create a class, instances of which will be triggering events, we publish a list of events that these instances will trigger in a class method called eventsTriggered.  This list lets the outside world know what kind of events our object will be triggering as it changes state (e.g., #getMenu, #button1DoubleClick, and so on).

When someone wants to be notified that your object triggered some event, they send it  the message when:send:to: with the event name (a symbol) as the first argument, a selector (symbol) as the second argument, and the receiver object for the selector as the third argument.  If the selector requires arguments, they use when:send:to:with: and include an array of arguments for the selector.

Instead of using the MVC-style changed message to notify dependents that they have changed, subclasses of PsiEventModel trigger events that describe the kind of change that has occurred, using one of the following expressions:

self triggerEvent: aSymbol

self triggerEvent: aSymbol with: aParameter

self triggerEvent: aSymbol withArguments: anArray

Any dependent object that wants to be notified of the event must notify the other object using one of the following expressions:

aModel when: aSymbol send: aSelector to: anObject

aModel when: aSymbol send: aSelector to: anObject with: aParameter

aModel when: aSymbol send: aSelector to: anObject withArguments: anArray.

By sending the message when:send:to: or one of its variations to aModel, you are setting up the dependency relationship, just as VisualWorks users are used to doing with onChangeSend:to:, or as any MVC programmer accomplishes using addDependent:.

GO's trigger a well-defined set of events which are documented in the "Class Hierarchy and Events" chapter of the manual.  For example, when you create a GO, you can say:

aGO when: #select send: #iHaveBeenSelected to: self.

Then, you will receive the message iHaveBeenSelected whenever a user clicks inside of aGO.


How to Get Up to Speed Quickly

This document is the logical place to begin.  Of course, nobody wants to read the manual, and we are no exception.  So, for the reading-disinclined, we suggest the following quick cruise:

1.	Read and understand the Begin At The Beginning section.  Hey, if you're reading this, you must be on the right track.  In particular, be sure you are familiar with Basic Concepts and Classes.

2.	Briefly try the example applications, see how they behave, and consider what parts are applicable to your application.  You may find user interface techniques you had not thought about or which you had discarded in your initial design process because of the difficulty.  When you look at the Drawing Editor, think of it as an example that shows the default behavior of GF/ST, not as a drawing tool.

3.	Unless you have questions about how to use some of the examples, move directly to Designing a New Application.  It uses a very simple Network Editor as a learning tool to show you how all the concepts hook together.  The first three or four sections should give you a good overview.


More Tips

Use the "How To's" section as a reference when you don't know where to look, when you're looking for advice, or when you can't find working code in the examples or the Network Editor that solves your problem.  We provide the GF/ST Class Hierarchy section for completeness - hopefully, you'll be using the Smalltalk browsers for most of this material.  One very useful thing you'll find summarized in that section is the events that GF/ST objects trigger as you use them.  Put off reading these sections until you need specific help.

Determine how you will use the basic building blocks of the framework within your own application.  Smalltalk applications come in an infinite variety of flavors, which is the reason that a good framework is necessary.  We've tried to make this process easy for you by providing a lot of discussion about the factors that should influence your decisions, as well as the "how to's" of using the framework.

Use the Visual Inspector™ to help understand how things hook together.  See What is the Visual Inspector? for information about how to use the tool.

If you find yourself moving very far beyond the "how to's" that we provide, you will need to start exploring the framework classes in more detail.  You need to understand the why behind GF/ST before you start making significant changes to the how.  The Visual Inspector is a key tool that lets you explore the interconnections between framework components.

When you look through GF/ST classes, you can consult the class method called COMMENT for a description of each class and what it is used for.  When we say "see the comments in" some class, we mean "look in the class COMMENT method".  In ENVY versions of GF/ST, you should look in the class comments using your favorite ENVY browser.


Tools vs. Frameworks

GF/ST is a framework, not a tool.  The Visual Inspector is a tool we provide with GF/ST that was built using the framework.  You do not need to understand anything about GF/ST to use the Visual Inspector.  If you want to build an application like the Visual Inspector, you must understand how to use the GF/ST framework.

As a general rule, tools built on good frameworks are easy to modify and extend.  Your end users may not see any difference between a working application built on a bunch of spaghetti code and one built on top of a good framework.  On the other hand, when you use GF/ST to build a graphical application, you won't be placed in that awkward position of having to explain to your boss (who was sold on Smalltalk based on re-use and ease of maintenance) how you have to go back and re-work everything to make one simple change because it violates a basic assumption you made early-on.

Frameworks are meant to be extended.  A good framework will minimize the amount of changes and extensions you need to make to use it in the context of your particular problem.  However, don't hesitate to add subclasses and methods within the GF/ST class hierarchy.  If you need a particular capability, add it in.  That's what a framework is for.  If you find you cannot implement the feature, or you had to perform massive surgery on classes and behavior we supplied, we need to know so that we can determine whether the basic GF/ST framework should be modified or extended.

For example, if you are working on an interface builder application, you may want to give your users the ability to position host components (like buttons) on the screen.  The Drawing Editor demo shows some examples.  We do not plan to support any more host components than the ones we use in the Drawing Editor.  If you need more, implement them.  For us, the only important issue with respect to host components is that we not make assumptions in the framework that would prevent people from writing applications that use them.  By implementing a few host components, we've proved to ourselves that there is no problem, and we've provided a simple road map showing how you can do it.


Support

When you purchase GF/ST, we commit to 60 days of basic support from the purchase date.  Because GF/ST is a very general framework and can be applied to so many different problems in so many different ways, we want to clarify our position with respect to basic support.  Basic support includes:

•	Help with installation problems

•	Bug fixes to Polymorphic-supplied classes

•	Clarifying the use of the framework when the documentation was inadequate or unclear.

Support does not include Smalltalk programming advice or general object-oriented analysis and design.  If you feel that you need help in these areas in general, or you need someone to come in and look at your problem and the ways in which GF/ST can be most effectively applied to it, we would be happy to do so on a consulting or extended support contract basis.  We can also refer you to qualified consultants in your local area.

We have made every effort in this document to provide the information you need to answer questions that begin with "How do I..." and "What is the best way to...".  When you hit one of these questions:

1.	Read the manual first and check the index.

2.	Look through the example applications to find how a similar problem is solved in them.

3.	If neither 1 nor 2 get you where you need to be, send us EMail.  Describe your problem in detail.  Refer to the README.TXT file for information on how to reach us via EMail.

We are always interested in feedback on the use of GF/ST.  Please let us know about your experiences in using it, any problems or inadequacies you find, and ways you extended it, so that we can continue to improve GF/ST and the documentation.


Basic Concepts and Classes

GF/ST introduces many new classes to Smalltalk, but there are only five you need to be aware of to get started, because they are central to any application built using GF/ST:

•	GFGraphicObject

•	GFDrawingInterface

•	GFDrawingPane

•	GFTool

•	GFHandle

To minimize the possibility of naming conflicts, you'll find these and all other GF/ST-specific classes added by Polymorphic prefixed with "GF".  When we add non-GF-specific classes (such as PsiEventModel), we prefix the class names with "Psi", for Polymorphic Software, Inc.  For clarity in our discussions, we'll retain the prefixes in all discussions in this document, unless we're referring to a generic object, like "a tool".

You will also find that GFGraphicObject is a subclass of PsiEventModel.  GF/ST uses an event model for the basis of communication and updating, and PsiEventModel class helps unify these concepts.  We've already discussed the fundamentals of the event model (see What You Absolutely Must Know to Use GF/ST), but we thought you'd like to know up front here that many GF/ST classes reside under PsiEventModel, a subclass of Object.

To use GF/ST, you do not need to understand how a GFGraphicObject works.  You can create whole new kinds of graphic objects without knowing the details of how the GFGraphicObject base class works.  It is, however, essential to know what classes you will be using and how they interact with one another.  In this section, we want to give you a broad-brush picture of the roles and responsibilities of key GF/ST classes.  We'll be getting to specifics of how to use them in the next sections.

Related Topics:

GFGraphicObject

GFDrawingInterface

GFDrawingPane

GFHandle

GFTool


Introducing GF/ST - Basic Concepts and Classes - GFGraphicObject
GFGraphicObject

If there is a central class in an object-oriented framework like GF/ST, GFGraphicObject would be it.  Often, we'll use the shorthand "GO" to refer to anything that is a kind of GFGraphicObject.  You may also catch us calling a GO a figure for historical reasons - old habits are hard to break.

A GO uses events to notify interested parties about things that happen to it.  Events are used to enforce constraints and to provide loose coupling between a GO and a domain model object that it displays.

You will use the standard GF/ST event protocol with graphic objects.  For example:

aGO when: #select send: #iAmSelected to: self

will cause aGO to send you the message iAmSelected when a user selects it.

A GO knows how to display itself, determine whether a point is contained within it, and whether an area intersects it.  A GO also generates handles and menus.  GO's are always contained by another GO, with one exception - GFDrawing.  GFDrawing is the graphic object that is the root container of all other GO's in a display; to be exact, its container is an instance of GFDrawingInterface.

Since a graphic object is responsible for displaying itself, it holds the protocol you must use when you are modifying it.  Whenever you are modifying the state of a graphic object in a manner that will affect the display, you must do so using the damageDuring: message:

aGO damageDuring: [ ... some code... ].

You will especially want to be sure to use damageDuring: whenever you move or change the bounds of GO's.  Think of damageDuring: as being the tool that you have to say "evaluate this code, keep track of damage to GO's, and update the display when you're done."

Graphic objects have a rich protocol for manipulating and accessing information about them.  Learning the common protocol and the events built into GF/ST will be one of your first steps.  The discussion in Designing a New Application is the best place to start.  Use the GF/ST Class Hierarchy section as a reference when you need it.

GFDrawing

A GFDrawing contains all GO's that are displayed and manipulated.  GFDrawing is itself a subclass of GFGraphicObject, in the great tradition of roots being a specialization of the internal nodes.  The drawing is responsible for managing the GO's under its control.  It coordinates the display of GO's in what is known as the draw loop.  The drawing collects all the damaged areas, and forwards them to the interface.  Given a point or area, the drawing is able to return the GO's containing the point or intersecting the area very quickly.  It keeps track of the Z-order of figures, and coordinates their display to maintain visual Z-order.  Given an area and a pen, the drawing will efficiently display only the figures intersecting the area, providing a key function in the speed of the display system.  The drawing is also responsible for keeping the definition of the global coordinate system for its components.

Generally, you will not be accessing a GFDrawing directly.  Drawings form a very critical piece of the GF/ST framework, but they are typically not of concern to you.  Instead, you will be manipulating either a GFGraphicObject held in a GFDrawing, or you will be manipulating and accessing its interface, a GFDrawingInterface.


Introducing GF/ST - Basic Concepts and Classes - GFDrawingInterface
GFDrawingInterface

A GFDrawingInterface manages the editing and manipulation of a drawing.  This class acts as the glue between all the major players of a graphical application.  It ties together the tools operating on the drawing, the handles generated by the drawing's components, and the physical display of the drawing itself.  The interface provides the manipulation functions because these functions are closely tied to the display of the drawing.   The interface provides a means of adding, removing, and grouping of GO's in the drawing.  For example, to add a graphic object to a drawing, you would create the GO and tell the interface:

interface addGO: aGO.

The interface deals with the low-level protocol for displaying the GO on the drawing.  It also keeps track of what GO's are currently selected, and it is in charge of displaying and hiding handles.


Introducing GF/ST - Basic Concepts and Classes - GFDrawingPane
GFDrawingPane

GFDrawingPane is a pretty simple class.  Its main responsibility is to provide a nice surface to display the drawing with.  This responsibility could be fairly large, but the GFDrawingPane uses a GFGraphicsDisplaySystem to do all the hard parts.  The GFGraphicsDisplaySystem is a class used internally by GF/ST, and it lets us factor-out behavior and state that should not be a Window's responsibility.  Reuse - wow, now we have a class that is useful in all versions of Smalltalk, since GFDrawingPane itself would not translate between the environments at all.  All the GFDrawingPane itself is concerned with is keeping Windows (that's the one with the ™ by it) happy, while redirecting user input to the interface of the display system.

You will probably be using the GFOwnDCDrawingPane subclass of GFDrawingPane in your applications.  The GFDrawingPane is used when you do not want to use GF/ST's double-buffered display.  As the name implies, double-buffering uses two buffers in the drawing process, only one of which is displayed at any given time.  It reduces the flicker associated with displays that use a draw-erase-draw cycle to the screen, because you don't see all the redrawing that goes on in the hidden buffer.  Double-buffering uses a few more resources than drawing without it.  If you are using GF/ST to build relatively static graphical displays, you should probably use the GFDrawingPane.  Otherwise, use the GFOwnDCDrawingPane.  We believe that most applications will use the GFOwnDCDrawingPane.

Note that the GFOwnDCDrawingPane uses a GFDoubleBufferingGraphicsDisplaySystem as the internal class that does all the hard parts of the display operation.  The GFDrawingPane uses the GFGraphicsDisplaySystem.  Both of these classes are considered to be private to GF/ST.

When you create an application that uses GF/ST, you will create an instance of GFDrawingPane or GFOwnDCDrawingPane, connect it to a GFDrawingInterface, and be on your way.


Introducing GF/ST - Basic Concepts and Classes - GFHandle
GFHandle

All direct user input to graphic objects comes in via either a GFHandle or GFTool.  Even things like "move" operations on a GO, which take place when you hold down the left mouse button within a GO but outside of a handle, are performed by creating a handle on the fly.

Because handles are so central to graphical applications built using GF/ST, you would expect them to be extremely flexible in the way they can be used.  One way to get this flexibility is to build many specialized subclasses of a generic GFHandle class; however, if you examine the hierarchy, you will find this is not so.  Instead, GFHandle is heavily parameterized.  When we use a word with that many syllables, it's a sure sign that it will be very easy to get the behavior you want out of a handle, but it will be very hard to describe how we accomplish it under the covers.  Fortunately, this kind of situation is handled (pun intended) quite effectively through the use of examples.  You will find many examples of different kinds of handles in subsequent sections and in the example applications.

A handle's main responsibility is to mediate mouse input, and using that input, to manipulate a GO in some fashion.  Handles have a locator (see GFLocator), which tells them where they are located.  Handles are also responsible for displaying themselves.

At first glance, it might seem that handles are a kind of GFGraphicObject.  We have chosen not to architect the framework in that way for several reasons.  First, handles are fairly lightweight in the display department.  If you need handles that are not lightweight, you can always give a handle a GO to present.  Secondly, handles are transitory.  They are generated and removed frequently.  They are also always displayed on "top" of the drawing.  There is no concept of a handle being hidden by a GO.  Finally, handles are logically part of the interface, not the drawing.

You will probably not need to modify, extend, or subclass the handle classes we provide.  If you do, you can examine some of the variations in GFHandle, like GFBorderHighlightHandle and GFObjectReferenceHandle that are used in the Visual Inspector.  The ones you will be using typically are GFTrackHandle (for tracking mouse input) and GFConnectionHandle (for connecting GO's).


Introducing GF/ST - Basic Concepts and Classes - GFTool
GFTool

A tool represents the current mode of manipulation of the drawing.  For example, you will be using a GFSelectionTool when you are in a "selecting" mode, and you will be using a GFCreationTool when you are creating new GO's directly.  Many developers have faced the dilemma of how best to hold onto the "state" of a graphical application.  Hmm, do I put it in the window, in the application...  The answer in GF/ST is that you tell the drawing (by sending a message to its interface) to set the active tool to a specific one that is appropriate.

Tools coordinate closely with the interface currently editing the drawing.  The tool determines what will happen in a drawing.  Since the tool is the focus of all input events, the tool can filter these events as it sees fit.  Thus, the tool can directly manipulate GO's in the drawing, or direct the handles of GO's to manipulate the figures.  Although simple in behavior, the tools provide an incredibly useful function that would be difficult to provide otherwise.


More Critical Players

If you take a look around the GF/ST class hierarchy, you'll find a number of other classes beyond GO's, tools, and handles that are critical to the framework.  Now that you know what the basic classes are, let's explore some of the other areas of the framework that you will encounter.

Related Topics:

PsiEventModel

Constraints

GFLocator

GFPositionConstraint

GFTrackHandle


Introducing GF/ST - More Critical Players - PsiEventModel
PsiEventModel

As discussed in What You Absolutely Must Know to Use GF/ST, if you don't understand events, then everything you see in GF/ST may appear to be done by magic.  The event system is the wiring grid of the system.  The use of events makes GF/ST incredibly flexible and powerful, but it also means that  the flow of control can be hard to follow.  It can be difficult to follow (just as the change-update mechanism in MVC can be) because the dependency relationship is set up long before the event is triggered and acted upon.

GO's generate many different kinds of events.  See GF/ST Class Hierarchy for a compact summary of the events that GO's trigger.  To aid in event processing efficiency and to avoid garbage collection problems, we keep track our own event handlers.  We want to re-emphasize here that understanding the event model is central to understanding how GO's are hooked up with each other, and how constraints are maintained in GF/ST.

Events also provide a wonderful way to separate a GO from the model it represents.  For example, retrieving the menu for a GO is taken care of with an event.  If the event isn't handled, then a default menu is supplied.  However, because the object triggers the getMenu event, you can use any appropriate object to generate and return the menu when it is needed.


Constraints

Constraints are GF/ST's mechanism for defining and enforcing relationships between GO's.  Smalltalk supports the concept of constraints via dependency.  In Visual Smalltalk, developers typically use the event model to set up constraints:

anObject
when: #stateChanged
send: #newState:
to: me
with: state.

In GF/ST, we model constraints with - well - objects, of course.  GF/ST constraints are all subclasses of EvaluableAction.  When we have specific kinds of constraints that require new behavior and/or state, we implement a subclass of EvaluableAction.

Constraints in GF/ST are driven by events raised by the GO's themselves.  Constraints can be attached to any event triggered by a GO.  A common occurrence is to attach constraints to the damaged event that is raised by all figures.  Whenever a figure is damaged, the event is triggered, causing the constraint to be evaluated.  This approach is used to ensure that any dependent affected by the constraint is kept up to date when a related aspect of the GO changes.

Constraints that have to do with the location of GO's work together with GFLocator objects.  For example, when you connect objects, you will see the connection line snap to a predefined location on the target GO.  This behavior is accomplished with a GFLocator.  We'll talk about special kinds of constraints after we know what a locator is.


Introducing GF/ST - More Critical Players - GFLocator
GFLocator

A GFLocator is a kind of Message that, when evaluated, answers a point.  A GFLocator holds onto a receiver, a selector, and arguments.  When it is evaluated (by sending it the message value or asPoint), the receiver is sent the selector, with the optional arguments.  This receiver's method (defined by the selector) must return a point, which is the value of the locator.  VisualWorks users will recognize this approach as being analogous to an AspectAdaptor.

Consider a case where you want to keep track of the center of a GO.  You would create a locator with the receiver object set to the GO being tracked.  The selector would be the symbol #center.  There would be no arguments in this locator.  When the locator is sent the message asPoint, it is evaluated, sending the message center to the GO.  All GO's understand the message center, answering the center point of the GO.  There are many such helper methods defined on GO's, usually at the top level in GFGraphicObject itself.  For instance, topLeft, topRight, leftCenter, and rightCenter are all available.

You will probably find yourself using a GFLocator at times in your applications.  The most likely case will be when you need to identify a point in a GO, perhaps to locate a special handle.  For example, if you wanted a locator fixed at the point 10@10 off the origin of a GO, you would create a locator as follows:

GFLocator
receiver: aGO
selector: #offOrigin:
arguments: (Array with: 10@10).

Or, alternatively:

GFLocator
on: aGO
at: #offOrigin:
with: 10@10.

which does exactly the same as above.  When you send the message value to the locator (i.e., you "evaluate" it), it would send the message offOrigin: to aGO with the argument 10@10.  The result would be a point which was 10@10 off from the origin of aGO .  Refer to Specializing Handles in the Network Editor for an example of using a locator.


Introducing GF/ST - More Critical Players - GFPositionConstraint
GFPositionConstraint

A GFPositionConstraint is a kind of Message that when evaluated, sends a setting message to an object with the point represented by a locator as the argument.  For instance, if you wanted to keep the start position of aLineGO fixed to the origin of anotherGO, you would do the following:

GFPositionConstraint
location (GFLocator on: anotherGO at: #origin)
receiver: aLineGO
sending: #startPoint:.

This would return an instance of a GFPositionConstraint that, when evaluated, first gets the point represented by the locator.  (See the discussion in GFLocator for a description of how the locator produces this point).  This point is then sent to the receiver, aLineGO, as an argument to the method startPoint:.

Will you ever use GFPositionConstraints?  You will be using them whenever you connect objects, but you may never have to deal with the internals of them.  In fact, we use them in GFDependentLineGO's, which are the lines that connect objects in a display.  These dependent lines use GFPositionConstraint's to determine their start and stop points rather than simply holding onto instance variables.


Introducing GF/ST - More Critical Players - GFTrackHandle
GFTrackHandle

One commonly used specialization of handle is the class GFTrackHandle.  This class encapsulates the handle behavior of tracking a mouse.  When invoked, this class will track the mouse in steps.  When the mouse changes position, a two-step process is invoked.  The handle first evaluates the sense action with the delta between the last position and the current position as its argument.  The result of this message is then used as an argument to the change action.

The design of GFTrackHandle allows it to generate objects (which don't have to be points) based on the mouse tracking, and to feed these generated objects into some change action that changes the GO's state.  Obviously this approach is very powerful.  For instance, the handle that changes the border color of a GFAbstractPathGO uses the sense action to return just the vertical delta of the mouse position.  Using this point, the handle changes the border color of the GO by evaluating the action borderDarkenBy: with the GO as the receiver and the vertical delta as its argument.


Portability Considerations

The initial release of GF/ST was for Visual Smalltalk 3.0.1, under Win32.  You may have detected from the GF/ST architecture that it is designed for portability.  From a developer's point of view, you must know what protocols you can rely on to be present in other implementations.  We have attempted to flag all private methods in their comments with the word "Private", and this should be a clue to you that you are using or modifying a method which we reserve for our internal use.  You can count on base GF/ST classes and protocol being available in Visual Smalltalk, VisualWorks, and VisualAge implementations.

Since events are so central to GF/ST, you should also rest assured that the when:send:to: style of hooking up to events will be present no matter what Smalltalk platform you deploy your application on.  Similarly, you should expect the same events to be triggered at the same times regardless of Smalltalk version.

Related Topics:

Why Do You Use That Syntax?

Visual Smalltalk Platform Dependence


Why Do You Use That Syntax?

GF/ST modifies no base image methods on any of the Smalltalk platforms.  We have also factored-out vendor-specific code wherever we can.  In fact, GF/ST has about 85% common source code between Visual Smalltalk, VisualWorks, and VisualAge implementations.

In our experience, most end-users are only marginally concerned with Smalltalk-vendor independence in their application implementation.  This situation arises for two reasons:  first, you've committed to a vendor already; and second, your knowledge of the details of something like Visual Smalltalk versus VisualAge is not enough to let you make conscious decisions regarding writing portable code.  The latter issue may be minimized as the ANSI standard for Smalltalk emerges.

A side effect of our efforts to gain maximum portability across platforms is that our code may look overly indirect.  For example, you can find GF/ST code that looks like this:

findTargetAction := self class gfMessageClass
receiver: self
selector: #graphicObjectIn:at:.

In Visual Smalltalk, we could have written:

findTargetAction := Message
receiver: self
selector: #graphicObjectIn:at:.

In VisualWorks, we could have written:

findTargetAction := MessageSend
receiver: self
selector: #graphicObjectIn:at:.

In VisualAge, we could have written:

findTargetAction := GFMessage
receiver: self
selector: #graphicObjectIn:at:.

In the latter case, GFMessage is our subclass of the base IBM class DirectedMessage, which we implement on that platform for protocol compatibility.  You can see that our porting job between platforms now amounts to re-implementing the gfMessageClass method, which you'll find in Object.

Should you write code like this?  It depends on whether you care as much as we do about portability.  If so, go ahead.  You'll find some helper methods in Object that look like gfSomethingClass which we've found useful.  If not, you certainly won't hear any complaints from us!


Visual Smalltalk Platform Dependence

The inversion of coordinate systems between Windows and OS/2, and Digitalk's decision (well, the decision was made before they became one business entity with ParcPlace) to adhere to the native approach in the Smalltalk image, is probably the biggest issue you will face when porting a graphical application from Windows to OS/2 and vice-versa.  In practice, you may not be able to adhere to the common protocol defined by Digitalk (e.g., rightAndDown: instead of + for Points).  This is particularly true in problems involving layout, where you must depend on Point arithmetic instead of artificial non-arithmetic protocol.  In these cases, you will face a porting task, and the GF/ST framework will offer only minimal shielding from a problem which is not under our control.  We have not invaded base image methods like + in Point class - you will just have to deal with the Visual Smalltalk style of rightAndDown:.  None of these problems are an issue in other implementations of Smalltalk.

GF/ST uses a class called GFCoordinateControl to help handle the underlying usage of the native coordinate system on the various platforms.  See the extensive comments in that class for more information.  Note again that both VisualWorks and IBM Smalltalk/VisualAge use a consistent coordinate system within Smalltalk across all supported platforms and operating systems.


Licensing

GF/ST software is copyrighted and owned by Polymorphic Software, Inc.  GF/ST is written entirely in Smalltalk and is licensed to you with full source code upon purchase and acceptance of the Polymorphic License Agreement that accompanies its delivery.   Details of licensing and distribution of applications based on GF/ST are provided in the Polymorphic License Agreement that accompanies the product.

You must purchase one license per developer.  Within the limitations of your Smalltalk vendor's license agreement, you may distribute runtime applications developed using GF/ST without paying any royalties for the use of GF/ST to Polymorphic.  You may not distribute any source code provided with, taken from, or derived from GF/ST without express written consent from Polymorphic Software, Inc.

Want to use GF/ST in an application you're delivering in a Smalltalk development environment?  We're happy to license it to you for that purpose, subject to negotiable royalties.  In that case, we can arrange for you to distribute it with or without source.


User-Supplied Goodies

GF/ST is ripe ground for clever users to do new things with.  You'll be building applications with GF/ST, and we'd love to hear about them and see the finished products.  Along the way, however, you may develop a new WhatsIt thing that others would love to have.  Please, send it to us!  We will constantly be updating our library of user-supplied goodies and supply them with GF/ST.  If we choose to supply your contribution as a standard part of our release, we'll leave your copyright notices on any code you supply, and put your name in lights for new GF/ST users to see.


Learning by Example

We provide a number of examples with GF/ST:

•	Demo Launcher - Used to launch the example applications.  The launcher (actually, the class GFDemoLauncher) uses GFButtonGO's to launch the applications.

•	About Dialog - The GFAboutDialog displays the propaganda information about the demo as well as the rotating tensegrity picture.

•	Drawing Editor - A kind of "catch all" application that lets you try out new GO's you might create.  You can access all the basic GO's supplied with GF/ST from the floating palette.  The Drawing Editor (actually the class GFDrawingView) shows the default behavior of all base GF/ST GO's.

•	GFThreeDEditor - A good example of coupling underlying domain models (of 3-D figures in this case) with GO's.

•	PsiVisualInspector - A new tool for your toolbox.  We expect you'll start using this inspector instead of the standard ones in Smalltalk, especially since you have them built into it, too.

These applications are all well-commented, and we expect you to browse through them to learn how they solve different user interface issued in GF/ST.  We'll be using them extensively in the How To Use the How To's to refer to specific chunks of code.

Also, don't forget to check in the "goodies" supplied with GF/ST and which are available for download from our web site:

http://www.polymorphicSoftware.com

Some of these goodies will also provide useful code for mining in your application and discussion about their implementation.

For completeness, we'll include a description of how to use each of the major demonstration applications.  In them, we'll explain the basics of what a GFHandle looks like, and so on.  You may find it easier just to open them up and use them, and then refer back to the appropriate discussion in the manual when you need to.  The exception to this approach is the Visual Inspector.  There is a lot of functionality built into the Visual Inspector which you may discover by playing with it, but which might be easier to read about.  For example, you can assign objects into instance variables using the connection handles, but it's not obvious from looking at it.  For details on using the Visual Inspector, refer to What is the Visual Inspector?.

Following each "Using the..." section, we'll provide a brief overview of how each of the applications works.  These sections should give you a good road map for exploring the applications on your own, explaining the approach which was taken and any key design decisions made along the way.

Probably the most important learning-by-example tool we provide for people starting out is the Network Editor.  It is a simple application, it's easy to understand what it's doing, and it illustrates all the basic concepts you need to know to get up and going.  It forms the basis of an entire chapter: Designing a New Application.


Using the Demo Launcher

The Demo Launcher opens automatically when you install GF/ST.  It shows four buttons from which you can launch the individual applications, and a Help button.  We use the Demo Launcher in the runtime demonstration application as the user's only entry point into the application, a typical Smalltalk application program approach.  In runtime, we open the About Dialog first, and then the demo launcher, so if you close your Demo Launcher and then re-open it, you will be presented with the About Dialog before the launcher opens.

To open the Demo Launcher, evaluate:

GFDemoLauncher open.

It should look something like this, depending on what options you have installed:

The four buttons on the left open the Drawing Editor, the 3D Figures Demo, the Visual Inspector, and the About Dialog respectively.  The last button opens the Help file for GF/ST, or for the runtime demo if you are in runtime.  On-line Help is only available under Windows.


Understanding the Demo Launcher

The buttons in the Demo Launcher are instances of GFButtonGO.  We created GFButtonGO because it was actually easier than dealing with the Visual Smalltalk interface to Windows buttons that show bitmaps, DrawnButton.  If you use WindowBuilder under Visual Smalltalk or VisualAge, or the UIPainter under VisualWorks, someone has already done the hard work for you.  In GF/ST, we want to provide you with the low-level framework to build emulated widgets and to use host components.  We use the GFButtonGO to demonstrate that emulated widgets are simple to make in GF/ST.  You shouldn't be able to tell any difference between GFButtonGO's and standard Windows-style buttons.  You can check the differences out for yourself using the Drawing Editor's Host Components category of GO's to place a GFHostWidgetGO button next to a GFButtonGO.

In Visual Smalltalk, the GFDemoLauncher was built as a subclass of ViewManager.  In VisualWorks it is a subclass of ApplicationModel, and in IBM Smalltalk it is a subclass of GFApplicationWindowES.  They all follow a generic approach for opening a GF/ST application, which is described in detail for the Network Editor (see Creating and Opening the Network Editor UI).  The specific methods for the GFDemoLauncher are:

GFDemoLauncher>>open
GFDemoLauncher>>initInterface

You can explore these methods yourself.  The other methods in the class are for taking action when a button is clicked.


Using the Drawing Editor

This section discusses how to use the Drawing Editor.  The Drawing Editor is a kind of "catch all" application that lets you try out new figures you might create.  It also illustrates the default behavior of all GO's supplied with GF/ST.

When we talk about the "Drawing Editor", we're mainly talking about the class GFDrawingView.  Because the GFDrawingView is a very basic tool, we included it as part of the base GF/ST framework.

From the Launcher button in Visual Smalltalk, you're using the GFDrawingViewWithStatus, a version that includes status panes  at the bottom.  Status panes are an "extra" in Visual Smallalk.

In VisualWorks, you'll be using the GFDrawingEditor instead of the GFDrawingView.  Use of the word "View" appended to a class name is standard for subclasses of ViewManager in Visual Smalltalk, but it implies the wrong thing to VisualWorks users.  In this document, you will find us referring to GFDrawingView class, and if you're a VisualWorks users, you'll just have to bear with us and think:  GFDrawingEditor.

You can open the Drawing Editor from the Demo Launcher, or by evaluating:

GFDrawingView new open.	"In Visual Smalltalk"
GFDrawingView open	"In VisualAge"
GFDrawingEditor open	"In VisualWorks"

The platform-independent alternative, used from the GFDemoLauncher is:

GFDrawingInterface open.

The latter approach to opening the Drawing Editor emphasizes the role of the GFDrawingInterface in GF/ST.  It glues together all the parts of a GF/ST direct manipulation application.  The GFDrawingView is only used to coordinate the multiple windows of the Drawing Editor itself, the one showing the GFDrawing and the one displaying the palette, discussed next,

There is a slight delay the first time you open the Drawing Editor, as we cache the bitmap and other resources from files.

Related Topics:

Basic Drawing Skills

Menus

Handles

Moving a Graphic Object

Multiple Selections, Grouping, and Composites

Front-Back Ordering

Undo

Host Components

Performance


Basic Drawing Skills

Most things about the Drawing Editor should feel familiar to you.

There is a main window to draw-in, labeled Drawing, and a floating palette window.  The items on the left of the palette are used to choose the category of tool you are using.

When you select an item on the left, the tools available in that category update.  You always have a selection tool available at the top no matter what category you choose.  Note that as you select categories and tools in the palette, the status pane at the bottom of the drawing window updates to let you know what's going on.

The exact look of the Drawing Editor and the palette will vary slightly between Visual Smalltalk, VisualWorks, and VisualAge.  For example, there is a multi-draw button on the palette in VisualWorks to be consistent with the UIPainter style.  We have also included scaling and grid options in the menus.

The concept of a tool is central to GF/ST, as is the concept of handle.  For an overview of the concepts, refer to Basic Concepts and Classes.  For details of how you will be using tools and handles in your own application, you can start with the chapter on Designing a New Application.  Right now, we're trying to concentrate only on how the Drawing Editor behaves without getting hung up in too much detail.

Each tool may have a different behavior for drawing.  For example, you draw rectangles by holding down the left mouse button and dragging.  You draw polylines by clicking with the left mouse button at the endpoint locations of each line segment, and you double-click when you're done.  Try them out.  Actually, this behavior is all taken care of by using different instances of the GFCreationTool.

When you're done using the drawing tool you selected, the Drawing Editor automatically switches you back to the selection tool.  The thing you created on the screen is a kind of GFGraphicObject; i.e., it is an instance of some subclass of the GFGraphicObject class.


Menus

Each graphic object on the screen can have its own specialized pop-up menu.  You don't have to select the object to bring up its menu.  Just position the cursor over the object and click the right mouse button.  Try some of the options to see how they work.  Line graphical objects (or more generally, path figures) also have their own pop-up menus - you just have to aim well when you pop them up.  Check out the different behavior of the various types of graphic objects.  Where do these menus come from?  They are either inherited, or you can simply use the standard getMenu event to get the menu from anyone you want.  See the discussion in How To's sections Setting the Menu of a GO and  Setting the Menu of a Drawing for details of using and disabling menus in GF/ST.


Handles

Each graphic object can have its own specialized set of handles.  To be technically accurate, graphic objects do not know what handles they have - they simply trigger the event #generateHandles, and any object can return the appropriate handles.  The Drawing Editor demonstrates the default handles which are provided when the event is not handled.

Handles are typically drawn as little black boxes in an appropriate location.  When you select a handle, the graphic object takes some appropriate action, such as allowing you to move a corner when you select a corner handle.  Handles can do pretty much anything you want.  For example, the default handles on the GFRoundedRectangleGO look like this:

Note that when you select a handle, the status pane updates to show you what the handle is used for.  You can use the descriptions we provide, or you can put in your own.  The descriptions and the coupling of the events to status pane display makes a useful and unobtrusive "hint" tool for your end users.

You can use the corner handles to resize a GO.  When you select the border width handle, the pixel width of the border is adjusted as you move the mouse vertically.  The corner radius handle adjusts the sharpness of the rounded corners.  You can connect two graphic objects using the connection handle.  Connections are a particularly difficult aspect to deal with generally in a graphical application.  When you connect two objects, you'll find that the line connecting them is another graphical object that is constrained to move with the objects it connects - a GFDependentLineGO.  The connecting line has its own pop-up menu, letting you change it from a line to an arrow, or to an orthogonal path.  Any, all, or none of these handles may be useful to your application, so we provide powerful ways to choose, display, and otherwise manipulate handles.  See the discussion in Specializing Handles in the Network Editor and the "How To's" for more details.


Moving a Graphic Object

To move a graphic object, select inside of it but not over a handle.  Hold the left mouse button down and drag to any position.  The Drawing Editor automatically scrolls when you move outside of the currently visible area.


Multiple Selections, Grouping, and Composites

You can select more than one graphic object by holding down the shift key as you select them.  Alternatively, you can "rubber-band" around multiple objects when are using the selection tool.  Once you have more than one object selected, you can create a group or a composite using the "group" or "compose" menu options.  Grouped objects can only be manipulated as a whole, while composite objects are positionable within the composition area on the screen, and can be moved as a whole.  Try them out.  It's easier to see the difference when you play with them.

Groups and composites are one of the most powerful tools in GF/ST, since they let you compose arbitrarily complex graphic objects from very basic ones.  See Creating a GO in the Network Editor for a discussion of using groups and for guidance in how to decide between groups and composites.


Front-Back Ordering

You will find out as you move objects that they are implicitly aware of their position front-to-back relative to one another.  You can change their ordering using, of course, an ordering tool.  We provide two position tools - one to move a figure to the top, and one to move it to the back.  They are both instances of GFGOActionTool.  The tool holds onto an action block that is evaluated for the selected GO's in a view.  You should feel free to get more creative.

Here's a fun thing to try.  Create five or size graphic objects like rectangles.  Make them different colors using the fill-color handle or the pop-up menu.  Overlap them slightly, top to bottom as shown below:

Select every other one top to bottom using the shift key as you select the individual objects.  Now, letting go of the shift key, hold the left mouse button down while it is positioned somewhere inside of a selected figure and move the group you've selected.  You do not have to group them to move them all together, just have multiples selected.  The front-to-back ordering is maintained as you move them, and you'll see the selected figures hide behind the ones you did not select as you move the group.


Undo

The undo operation in GF/ST is very powerful.  We use a "memento" approach, and it is generalized for use in any of your applications, with or without GF/ST.  Give it a test by undoing up to fifteen things you've done.  We chose to limit the size of the bounded stack tracking undo-able actions to fifteen in the Drawing Editor, but you can set it to be unlimited or to any size you want.  See the comments in GFBoundedStack for more information on the undo stack itself.

Undo is difficult to achieve in Smalltalk without violating encapsulation - how do you undo an operation on some object or an operation done by an object on itself, without having access to protocol or state you should not know about?  The approach in GF/ST uses the "memento" pattern discussed in Miscellaneous Topics in Miscellaneous Topics of Mostly GFGraphicObject-Related How To's.  Check out the GFMemento class and the GFDrawingView, where it is used, for details.  The comments in the GFMemento class provide a discussion of how the memento pattern has been implemented in Smalltalk.  You should find the GFMemento class to be useful in non-GF/ST applications as well.


Host Components

We've thrown in a few host components, or widgets, for fun.  All host components are instances of  GFHostWidgetGO.  A GFHostWidgetGO can hold onto any kind of host component.  We've included host components in the Drawing Editor for Button, List, Text, and EntryField.  We included our own text figure in the host components category even though it is not a host component like static text would normally be.

True host components always show up on the top of a drawing because the underlying window system doesn't understand how to deal with the non-host components like rounded rectangles.  They also don't display quite as smoothly when you manipulate them as the other components because the host windowing system that is drawing them isn't as good at it as we are.  Specifically, we use a double-buffering technique in GF/ST, as discussed in the GFDrawingPane section of Basic Concepts and Classes.

Under Visual Smalltalk and VisualAge, the GF/ST host components are real Windows components.  Under VisualWorks, they are true VisualWorks widgets.  In the Drawing Editor, we've elected to make them active as soon as you unselect them, or select something else on the screen.  In other words, if you draw a button on the screen, click on something else, and then click back on the button, it behaves just like a real button, since it is a real button.  The consequence of this choice is that you need to use rubber-banding to re-select a host component once you've drawn it.  If you were developing an application for building user interfaces, you would probably want to be able to switch between a building mode and a test mode to avoid confusion.


Performance

See any flashing as you manipulate graphical objects?  We've spent a lot of time optimizing display performance.  Also see the discussion in the GFDrawingPane section of Basic Concepts and Classes for more details.

Notice major any degradation of performance as you add tons of graphic objects?  We use a GFQuadTreeElement approach to be able to handle large numbers of objects.  Most drawing programs use outlines as you manipulate objects on the screen.  The problem with outlines is that you cannot really see how your object will look when you get done modifying it.  In addition, the performance of many drawing packages will degrade rapidly as complexity increases.  The reason for this degredation is apparent when you consider what goes on as you drag an object across a complicated display of other objects, maintaining knowledge of z-ordering.  Even if your application ignores z-ordering, you still need to determine what objects on the screen are affected by your changes to another object.  Conceptually, it is not hard to do, but in practice, simple approaches make dragging, selecting, and scrolling become your biggest performance bottleneck.  GF/ST's quad-tree approach manages the spatial complexity to maximize performance.  See the discussion in Miscellaneous Topics in Miscellaneous Topics of Mostly GFGraphicObject-Related How To's for additional discussion.


Understanding the Drawing Editor

Now that you've looked at the Drawing Editor, let's take a closer look at how GF/ST works under the covers.  We'll divide our discussion between the topics of:

•	Communication

•	Handle Concepts

•	ToolConcepts

By looking through the class GFDrawingView, you can gain some appreciation of what we mean by our earlier statement that the Drawing Editor simply shows the default behavior of graphic objects.  The GFDrawingView class does almost nothing.  It is comparable in complexity to the GFFloatingPaletteWindow you use to select tools from the various categories.  All the work is done by the underlying GF/ST framework classes you were introduced to in Basic Concepts and Classes.

Related Topics:

Communication

Handle Concepts

Tool Concepts


Communication

GF/ST provides clear paths of communication between:

•	what is displayed on the screen

•	the domain model objects of your application

•	user input.

Communication may be required between any and all of these components.  For example, the GFAboutDialog shows only communication between domain information (the position of the 3-D figure in 2-D coordinates) and what is seen on the screen - there is no user interaction.  The Drawing Editor is an example of an application that does not have any identifiable domain model information; e.g., a rectangle in the drawing is not displaying the state of some other interesting object.  Everything that happens in the Drawing Editor is held within the drawing that is created.

You do not need to (and are hereby actively discouraged from doing so) maintain direct references between the objects taking part in this interesting dance, since we use an event model to communicate between them.  See the discussion in What You Absolutely Must Know to Use GF/ST for background on the event model and a contrast with the Model-View-Controller approach.  We believe that GF/ST takes the good parts of both MVC (loose coupling and factoring) and a modern event-driven approaches.


Handle Concepts

The information that is being created as you manipulate the graphical objects in the Drawing Editor can be directed anywhere and dealt with in any manner that is appropriate to your application.  All direct interaction with the graphical objects is done through a handle (an instance of GFHandle or one of its subclasses).  In the Drawing Editor, the handles (GFTrackHandles actually, which appear by default as black squares) send the information directly to the graphical object which responds accordingly.

Select a rectangle in the Drawing Editor.  The handles on the four corners transport information about the position of the corners of the rectangle.  When you drag one of these handles, it sends the growBy: message to the graphical object.  When you hold down the left mouse button in a graphical object and you are not on a handle, a GFSelectionTrackHandle is created dynamically that transports information about the changes in position to the graphical object.

You can see that handles provide the central interface for input from the user in a GF/ST application.  They take input from the user and route it to the appropriate object.  Since handles play such a key role, you'll need a powerful way to define and control what handles show up, what they do, and what they look like.  Refer to the discussion in Specializing Handles in the Network Editor for detailed discussion of various handle protocols, as well as the "How To's".  The 3-D Figures demonstration shows some even more interesting variations on handles which we will be discussing in that section.


Tool Concepts

You are always using a tool of some kind in the Drawing Editor.  When you are selecting objects, you are using the GFSelectionTool.  When you are creating a figure, you are using a GFCreationTool of some kind.  In the Drawing Editor, you select a tool from the palette.  In another application, the state of the application may dictate the tool which is used.  For example, the GF/ST Launcher only uses a tool that allows figures to be selected, but which disables the drag-select (rubber-banding) that is available in the Drawing Editor.

We expect tools to be the part of GF/ST which will be the least-modified by users.  Tools are very important to GF/ST applications, but the ones supplied with it should solve most general problems.  When you find you need some specialized behavior for a tool, though, you should just have to make slight adjustments to the ones we supply.

An aside for those readers interested in some historical design perspective.  Traditional Model-View-Controller architecture depends on a triad of the domain model, and a view-controller pair.  Visual Smalltalk modifies the MVC paradigm slightly.  The NotificationManager provides the event dispatching function of the controller, pushing the remaining responsibility of the now-defunct controller to the view.  Under IBM Smalltalk and VisualAge, the same effect is accomplished using the OSEventHandler class.  The result is that in a pure MVC-based approach, there is one controller for every view, while in Visual Smalltalk and VisualAge, there is one controller in the entire system.  In GF/ST, the tool takes the place of the controller, providing a one-to-many relationship between the currently active controller (tool) and the views (figures).  This approach provides tremendous flexibility over the event stream that the figure ultimately sees.


Using the 3-D Figures Demo

The 3-D Figures demo is not designed to be a general 3-D drawing editor.  It was born from a desire to illustrate ways to couple together domain model classes with a graphical display.  We'll be discussing just what that means later on.  For now, let's just stick to what the demo does.

You can open the 3-D Figures demo from the launcher or by evaluating:

GFThreeDEditor open.

There are only three options available in the demo, and they are presented to you as three buttons at the top of the screen.  The buttons themselves are host components, (actually, instances of GFHostWidgetGO) and we've disabled your ability to move and otherwise manipulate the buttons.  The kinds of GO's the buttons produce are shown below each button.  Note that on some platforms, we've added in buttons to toggle between "Animate" and "Stop" and to "Clear" GO's from the screen.

When you select a GO, it shows a central handle that you can use to rotate it, or more specifically, modify the pitch and yaw of the object.  The X, Y, and Z labels are actually handles, too.  They allow you to adjust the X, Y, and Z size of the object when you hold down the left mouse button over the handle and drag.


Understanding the 3D Figures Demo

You may already have existing classes that are used in your application.  To use GF/ST, you will need to understand how to hook these classes into the graphical display tools in the GF/ST framework.  The 3-D Figures demo is a good example of how to do just that.

Related Topics:

More About Handles

Composites and Grouping

Dependent Lines

More About Menus


More About Handles

Add and select a cube in the 3-D Figures Demo.  The displayed cube actually has four handles.  One is the black square in the center that looks like the standard handles generated for any GO.  The other three handles are the letters X, Y, and Z.  When you use any handle in this demo, the handle sends messages to a separate instance of an interface class, which then manipulates an instance of a cube.  In this scenario, the cube is considered to be the domain model - a cube knows its dimensions and what its orientation is.  The graphical display is a reflection of the state of the cube - which way it is facing, how tall it is, and so on.  The instance of an interface class could be ViewManager in VisualSmalltalk, ApplicationModel in VisualWorks, and in VisualAge, it is a subclass of GFApplicationWindowES.  It could simply be a subclass of Object, as long as it can fulfill its role of coordinating the flow of information from the handles to the cube itself.

In the GFThreeDEditor, we use a GFParameterizedTrackHandle.  We use the #generateHandles event (see Adding a GO for a Network Node), and when the figure needs handles, we create them on-the-fly.  For example, from the GFThreeDEditor>>handlesForFigureIndex: method, here is what the "Z" handle looks like:

(GFParameterizedTrackHandle
on: aGO
at: #topLeft
change: (Message
receiver: self selector: #changeZ:delta:)
with: i)
scrollFlag: false;
limit: false;
displayImage: (GFTextGO text: 'Z')

When you grab the "Z" handle and move it, the handle sends the message changeZ: delta: to the GFThreeDEditor.  The first argument is the index of the GO tracked by the GFThreeDEditor, i, and the second argument is a point that is the change in mouse position.  In its changeZ:delta: method, the GFThreeDEditor then tells the underlying domain model to change its Z dimension.

Another interesting point about the handles in the 3-D Figures demo is that the letters used for the handles are created using GF/ST.  In fact, you can make a handle out of any GF/ST graphical object.  The X, Y, and Z handles are made of GFTextGO objects.


Composites and Grouping

The 3-D Figures demo shows the power of being able to group graphic objects into composites.  For example, each cube you create in the demo is made up of the six closed polylines (one for each face) contained within a composite.  This approach makes it easy to treat the displayed cube both as a whole and as a cluster of component parts.  For example, when the cube is moved, it is treated as a whole.  The z-ordering of the 3-D figures relative to one another also requires that the figures be treated as a whole.  On the other hand, z-ordering of the faces as you rotate the figure must be determined within a figure.  Menu selections can also be applied to the specific polyline you clicked on.  Try bringing up a menu on one of the sides of the cube and make the filled polyline transparent or change its line width.  Composites let you retain the control over the independent GO's, while a group would only let you treat them as a whole.


Dependent Lines

Add a tensegrity to the 3-D Figures demo.  The red, blue, and green lines are actually dependent lines that are automatically updated when the polylines that define the figure are updated.  These colored lines are the same connecting lines that can be created from the center handle of a rectangle while in the Drawing Editor.

From the GFThreeDEditor>>addPsiSolidAt: method, the dependent lines are created as follows:

lines at: 1 put: (GFDependentLineGO
startLocation: (GFLocator
on: (gos last at: 1) at: #point: with: 1)
stopLocation:  (GFLocator
on: (gos last at: 8) at: #point: with: 1)).
(lines at: 1) color: Color red.

The lines variable is a collection of the GFDependentLineGO's that is created at the beginning of the method and added to the GFCompositeGO at the end.  The instance variable gos is a collection of collections, one for each of the GFCompositeGO's in the drawing.  For efficiency of display and manipulation of 3-D objects in a 2-D world, each of the secondary collections tracks the individual GO's in the composite as well as the composite itself.  (It's just a caching trick to eke some more speed out of the demo, since determining face order in a rotating 3-D object is computationally intensive).

Note the use of GFLocator's to define the position of the start and stop location of the dependent line.  See the discussion in GFLocator of Basic Concepts and Classes for an overview of how locators work.

What's a tensegrity?  Besides being our logo and the name of a damn good persistent object framework for Smalltalk, a tensegrity is a term coined by R. Buckminster Fuller to describe a structure that is held together by tensile, rather than compressive forces.  The simple tensegrity you're looking at is an octahedron composed of three rods (represented in the figure as the thick red, green, and blue lines) held in place by the tension from the connecting lines.


More About Menus

Notice that the pop-up menu for a cube is different than the menu for a tensegrity.  Actually, the Faces submenu is different, because we want the faces of the tensegrity to remain transparent. You can specialize menus very easily because we use the event system to get the menu for an individual graphic object.  The application itself can define the menu to be used for each graphic object if the default menu is not wanted.  The application is also able to define what, if any, menu should be used for the overall drawing, when you pop-up a menu outside of a figure.

In the GFThreeDEditor, each 3-D figure on the screen is a GFCompositeGO.  The composite is made up of filled polylines (GFPolylineGO's), one for each face of the figure, and each of the polylines has its own menu.  In the GFThreeDEditor>>addFigure: method, you can see how the menu for the polyline (referred to as aGO) is hooked into the GFThreeDEditor itself:

aGO when: #getMenu
send: #menuForFigureIndex:
to: self
with: (Array with: aGO with: (gos size + 1)).

When you click on the right mouse button while the mouse is positioned over a face of the 3-D figure, GF/ST triggers the #getMenu event.  The GFThreeDEditor is in turn sent the message #menuForFigureIndex:, the argument for which is an Array with two elements (the first being the polyline GO, and the 2nd being the composite).  The menuForFigureIndex: then returns the appropriate application-specific menu.


Understanding the GFAboutDialog

The GFAboutDialog is shown whenever you open the Demo Launcher.  You can invoke it again from the launcher, or by evaluating:

GFAboutDialog open.

The GFAboutDialog>>open method shows one way to couple the GFOwnDCDrawingPane with the GFDrawingInterface in a window that also has standard subpanes.  Refer to that method directly, and see the discussion in Creating the GFNetworkNode GOCreating and Opening the Network Editor UI" for detailed discussion of similar issues in the Network Editor example.

The spinning tensegrity in the GFAboutDialog is not an animation using a pre-defined set of bitmaps.  The animation is accomplished by sending messages (in a background process) to an underlying domain model object to change its rotation, asking it for the equivalent 2-D coordinates in its new position, and asking the composite figure to redraw itself.  The background process is initiated in the GFAboutDialog>>startSpin method.


Understanding the Visual Inspector

You can invoke the Visual Inspector from the GF/ST launcher.  The Visual Inspector is a tool that shows the state of objects and the relationships between objects graphically.  It is a complex enough tool to warrant a separate chapter in this document (see What is the Visual Inspector?).  Polymorphic also supplies a non-source version of the Visual Inspector as a freeware Smalltalk product on some platforms, which is another reason that we've broken out the how-to-use-it documentation from the how-to-understand-it documentation.

Visual Inspector classes are prefixed with Psi, since we view it as a Polymorphic Software utility, not as part of the base GF/ST framework or product.

You can open the Visual Inspector from the launcher, or by evaluating:

PsiVisualInspector open.

To open a Visual Inspector on an object, send it the message psiVisualInspect.  For example:

Transcript psiVisualInspect.

Related Topics:

The Approach

New Classes

The Visual Inspector Put in Context


The Approach

The Visual Inspector stands in contrast to the 3-D Figures demo in the way it uses the GF/ST framework.  The 3-D Figures made use of existing GF/ST classes, like GFPolylineGO, to display underlying domain model classes, like GFCube.  The Visual Inspector was built by adding new subclasses to GFGraphicObject that are specialized for inspecting Smalltalk objects.

Creating the Visual Inspector was an easy task using the GF/ST framework.  Almost all the coding fell into two categories:  displaying the desired representation of the object in question, and providing the behavior for manipulating the fields of the object.  Everything else, from the display, handle behavior, and constraints imposed on referencing lines, came from the framework provided in GF/ST.


New Classes

We subclassed off of GFCachedGO to create PsiAbstractObjectGO.  Rather than draw a potentially complex series of figures for every object, we build the bitmap representation of the figure in a single method.  This gives the inspector its terrific speed in display and manipulation.  (It also means that a scaled view in the Visual Inspector uses scaled bitmaps, which do not look as nice as they could if they were redrawn at the new scale each time.)  Among other things, PsiAbstractObjectGO holds the common display behavior for the figures in the inspector, giving them that chiseled look we liked.

To flesh out the hierarchy, we initially created three subclasses of PsiAbstractObjectGO - PsiObjectGO, PsiVariableObjectGO (a subclass of PsiObjectGO), and PsiVariableByteObjectGO.  These classes were needed to provide different behavior for the three flavors of objects available in Smalltalk.  To deal with primitive objects, such as nil, Integer, true, false, and Symbol, we also created a subclass of GFTextGO, PsiPrimitiveObjectGO.  To round it all off, we added two more classes under PsiObjectGO - PsiOrderedCollectionGO, and PsiDictionaryGO.  Since the base image development environment provides these specialized inspectors, we decided their usefulness would extend into the Visual Inspector as well.

In addition to the PsiObjectGO hierarchy, we needed an object to represent the reference held by one object to another object.  We created a subclass of GFHandle, called PsiObjectReferenceHandle, to take care of this task.  The added responsibility of this class is to keep track of its connection, a dependent line of some sort.


The Visual Inspector Put in Context

Besides being a useful tool for your development, the Visual Inspector is different than the other example applications provided with GF/ST, since it extends the framework.  It adds in new GO subclasses and a new handle class.  We encourage you to extend the GF/ST framework, and we caution you that, often, your problem can be solved by combining existing GO's and making use of the events and parameterization that are already present.  Refer to Designing a New Application for more discussion and help in making these kinds of decisions in your own application.


Designing a New Application

The best way to learn GF/ST is to build a simple application with it.  Start simple, add complexity later.  Do yourself a favor and try to factor your domain model classes out of your application.  In other words, your ViewManager, ApplicationModel, or GFApplicationWindowES class should act as the glue between user interface classes (like windows, GO's, and their interfaces) and domain model classes that actually represent your problem domain.  To emphasize the glue-like nature of these "interface" classes like ViewManager and ApplicationModel, we are simply going to use the generic class name GluePuppy.  Now we can refer to the GFNetworkEditor as a subclass of GluePuppy in this document, and you'll just have to translate mentally to the right class name in the Smalltalk you're using!

The major demo applications like the Drawing Editor are incredibly useful, but we want to go through the entire thought process of building a brand new application.  We've chosen as a learning vehicle something that software developers are already familiar with, a computer network.

Related Topics:

The Scenario

The Problem Domain


The Scenario

We have a network of computers and a group of obnoxious network managers who are demanding a way to visualize the relative volume of traffic between them.  We have the technology!  The network managers can already see things in sorted lists, which are nice, but they want a way to get the big picture at a glance.  In the great tradition of Smalltalk, we've taken on the job of building a prototype so we can give them a feel for the kinds of things that can be done.

We'll limit ourselves in the prototype to simply being able to build the network interactively, assign weights to represent the traffic between nodes.  In the real thing, this kind of information would be gathered from the network itself.  We'll use a simulation to show how we can easily help visualize the effect of network traffic.  The simulation is a variation on the old N-body problem, where nodes repel one another if they are connected, and the higher the traffic weighting, the stronger the spring is connecting them.


The Problem Domain

Without offering any apologies to the methodologists in the crowd, we don't need a committee and a huge white board to understand the problem domain for this little prototype.  There is really only one domain model object - let's call it GFNetworkNode.  You may think we need a GFNetwork object also, and you're right.  For purposes of the prototype, though, we're going to let the GluePuppy subclass invoke operations on the group of nodes (or GO's, actually) that are held in the drawing.  One might also argue that we need a GFNetworkConnection object, but we're not going to worry about it right now.

One other consideration as to why you might want a GFNetwork object in your own application:  what if you want to store the thing persistently?  It's not a good idea to store GluePuppy instances, since they might hold onto all kind of transient information, like Window handles.  It would be a lot better to store GFNetwork objects.  But, that is an exercise left to the reader.

A node is connected to other nodes - let's hold onto them directly in an OrderedCollection of GFNetworkNodes.  Each node needs to know what the traffic weighting is between it and the nodes it connects to.  We could hold onto this information in another OrderedCollection, but since we might want to be able to locate and modify these weights arbitrarily as a user clicks around the screen, let's use a Dictionary with keys of GFNetworkNodes and values of Numbers that represent the network traffic weight on a scale from 0 to 100.  Nodes should also hold onto their name, a String.

GFNetworkNodes will need the usual cast of accessing methods, at least if we want to provide public access to their state information.  We also need to provide behavior for adding and removing connections between nodes, and for assigning and retrieving the weights.  It is our decision that nodes will always know about all their connections; i.e., when I connect to you, I hold onto you in my collection of connections, and you will hold onto me in yours.  The byproduct of this assumption is that nodes need to be smart about disconnecting themselves from other nodes.  If you have an object that is holding onto a node (like a drawing, or a network), and you want to delete a node from it, you need to tell the node to disconnect itself from everything before removing it.  Otherwise, you may be holding onto other nodes that hold onto the node you just deleted.  Ain't objects great?  This little issue is one of the good reasons to use a GFNetwork object for all public protocol related to adding and removing nodes, rather than letting it be obfuscated in a GluePuppy subclass (even though we don't do it in this prototype).

Here is what a brand new GFNetworkNode looks like:


Creating and Opening the Network Editor UI

GF/ST adds in just a few new requirements to the opening sequence you've always used for your application.  You'll still use old GluePuppy to glue things together, and you'll still create views (or whatever your Smalltalk calls them) and position them in a container view of some kind.  You can use PARTS in Visual Smalltalk, or WindowBuilder in Visual Smalltalk and VisualAge.  You'll just have to hook your GFDrawingPane to an instance of GFDrawingInterface, which in turn holds onto a GFDrawing.

Related Topics:

Opening Lines

Designing and Setting Up the Layout


Opening Lines

Pick your own poison for your GluePuppy class.  As we said before, this class might be ViewManager or ApplicationCoordinator under VisualSmalltalk, ApplicationModel under VisualWorks, our own GFApplicationWindowES under IBM Smalltalk/VisualAge.  If you use ObjectShare's WindowBuilder/Pro under IBM Smalltalk/VisualAge, it would be WBApplication.  We'll call our new class GFNetworkEditor and stick with the style:

GFNetworkEditor open.

to start it up.  You should feel free to use WindowBuilder, PARTS, or the VisualAge Workbench with GF/ST.  For us, the lowest common denominator is, unfortunately, none of the above.  That means we have to work with them all, but we can rely on none of them being present.

For convenience, we're going to hold onto all the GFNetworkNode graphic objects in an instance variable called nodeGOs.  For our prototype, we'll use the collection of node GO's as our network.  It will also be convenient for us to hold onto the GFDrawingInterface (we'll call it interface) that will be our main way to communicate with the drawing as a whole.

The initialize method is invoked automatically when new instances of ViewManager are created using the class method new and when instances of ApplicationModel are created using the class method open.  The initialize method is a good place to set up our drawing and its interface.  At a minimum, it should look like this:

initialize
super initialize.
interface := GFDrawingInterface newWithDrawing.

If you need to do something more elaborate to the drawing or the interface, you can do it in a few steps, as in:

initialize
| drawing |
super initialize.
interface := GFDrawingInterface new.
drawing := GFDrawing new.
interface
setDrawing: drawing;
noUndo;
disable.

In WindowBuilder, you would use the preInitWindow method instead of the initialize method, typically.

In base IBM Smalltalk, you would implement the same steps in a mixture of an open method and a createShell method.

Now you need to write the open method.  A generic open method for Visual Smalltalk would look something like this:

open
"Open a GFNetworkEditor"
self
labelWithoutPrefix: 'Network Editor';
noSmalltalkMenuBar;
createViews;
setUpMenus;
openWindow

and you will have to write the createViews and setUpMenus method yourself or using WindowBuilder.

Under VisualWorks, the UIBuilder takes care of all these details for you, since you defined them as part of your windowSpec.  Under base IBM Smalltalk, the open method might look like this:

open
"Open a GFNetworkEditor"
interface := GFDrawingInterface newWithDrawing.
super open.
self revisit: nil

We're hoping that all of these details and platform specifics don't scare you.  There are really two simple things to remember:

1.	Use a method like initialize, which is automatically called in your environment, to instantiate the GFDrawingInterface and hook it up to a drawing.

2.	Hold onto the GFDrawingInterface in some convenient way, typically in an instance variable of your GluePuppy subclass, since you'll be using it later.


Designing and Setting Up the Layout

Here is what we want it to look like, and you can refer to the code to see how it's done:

We'll use the ScrollBar at the bottom to let users adjust the weights between nodes when one is selected, and we'll provide feedback on what the current value is using a simple StaticText pane.  We'll use the Balance, Step, and Halt buttons to start the simulation that updates the display to reflect the weights assigned to each connection, but we don't have to worry about those things until we are able to create and connect nodes.

You'll be creating a GFOwnDCDrawingPane and adding it as a subpane or child window in your createViews-type method.  When you do so, you will create it for a GFDrawingInterface - the one we set up at the beginning in the initialize method.  One way to do this is to use the protocol:

GFOwnDCDrawingPane forInterface: interface.

If you're using WindowBuilder, which automatically generates a createViews-type method which you don't want to edit manually, you can assign the interface to the drawing pane in the initWindow method that is called before the window actually opens:

initWindow
(self paneAt: #drawingPane) setInterface: interface.

Remember, you won't be talking to the GFOwnDCDrawingPane directly, but instead, you will use its interface.  The "direct manipulation" operations, like adding, connecting, and moving nodes have nothing to do with the static layout of the user interface that is defined in createViews.  We'll be hooking into events that are triggered by the GO's as we create them, to be able to update our normal Smalltalk widgets, like ScrollBar and StaticText that are used in the GFNetworkEditor.  In addition, we'll need to update the state of our underlying domain model GFNetworkNode objects as the user establishes connections and moves the scroll bar.


Decisions Made in Building the Network Editor

By now, we've figured out how to open up the basic GluePuppy application and established what we want to do with it.  So far, all we've done to complicate your life is made you hook up a GFDrawing with a GFDrawingInterface, and told you to be sure your GFOwnDCDrawingPane is connected to the GFDrawingInterface.  Before moving on, we want to discuss some of the decisions that you will face when building an application like the Network Editor and provide some tips to make it easier.

Related Topics:

Choosing GO's

Choosing Tools


Choosing GO's

Let's say we know we want nodes to look like rounded rectangles with text in them, as we saw in the picture.  Do you know how to embed text in rounded rectangles yet?  Probably not.  Look through the names of the subclasses under GFGraphicObject.  The easiest thing to do is to use the existing GFRoundedRectangleGO for now.  You can worry about the text later.  Without doing anything special, you'll get the behavior and handles that are shown in the Drawing Editor.  It's probably not exactly what you want, but you can tailor it to meet your needs later.

Do you need to create a new subclass for your graphic object?  After all, this is a graphic object to display GFNetworkNodes.  It should probably know what it is displaying, and we probably want all kinds of special behavior that we're not going to get out of that old GFRoundedRectangleGO.  The answer in almost all cases will be No.  One reason is that the behavior of the network node GO you want is always invoked through a handle or a tool.  And, you can combine GO's into a group (a GFGroupGO) to make them look like almost anything you want.  Check out the 3-D Figures demo; it was all done using the standard GO's available in GF/ST.  Finally, you can let GO's hold onto your domain model objects (or any object) through the use of its metaObject.

When do you want a new kind of GO?  This will be a design decision you need to make.  For example, we felt that the Visual Inspector warranted whole new subclasses of GFGraphicObject, as well as specialized connection handles.  On the other hand, we did not have to play around with GFTool at all.


Choosing Tools

The tool represents the mode of use of the drawing in your application.  In the Network Editor prototype, we chose to use the GFSelectionTool always, and create the GO's programmatically, as opposed to direct user mouse input.  In other words, we'll add new nodes as a result of a menu pick, not by selecting a GFCreationTool and rubber-banding.

When you create a GFDrawingInterface, it sets the default tool to be the GFSelectionTool.  Graphic object classes will tell you what kind of creation tool is appropriate when you want to create a GO of that type.  For example, if you want to switch to a creation mode for a GFRoundedRectangleGO, you would say:

interface selectTool: GFRoundedRectangleGO creationTool.

Be sure you provide a way to get back to a selection mode; for example:

interface selectTool: GFSelectionTool new.

The interface actually holds onto a palette of tools which is an instance of GFToolPalette.  You can also use the palette to choose a category and a tool within that category.  The Drawing Editor displays this palette using a floating window which lets you easily select a tool, but you can hook it up to a tool bar or menu item just as easily, since the GFToolPalette is not specific to any user interface.  The preferable way to manipulate tool selections is to use the palette, although in simple cases like the Network Editor, where we are only using one tool, there is no need.


Adding a GO for a Network Node

By now, you're beginning to get the idea that we were really serious about using the interface for most of your interaction with the components of a GF/ST application.  Adding and deleting GO's is no exception.

Related Topics:

The Method

Creating a Network Node

Hooking to Events

Positioning the GO

Making it Display

Tracking GO's


The Method

We decided to forego tool selection in this simple application by using "Add node" and "Delete node" menu selections.  The result of choosing "Add node" in the menu is that our GFNetworkEditor will receive the message addNode.  Let's take a close look at the method (very close, considering the type size we have to use):

Line	Method Body
1	addNode
2	"Add a node to the display."
3	| name node nodeGO |
4	name := Prompter prompt: 'Enter node name' default: ''.
5	(name == nil or: [name isEmpty]) ifTrue: [^nil].
6	node := GFNetworkNode new name: name.
7	nodeGO := self nodeGOFor: node.
8	nodeGO
9	when: #generateHandles
10	send: #handlesFor:
11	to: self
12	with: nodeGO;
13	when: #select send: #updateWeight to: self;
14	when: #deselect send: #updateWeight to: self;
15	origin: interface visibleRectangle center.
16	interface addGO: nodeGO.
17	nodeGOs add: nodeGO


Creating a Network Node

In lines 4-6, we prompt for a name and create an instance of our domain model class, GFNetworkNode.  GFNetworkNodes know nothing about how or if they are displayed.  To do that job, we need a graphic object, which we create in the nodeGOFor: method seen in line 7.  We'll be discussing it more later on.  For now, it could just return a new instance of GFRoundedRectangleGO.

Note that line 4 is the only line that is Visual Smalltalk specific.  There are equivalent expressions in VisualWorks and VisualAge for prompting a user.


Hooking to Events

Lines 8-12 show a single when:send:to:with: message that hooks into the generateHandles event that every GO triggers when it needs to display handles.  By telling the GO that we will handle this event, we are saying that we will be the ones returning the handles for the nodeGO in our handlesFor: method.  The nodeGO that triggers the event will pass itself along as the argument, so we can ask it any questions we want.  Similarly, in lines 13 and 14, we are telling the GO to let us know to update the weight display whenever it is selected or deselected.  We only want weight to show up when a connection is selected, not a node, so we need to know when a node is selected to be able to clear the display.


Positioning the GO

In line 15, we set the origin of the GO to be at the center of the visible area of the drawing.  The drawing itself is, by default, the same size as the display device you're on, but you may have scrolled up or down.  The interface takes care of tracking where the visible area is relative to the drawing as a whole, so you can use it instead of worrying about things like topCorner as you would with a GraphPane in Visual Smalltalk.


Making it Display

Up to this point in the method, we have drawn nothing on the screen, we have only created the GO, set up the events, and set up where it will be drawn.  In line 17, we actually draw the GO on the screen by telling the interface to addGO:.  As soon as it is on the screen, you get all the default behavior of the GO, so you can move it around and so on.  However, because we hooked into the generateHandles event, we must have our own handlesFor: method.  You can try it out without hooking into the generateHandles event, and you will get the default handles for the kind of GO you returned from the nodeGOFor: method.


Tracking GO's

Last, but not least, line 18 shows how we are keeping track of each nodeGO we create in our own nodeGOs instance variable.  Sure, the interface holds onto these things, but we just thought it would be clearer to hold them directly.  In effect, this collection of GO's is our network, since each GO knows what actual instance of GFNetworkNode it models.  We set up that information in the nodeGOFor: method, our next topic.


Creating a GO in the Network Editor

We decided that we should use a GFGroupGO for the visual representation of our GFNetworkNode.  As we've said before, by all means use the built-in GO's like GFRectangleGO to get up and going if grouping GO's might slow you down.  On the other hand, a simple group GO consisting of a GFTextGO and a GFRoundedRectangleGO amounts to about three more lines of code than you would have to write for the GFRoundedRectangleGO alone.

Related Topics:

Group or Composite?

Creating the GFNetworkNode GO

Setting the metaObject


Group or Composite?

Use a GFGroupGO if you will not be directly manipulating the objects in the group; otherwise, consider using a GFCompositeGO.  Since we'll just be setting the text on top of a rounded rectangle, we can use a group with no qualms.  As previously discussed, GFGroupGO's use up resources by using a Bitmap.  Composite GO's do not use a bitmap, because you need to be able to directly manipulate the individual GO's inside of it.  In a GFGroupGO, for example, there is only one menu - the one for the group - while in the composite, the each individual GO retains its menu.  You can disable the use of cached bitmaps on GFGroupGO via the cacheFlag.  Using the cacheFlag: method, you can disable the caching of bitmaps and still retain the behavior of a group.  Setting the cacheFlag to false is slower for redraw, but uses fewer resources.


Creating the GFNetworkNode GO

Refer to the How To Use the How To's section for details of the protocol for creating various kinds of graphic objects.  GF/ST provides many class-specific instantiation methods that should meet your needs.

Here is the method we use to return a group GO for an instance of a GFNetworkNode:

Line	Method Body
1	nodeGoFor: aNode
2	"Private - Return a GO that is appropriate for aNode."
3	| textGO rrGO h v nodeGO |
4	h := 3 * (Font systemFont stringWidth: aNode name) // 2.
5	v := 3 * Font systemFont height // 2.
6	textGO := GFTextGO text: aNode name.
7	textGO translateBy: (0@0 right: (h // 8)).
8	rrGO := GFRoundedRectangleGO rectangle: (0@0 extent: h@v) cornerEllipse: 5@5.
9	rrGO fillColor: Color yellow.
10	nodeGO := GFGroupGO graphicObjects: (Array with: textGO with: rrGO).
11	nodeGO metaObject: aNode.
12	^nodeGO
Lines 4 and 5 set up the height and width for the GO based on the name of aNode, the system font, and a factor of 3/2 times the string size.  In line 6, we create a new instance of a GFTextGO with the node's name in it.  Once we have it, we want to push it to the right so it is centered.  Be careful with point arithmetic to use Visual Smalltalk methods like rightAndDown: if you ever hope to port Windows code easily to run under OS/2 and vice-versa.  In lines 8 and 9, we create a new, yellow GFRoundedRectangleGO.  Then, in line 10, we create a GFGroupGO and add the text and rounded rectangle to it.  None of these operations has caused any display activity, which is handled via the interface in the addNode method of GFNetworkEditor.


Setting the metaObject

If you want a GO to hold onto another object, and in particular, if you want it to know about its domain model, you can use the metaObject for it.  As in classic MVC-style programming, you should consider it to be perfectly proper for a graphic object to know about its underlying domain model, but you should avoid any knowledge in your domain model classes of how they are displayed.  A side note for you MVC haters out there:  loose coupling is the good part of MVC, duplication of view/controller protocols and rampant updates is the bad part.  It's not all bad!  A side note for those of you who don't know what MVC is:  count your blessings.  Just remember to factor your domain model classes out of your application, and never let them know how they are being displayed.

Line 11 shows how to set the metaObject for our new GFGroupGO we are using to display aNode.  Similarly, if you have the GO and you want to find out what its node really is, just ask it:

node := aNodeGO metaObject.

The method ends in line 12 by returning the GO, of course.


Specializing Handles in the Network Editor

Whenever a GO needs handles (i.e., when you select it), it triggers the #getHandles event.  If you are handling that event, you need to return a collection of handles to be displayed.  We saw in Adding a GO for a Network Node that we set up the #getHandles event by telling the nodeGO:

nodeGO
when: #generateHandles
send: #handlesFor:
to: self
with: nodeGO

Now, we need to implement a handlesFor: method.  There are a number of special things we want to accomplish in this method which we will be discussing in detail.  Overall, however, the purpose of this method is to return the proper set of handles for the GO, with the proper positioning and behavior.  This is our opportunity to override the default handle behavior for a GO.  Here is what it looks like:

Line	Method Body
1	handlesFor: aNetworkNodeGO
2	"Private - Return the handles for aNetworkNodeGO."
3	| connectionHandle aGO node |
4	node := aNetworkNodeGO metaObject.
5	connectionHandle := GFConnectionHandle
6	on: aNetworkNodeGO
7	at: #offCenter:
8	with: (self connectionLocationFor: aNetworkNodeGO).
9	connectionHandle setLocatorGenerationAction: [ :toGO :point
10	self locatorFor: toGO].
11	connectionHandle setFindTargetAction: [ :int :point |
12	((aGO := int graphicObjectAt: point) == nil or: [ aGO isConnectionGO])
13	ifTrue: [nil]
14	ifFalse: [
15	(aGO metaObject connections includes:  node)
16	ifTrue: [nil]
17	ifFalse: [aGO]]].
18	connectionHandle setTargetPositionAction: [ :toGO | toGO center +  (self connectionLocationFor: toGO)].
19	connectionHandle setConnectedAction: [ :conn :toGO |
20	node connectTo: toGO metaObject.
21	conn when: #select send: #updateWeightFor:  to: self with: conn;
22	when: #deselect  send: #updateWeightFor:  to: self  with: nil.
23	conn interface sendToBack: conn;
24	selections: (OrderedCollection with: toGO)].
25	^OrderedCollection with: connectionHandle
Related Topics:

Getting the node from the metaObject

Creating a GFConnectionHandle

Where Do Connection Lines Snap To?

Determining Valid Targets for Connection

Where Are Connections Drawn To?

What To Do After Making a Connection

Don't Forget to Return the Handles


Getting the node from the metaObject

When we created the GO, we set its metaObject to the specific GFNetworkNode it was displaying.  Now we are going to use it, so we hold onto the node in a temporary variable in line 4.


Creating a GFConnectionHandle

The only handle we want our node graphic objects to have is a connection handle.  Node GO's are sized automatically when we create them, and we're trying to keep the display clean.  Perhaps we'll use color at some later point to help network managers locate trouble spots, so we don't want them to have access to a fill-color handle.

In lines 5-8, we create an instance of a GFConnectionHandle using the on:at:with: class-specific instantiation message.  The arguments are the GO, a message selector that will return the appropriate point, #offCenter:, and the argument for that selector.  We use our own connectionLocationFor: method simply to return a point that is 3/8 of the GO's bounding box height.


Where Do Connection Lines Snap To?

The connection handle sits at the location you specify.  When you hold down the left mouse button over it, GF/ST shows you a connecting line which jumps to the position defined by the connection handle locator on a target GO.  In this manner, the point your lines snap to, defined by the locator, can be different than the place they come from.  In fact, the location they snap to as you drag a connection around can even be different than the place they end up when you let go (see Where Are Connections Drawn To? below).

In line 9-10, we specify an action of some kind (i.e., a block or an EvaluableAction - anything that responds to the protocol of EvaluableAction) which, when evaluated, returns a GFLocator on a point.  That point will be the place that lines snap to as you drag the cursor around while connecting nodes in the drawing.  Note that the arguments which are passed to the locatorGenerationAction are the GO being connected to, and the mouse location.

The locatorFor: method referenced in line 9 says:

^GFLocator
on: aNetworkNodeGO
at: #offCenter:
with: (self connectionLocationFor: aNetworkNodeGO)

Refer to GFLocator for a discussion of locators.  Typically, the connection handle location, the point returned by the locatorGenerationAction, and the point returned by the targetPositionAction (discussed in Where Are Connections Drawn To?) will all return the same point, but we didn't want to limit your options.


Determining Valid Targets for Connection

By default, any object in the drawing can be connected to.  In our Network Editor, we do not want users to be able to connect to the connecting lines between GO's, and we do not want connections to be established more than once between the same GO's.  We can customize the connecting behavior for a GFConnectionHandle by setting its findTargetAction, as shown in lines 11-17.

Once again, the argument to the setFindTargetAction: method is an action of some kind (in this case a block) that is evaluated as you drag the mouse over GO's in the drawing.  If the result of evaluating the action is a GO, GF/ST will evaluate the locatorGenerationAction to determine where to connect to.  If the result is nil, you will not be able to connect to that GO.  The arguments supplied by GF/ST are the GFDrawingInterface and the location of the mouse.  The logic in lines 12-17 asks the interface to determine what GO is located at the point, and it only returns the GO if it is not a "connection" GO and if the node itself is not already connected to the target node.


Where Are Connections Drawn To?

In line 18, we set the targetPositionAction for the handle.  Typically, the target position is the same as the location as is specified by the locator used in the locatorGenerationAction, but it does not have to be.  You would make the targetPositionAction return a different point if you want the connecting lines to connect to a different point than they snap to during the connection process.

The targetPositionAction, when evaluated with the targetGO as the argument, must return a Point.  Note that the locatorGenerationAction returns a GFLocator.


What To Do After Making a Connection

When you connect two GO's, GF/ST evaluates the connectedAction for the handle.  It passes the connecting line itself and the target GO (the one you're connecting to) as arguments.  The connectedAction is set in lines 19-24.

When we connect one GO representing a node to another, we need to change the state of the underlying domain model nodes, as shown in line 20.  While we're at, if we need to take any action when the user clicks on the connecting lines themselves, we might as well set up the events at the time the connection is made.  We want update the display of connection "weight" in the GFNetworkEditor whenever we select or deselect a connecting line, as shown in lines 21-22.  It makes the display look nicer if we always push the connecting line to the back, behind all other GO's, and this is done in line 23.  Finally, users felt it made connecting nodes easier if we select the one we just connected to each time, as shown in line 24.


Don't Forget to Return the Handles

When we're all done, we better return a collection containing the handles, as shown in line 25.  In this case, we only have the one handle, a GFConnectionHandle.  We've done all the work in this method to make it display and behave the way we want.


Removing Nodes in the Network

It is simple to remove GO's from a drawing using the removeGO: method of the GFDrawingInterface.  However, our job is more complicated in the Network Editor.  We are not just removing it from the display, but we need to unhook the connections in the display and the underlying domain model.

Referring to the deleteNode method in the GFNetworkEditor, you can see how the GO's are used to determine what domain model objects are affected by a deletion of multiple nodes on the screen.

Line	Method Body
1	deleteNode
2	"Delete any selections that are nodes."
3	| node |
4	interface selections copy do: [ :nodeGO |
5	nodeGO isConnectionGO ifFalse: [
6	(interface connectorsUsing: nodeGO) do: [ :conn |  interface removeGO: conn].
7	interface removeGO: nodeGO.
8	nodeGOs remove: nodeGO.
9	nodeGO metaObject disconnectAll.
10	].
11	]
Related Topics:

Determining What GO's Are Selected

Removing Connections

Removing a GO and Cleaning Up


Determining What GO's Are Selected

In line 4, we loop over all the GO's which are selected in the drawing.  The interface returns a collection of the selected GO's when we send it the message selections.  We need to make a copy of that collection because we will be removing GO's as we iterate over it.  If we don't make a copy, bad and mysterious things will happen in Visual Smalltalk.  As a general rule, never modify a collection as you iterate over it - make a copy of it, or find some other way to accomplish your objective.

In line 5, we filter-out all of the connection GO's; i.e., we are writing a method to remove nodes, not connections.  When we remove a node, we will take care of any affected connections.


Removing Connections

The GFDrawingInterface can tell us what connection GO's are connected to another GO via the connectorsUsing: method, as shown in line 6.  We then tell the interface to remove the connection GO's using removeGO:.


Removing a GO and Cleaning Up

In line 7, we remove the node GO from the interface.  We are tracking these GO's in our instance variable, nodeGOs, so we need to remove it from there, too, as shown in line 8.  We decided when we built the domain model that GFNetworkNodes should know how to disconnect themselves from the nodes they connect to, and this is accomplished in line 9.  Once again, we can send a message directly to the GFNetworkNode using the metaObject of the GO.


How To Use the How To's

The How To's section is designed to provide specific guidance for specific problems in GF/ST.  Graphic objects, tools, handles, events, and so on are all so interconnected, you just can't explain one part without paying any attention to the others.  For this reason, you will find many cross-references between the How To's and the actual GF/ST hierarchy and methods.

Use the How To's as a way to get unstuck when you're stuck.  Use them to double-check your own decision-making as you build a GF/ST application.  They are not designed for front-to-back reading, but as a reference source.


Releasing Resources

VisualWorks users: rejoice!  You can skip this discussion.  Visual Smalltalk and VisualAge/IBM Smalltalk users:  welcome to the border between Smalltalk and host resources.  Traditionally in these latter two environments, if you use an object that allocates host resources of any kind, you must explicitly release those objects when you are done with them.  If you don't, the Smalltalk garbage collector will collect them when they are no longer referenced, but the resources will not be freed.  The result is that your limited resources under your host windowing system will slowly degrade.

Related Topics:

Knowing When to Let Go of a GO


Knowing When to Let Go of a GO

One way to do deal with the problem is simply to send the message release to each GO when it is no longer referenced and will be garbage-collected, typically before you close your window or when you delete a GO.  Visual Smalltalk 3.x provides an easier way to get the same effect via finalization.  If you send an object the message needsFinalization, the virtual machine will cause the object to receive a finalize message just before it is to be garbage collected; i.e., when nobody references it any longer.

In GF/ST under Visual Smalltalk and VisualAge/IBM Smalltalk, we have chosen to make use of finalization the default when you create new instances of GFGraphicObject.  However, for finalization to work in Visual Smalltalk versions prior to 3.1 (it is included by default in 3.1), you must have the finalization "extra" provided by Visual Smalltalk loaded into your image.  If you don't, GF/ST will still work without errors (since we handle the DoesNotUnderstand exception when we tell a GO it needsFinalization).  However, you will then have to release GO's manually.  We highly recommend that you load the finalization extra when using GF/ST.  Finalization is available in the \EXTRAS\FINALIZE subdirectory in the Visual Smalltalk 3.0 release.

We implement a default finalize in GFGraphicObject that says:

self release.

We can therefore be sure that every nodeGO will receive the message finalize, resulting in the release of host window system resources, when it is no longer held onto by anybody.

Warning:  If you do not use finalization under Visual Smalltalk 3.0.x, you must explicitly release your GO's that use bitmaps (GFImageGO, GFGroupGO, GFButtonGO, and any of the Visual Inspector object GO's).  In addition, you should either use an unbounded stack for undo's (the default is 15) or turn the option off via noUndo.  The reason is that the GFBoundedStack holds onto instances of GFMemento in order to undo deletion operations you might perform.  If undo is enabled, you should not release GO's when you delete them, because an undo might cause it to come back.  And, since you can't release it when you delete one, if the stack is bounded, it is possible that the GO will be garbage collected when the stack grows, without your having released it.  If you use finalization, this problem goes away.  If you do not use finalization for some reason, you may want to consider making an unbounded stack or "no undo" be the default.


Mostly GFGraphicObject-Related How To's

This section summarizes the most common ways to create GO's and manipulate them.

Related Topics:

Creating Simple Figure GO's

Creating Group and Composite GO's

Creating Locators for Dependent GO's

Creating Dependent GO's

Setting the Menu of a GO

Creating a GFDrawingPane to Add to a Window

Setting the Menu of a Drawing

Locking down a GO

Adding and Removing a GO

Manipulating GO's

Using GO's at Line Ends

Miscellaneous Topics


Creating Simple Figure GO's

There are numerous class-specific instantiation messages which are easy to find on the class side of the GFGraphicObject hierarchy.  Some of them are listed below for your convenience, with appropriate arguments supplied.

GFRectangleGO rectangle: (0@0 extent: 25@25).

GFEllipseGO ellipse: (0@0 extent: 25@25).

GFLineGO start: 0@0 stop: 25@25.

GFRoundedRectangleGO
rectangle: (0@0 extent: 25@25)
cornerEllipse: 5@5.

GFPolylineGO withPoints:
(Array with: 0@0 with: 12@0 with: 25@25).

A Bezier curve is defined by its start and stop points and two control points used to determine the tangents to the start and stop points.  The class-specific instantiation methods let you define only the start and stop points, or you can specify the control points also.  The arguments for the second method shown below illustrate what the default behavior is when the first method is invoked (i.e., the control points are 10@10 away from the start and stop points).

GFBezierGO start: 0@0 stop: 25@25.

GFBezierGO
start: 0@0
stop: 25@25
controlPoint1: 10@10
controlPoint2: 35@35.

The orthogonal GO's have a multitude of instantiation methods for governing which start and stop directions they will use.  They also have protocol for specifying the start/stop positions together with the direction for each:

GFOrthogonalPathGO bottomStart: 0@0 topStop: 25@25.

A few more simple GO instantiation methods:

GFSplineGO controlPoints:
(Array with: 0@0 with: 12@0 with: 25@25).

GFTextGO text: 'GF/ST'.


Creating Group and Composite GO's

You can create group and composite GO's using the graphicObjects: method, supplying a collection of any GO's as an argument.  The following script illustrates the process.  Also, refer to Creating a GO in the Network Editor as an example of a simple GFGroupGO.

| aGO goCollection |
aGO := GFEllipseGO ellipse: (0@0 extent: 25@25).
goCollection := OrderedCollection new.
goCollection add: aGO.
goCollection add:
(GFRectangleGO rectangle: (0@0 extent: 25@25)).
goCollection add:
(GFButtonGO fromGO: aGO).
goCollection add:
(GFImageGO origin: 0@0 image: Bitmap fromUser).
GFGroupGO graphicObjects: goCollection.
" or... GFCompositeGO graphicObjects: goCollection."


Creating Locators for Dependent GO's

Locators are required when you use dependent GO's.  Dependent line type GO's we supply include GFDependentLineGO and GFDependentOrthogonalPathGO.  You may use locators if you specialize handle behavior, too.  See the discussions in GFLocator and Specializing Handles in the Network Editor.  For another example of dependent line usage, see the PsiSolid class used in the 3-D Figures demo, discussed in Dependent Lines.

Assuming we create a GFPolylineGO as follows:

| aGO locator |
aGO := GFPolylineGO
withPoints: (Array with: 0@0 with: 12@0 with: 25@25).

then, we can create a GFLocator for the center of the polyline as:

locator := GFLocator on: aGO at: #center.

When you send the locator the message evaluate or asPoint, it will return the result of sending the message center to the polyline, aGO.  Any message understood by the polyline is acceptable, as long as it returns a Point.

Other variations include:

locator := GFLocator on: aGO at: #offCenter: with: 5@5.

The argument 5@5 will be sent as the argument to offCenter:.

locator := GFLocator on: aGO at: #point: with: 1.

This last expression will locate the first point in the polyline.  It would only be useful with polylines, because all GO's do not understand point:.  This particular locator would be used to specify the location of a handle at a particular point on a polyline.

There is a set of common protocol in GFGraphicObject to return points at specific locations in GO's.  Specific GO's extend or override the protocol, as in the point: method of GFPolylineGO.  Common protocol that all GO's understand includes:

•	bottomCenter

•	bottomLeft

•	bottomRight

•	center

•	leftCenter

•	offCenter:

•	offCorner:

•	offOrigin:

•	offTopLeft:

•	rightCenter

•	topCenter

•	topLeft

•	topRight

The GFGraphicObject methods origin and corner are platform dependent in Visual Smalltalk; i.e., they will return different points under Windows and OS/2, due to the difference in coordinate systems.  Also, refer to Portability Considerations.


Creating Dependent GO's

This script illustrates how to create dependent GO's.  The protocols apply to the following classes:

•	GFDependentLineGO

•	GFDependentOrthogonalPathGO

Note that dependent GO's use the GFLocator to specify their start and stop locations.  The locator is a simple way to forward a message to another object to obtain a point, and it is described in GFLocator in Basic Concepts and Classes.

| rect1 rect2 startLocator stopLocator |
rect1 := GFRectangleGO rectangle: (0@0 extent: 25@25).
rect2 := GFRectangleGO rectangle: (30@30 extent: 25@25).
"Sending #asPoint or #evaluate to the following
Locators sends the message #center to the rectangles,
which return the Point location of their center"
startLocator := GFLocator on: rect1 at: #center.
stopLocator := GFLocator on: rect2 at: #center.
"Dependent lines and orthogonal paths take locators
as their start and stop points"
GFDependentLineGO
startLocation: startLocator
stopLocation: stopLocator.
"Orthogonal paths have default values for directions; i.e.,
should they start going left, right, up, or down"
GFDependentOrthogonalPathGO
startLocation: startLocator
stopLocation: stopLocator.


Setting the Menu of a GO

Assume that self returns a menu when sent ellipseMenu and rectMenu.  You can attach these menus to the GO's via the #getMenu event which is triggered by the GO when you click the right mouse button while the cursor is over the GO.

| go1 go2 |
go1 := GFEllipseGO ellipse: (0@0 extent: 25@25).
go2 := GFRectangleGO rectangle: (0@0 extent: 25@25).
go1 when: #getMenu send: #ellipseMenu to: self.
go2 when: #getMenu send: #rectMenu to: self.


Creating a GFDrawingPane to Add to a Window

When you use GF/ST in an application, you will be using a GFDrawingPane, or its double-buffered equivalent GFOwnDCDrawingPane.  Typically, though, you will not be sending messages to the GFDrawingPane itself, but to the GFDrawingInterface to which it is attached.  The GFDrawingInterface is your main channel of communication to the GO's as a group.  One of the first jobs you need to perform when you create your application is to create an instance of a GFDrawingPane and connect it to an instance of a GFDrawingInterface.  The script below shows one convenient way to accomplish the task.  Refer to the open and createViews methods in the various applications and examples that come with GF/ST for other specific examples.

| interface drawingPane |
"Step 1:  Create the drawing pane and the interface"
drawingPane := GFOwnDCDrawingPane new.
interface := GFDrawingInterface newWithDrawing.
"Step 2:  Set it to be the drawingPane's interface before opening the window"
drawingPane setInterface: interface.

The drawingPane can be added as a subpane in the creation of a window.  In practice, the GFDrawingInterface referred to above as interface will be needed again and should be put in an instance variable.

When using an interface builder like WindowBuilder or PARTS, you should create and connect the GFDrawingInterface to the GFDrawingPane before the window opens.  In these cases, you will not want to invade the createViews method which WindowBuilder automatically creates for you.  Instead, you should use the preInitWindow method in WindowBuilder or the initializeParts method in PARTS to accomplish the task in these cases.

Assume you are using WindowBuilder and that you have named the GFOwnDCDrawingPane 'drawingPane'.  You have also decided to keep the GFDrawingInterface in an instance variable called interface.  Your preInitWindow method would look like this:

preInitWindow
"Perform pre-opening initialization"
interface := GFDrawingInterface newWithDrawing.
(self paneAt: 'drawing') setInterface: interface

Assume you are using PARTS as an interface builder only.  In that case, you would use the properties dialog to name the GFOwnDCDrawingPane #drawing.  Your initializeParts method would look similar:

initializeParts
"Perform pre-opening initialization"
interface := GFDrawingInterface newWithDrawing.
(self partNamed: #drawing) setInterface: interface


Setting the Menu of a Drawing

We expect that the default menu for a drawing will suit your purposes during development.  The likelihood of it being correct for your end-users is approaching zero, however.  It is very simple to reassign the menu via the #getMenu event.  You will probably be holding onto the GFDrawingInterface in an instance variable.  From it, you can reach the drawing itself, to hook into the #getMenu event:

interface drawing
when: #getMenu send: #mainMenu to: self.

In most cases, you would want to set the menu for the drawing before opening the window.  As discussed in Creating a GFDrawingPane to Add to a Window, the appropriate spot would be in preInitWindow for WindowBuilder or in initializeParts for PARTS.

It is also easy to completely disable the drawing's menu:

interface drawing disableMenu.


Locking down a GO

You may want to disable all menus and handles on a GO.  For example, the GFButtonGO's in the GFDemoLauncher are an example of GO's used in such a manner.

aGO disableInteraction.


Adding and Removing a GO

The simplest way to add and remove GO's is to use the GFDrawingInterface.  The following methods cause the GO to appear on the display or be removed from the display.

interface addGO: aGO.

interface removeGO: aGO.

To add or remove GO's without redrawing, but still tracking damaged areas for subsequent redraw.

interface quietlyAddGO: aGO.

interface quietlyRemoveGO: aGO.

Once you have finished "quietly" adding and removing GO's, you can tell the interface to redraw itself:

interface redraw.


Manipulating GO's

Your application may call for users to manipulate and position GO's directly.  There are, however, many ways to manipulate GO's programmatically.  Some common protocols are summarized below.

To set the origin of a GO to the center of the visible area on the screen without causing a redraw:

aGO origin: aGO interface visibleRectangle center.

To set the origin of a GO to the middle of the drawing without causing a redraw.

aGO origin: interface boundingBox center.

To move a GO to the middle of the visible area on the screen without causing a redraw.  The argument, aPoint, specifies the delta in the Smalltalk platform coordinate system.

aGO translateBy: aPoint.

To send a GO to the back or bring it to the front, causing a redraw:

interface sendToBack: aGO.

interface bringToFront: aGO.

To redraw a GO after having modified it in some way, perhaps via translateBy:.

aGO redraw.

To redraw all damaged areas in an interface after modifying GO's in it, hiding handles first:

interface redraw.

To select a GO:

interface selections: (OrderedCollection with: aGO).

To change the scale of a drawing, causing a redraw.  The argument, aPercent, is 100 for 1:1 display.

interface scale: aPercent.


Using GO's at Line Ends

This release of GF/ST includes the ability to "plug in" any GO at the end of line-type GO's.  By line-type GO's, we mean subclasses of GFDirectedPathGO.  Probably the simplest way to understand the usage of end plugs is to evaluate one of the example methods provided in GFDrawingInterface:

GFDrawingInterface rectangleLineEndExample.

GFDrawingInterface circleLineEndExample.

GFDrawingInterface triangleLineEndExample.

Each of these methods opens up the Drawing Editor on a GFPolylineGO, with different kinds GO's used at the end.  The methods also use GF/ST to display the important instance variables that affect the display and behavior of the GO.

Look in the method body to see how to specify the GO end plug.  You'll be using an instance of GFEndPlugGO.  There are three instance variables that you should specify:

•	displayPoint -  The displayPoint is the point where the line end is located.  It will normally be the start or stop point of the line for which the plug is going to be used.

•	displayOffset - The displayOffset is the offset from the displayPoint to the origin of the GO that is being used as the end plug.

•	connectionOffset - The connectionOffset is the offset from the displayPoint to where the line should start drawing itself from.

You can also specify an arbitrary "action" to be performed, instead of a GO as an end plug.  When you do so, you will be using a GFEvaluableActionEndPlug.  We use a GFEvaluableActionEndPlug for arrow heads on the ends of lines.  It would be inappropriate to use a GO, since the arrow head needs to rotate to the angle of the line.  To see how we use these kinds of end plugs, look in the GFDirectedPathGO instance method endArrowPlug.

GFEvaluableActionEndPlugs specify a "draw action" and a "shape action".  The draw action draws the end plug, and the shape action draws the mask for the end plug.  The mask is black and white, where black represents the opaque area, and white represents the transparent area.  It is often more convenient to create instances of GFMessage (an "action", since it is a subclass of EvaluableAction) and send them the message asGFEndPlug; for example:

(GFMessage new
receiver: self;
selector: #displayEndArrowWith:for:) asGFEndPlug.

GFDirectedPathGO's use the standard displayWith: message to display themselves and their end plugs (if the end plugs are not nil).  In the above example, the result is (assuming self is aLineGO):

aLineGO displayEndArrowWith: aPen for: anEndPlug.

The end plug is passed as an argument so that aLineGO can let anEndPlug know what its displayPoint, displayOffset, and connectionOffset are.


Miscellaneous Topics

Coordinate Systems and Scaling

The coordinate system used by a drawing is the native coordinate system used by the host platform.  Internally, GF/ST uses the native coordinate system; however, protocol is provided to convert native coordinate points to user defined coordinates, and vice versa.  For example:

nativePoint := aGO coordinateToNative: userCoordPoint.

userCoordPoint := aGO nativeToCoordinate: nativePoint.

It is your responsibility, however, to remember which point is in defined in which coordinate system.

It is sometimes useful to have the origin of a drawing somewhere other than where the native coordinate system places it, and have the x and y axes increase in different directions.  The GFCoordinateControl class allows the user to specify where the origin is (relative to the native coordinate system), and in which directions the x and y axes increase.  Note that the coordinate system transformation has nothing to do with units of measurement - it only reflects where the origin is, and in what direction the x and y axes increase.

You can specify what form the units take by using the various subclasses of GFLogicalUnit.  The default unit specified is in pels (i.e., pixels on the screen).  You can also specify either hundredths of millimeters, thousandths of inches, or twips, using the GFHundredthMillimeter, GFThousandthInch, or GFTwip classes.  A twip is 1/1440th of an inch and is a common measurement used in print layout.

The GFCoordinateControl, used by the GFDrawing, holds onto the instance of GFLogicalUnit which is active.  The GFLogicalUnit, in turn, holds onto a resolution value.  In GF/ST, we use the GFThousandthInch logical unit defined by the class method defaultResolution.  The defaultResolution method ends up setting the resolution to:

1000.0 @ 1000.0 / Display pen pixelsPerInch.

On one machine at 800x600 resolution, this expression results in resolution being set to 15.625@15.625.  In other words, 0.015625 inches per pixel.

If you want to change the logical units for your drawing, you must set up the proper resolution values before the drawing is hooked to the interface.  This approach may change in future releases as we provide easier-to-use interfaces to the coordinate system and logical unit framework.

On some platforms, VisualWorks in particular, there is a significant performance penalty for using scaled drawings.  This penalty is incurred in VisualWorks because we must do the work associated with scaling ourselves, instead of letting the host windowing system take care of it.  There are related problems associated with scaling of lines.  On platforms other than VisualWorks, line widths scale up and down as you scale a drawing up and down, because the host windowing system facilities are used.  In VisualWorks, we would have to do the scaling ourselves that accounted for line angles, and we have elected not to do so.  Modifying the framework to scale lines in VisualWorks or to not scale lines on other platforms would be difficult.

Quad Trees

Typically, you should view the Quad Tree implementation in GF/ST as having "no use serviceable parts."  If you break it, very bad things can happen to your drawing.  It is however, both interesting and informative to be able to view the Quad Tree layout in a complex drawing.  To see it, send the message showQuad to the drawing interface.  Probably the easiest way to accomplish this is to open an inspector on a graphic object and evaluate:

self interface showQuad

The screen displayed here shows the quadrants of a complex drawing in heavy regular (red, if you're looking at the Help file) lines.  The display is a graphic illustration that the search for "what GO is under my cursor" is dramatically reduced in complexity when you only have to deal with one quadrant at a time.

A side note:  one of the key responsibilities of the damageDuring: method is to keep the quad tree in sync with the arrangement of GO's as they change during the evaluation of the block argument.  Refer to the discussion in the GFGraphicObject section of Basic Concepts and Classes.

Mementos

See the comments and code in GFMemento and GFBoundedStack for implementation.  See the Drawing Editor and GFDrawingInterface for details of usage.  For background on the memento approach, see Design Patterns by Gamma, Helm, Johnson, and Vlissides, an excellent reference for your library.  Our Smalltalk implementation of the basic memento pattern discussed in the Gamma book extends it to make it more flexible and useful.

GFMemento is a generalized class for recording state information while trying to preserve encapsulation.  It should only be created via one of the originator:state: methods, of which there are several variations.

A GFMemento preserves encapsulation by allowing access to its type and state information only if strict conditions are met.  These conditions are always met if the originator of the memento is identically equal to (==) the requester object.  The memento can also hold onto an accessKey.  The accessKey is a symbol.  When the requester object is not the same as the originator, the message specified by the accessKey is sent to the originator and the requester.  If the results are equal (i.e., as determined by the = comparison), then the requester is allowed access to the memento's type and state.  A good example of an accessKey would be #class.  If the accessKey is specified as #class, then any object of the same class as the originator can get access to the memento's internals.  If the accessKey is nil, then the requesting object must be == to the originator.

The default action is for a Symbol to be stored into the type field.  The restoreUsing: method (implemented in Object) will perform the type symbol using the state information as the parameter.

Undo is enabled by default whenever you use a GFDrawingInterface.  Of course, this means you have to provide a "hook" via a menu for your end-users to be able to take advantage of it.  If you're not going to let your users make use of the feature, you should definitely disable it, as it uses up unnecessary memory by holding onto GO's and the state information needed to undo each operation.  Disable it via:

interface noUndo.

The undo facility provided with GF/ST is not specific to GF/ST.  You can use it in your own application.  However, use of GFMemento requires design thought on your part.  The type and state information of a memento must be supplied by your application - it does not come for free.

Printing

Printing is invoked via:

interface outputToPrinter.

When you print, only the region that encompasses the GO's in your drawing is printed at the scale you specify.  Multi-page printing is handled automatically.  You can print in landscape mode by selecting it in the standard print dialog.  Since printing performance and abilities are tied very closely to drivers and hardware, please consult the README file for the most current information we have concerning any limitations on printing.  Always be sure to use the most up-to-date printer drivers.  If printing works properly on one machine or on one printer in your application, but not on another, you should suspect machine and/or driver setup, not GF/ST or your application.  If you have problems printing drawings that contain bitmaps (including GFGroupGO's), you should try using:

aGO cacheFlag: false.

By setting the cacheFlag to false, GF/ST will  not use a bitmap to cache the GO, and printing may be improved.  In particular, problems may be cured for some PostScript printers that have difficulty printing opaque bitmaps.


Mostly Handle-Related How To's

Handles are extremely powerful and thus extremely parameterized.  This design approach makes it possible for you to use handles in very different ways without creating new kinds of handles to get different behaviors.

Related Topics:

Track Handles

Handles, Scrolling, and Messages

Handles and Grids

Taking Action When a Handle is Used

Setting the Handles for a GO

Setting the Position Handle for a GO


Track Handles

Track handles are used to translate mouse movement into actions on a GO.  Track handles hold a change selector to specify the message which will be sent to the GO, along with a Point that typically specifies how much the mouse moves.  Track handles come in several flavors

•	GFTrackHandle - The simplest track handle.  Holds onto a GO and a change selector.  When you grab hold of the handle and move the mouse, it send the message specified in the change selector to the GO with the mouse movement specified as a Point.

•	GFParameterizedTrackHandle - Allows you to pass another parameter with the mouse movement.  With the parameterized track handle, you can take advantage of more elaborate protocol implemented on a GO and route the result of handle movement to any other object, including your own application.

•	GFSelectionTrackHandle - Adds the ability to apply changes to all GO's currently selected in the interface

•	GFAbsoluteTrackHandle - Adds the ability to send absolute cursor positions instead of mouse movement deltas.

GFTrackHandle

The following code creates a handle on a GO, located at its center.  The selector translateBy: is the change selector.

GFTrackHandle
on: aGO
at: #center
change: #translateBy:.

When the handle is moved it will perform the following:

aGO translateBy: delta.

To use a method like offCenter: on the GO, which requires a Point as an argument to specify how much "off center" the handle will be, there is more elaborate protocol on GFTrackHandle:

GFTrackHandle
on: aGO
at: #offCenter:
with: 5@5
change: #translateBy:.

GFParameterizedTrackHandle

GFParameterizedTrackHandles add the ability to incorporate a parameter with the change method.  The following example assumes we are using a GFPolylineGO.  To put a handle on the first point of the polyline, use:

GFParameterizedTrackHandle
on: aGO
at: #point:
with: 1
change: #movePoint:by:
with: 1.

Movement of the handle by some delta results in the following:

aGO movePoint: 1 by: delta.

An more elaborate very useful approach is to use a sense selector to compute a point from the mouse movement delta, and pass that result as an argument to the change selector.  The protocol is:

GFParameterizedTrackHandle
on: aGO
at: #offCenter:
with: 5@5
sense: #senseBottomRight:
change: #growBy:.

When the mouse moves by some delta, the following expression is effectively evaluated:

aGO growBy: (aGO senseBottomRight: delta).

Routing Messages Directly to Other Objects

You do not always have to let the GO be the receiver of the change selector message.  By using a Message object as the change selector instead of a symbol, you can specify some other receiver and selector to be used.

GFTrackHandle
on: aGO
at: #offCenter:
with: 5@5
change: (Message
receiver: myApp
selector: #rotateBy:).

In the above example, handle movement by some delta will result in the equivalent of:

myApp rotateBy: delta


Handles, Scrolling, and Messages

You may not want a drawing to scroll while a user drags a handle.  In this case, simply send the handle the message:

handle scrollFlag: false.

You can tell a handle whether or not to stop invoking the change selector when the mouse moves outside of the bounds of the drawing using:

aHandle limit: aBoolean.


Handles and Grids

If the grid is being used, you can limit the deltas to increments of the grid size.  By positioning a GO at a particular location that aligns with the grid, future handle movements will then always end up on grid points.

aHandle snapToGrid: aBoolean.


Taking Action When a Handle is Used

You can specify actions to be evaluated when you invoke a handle and when you release a handle.  This mechanism is used in the Drawing Editor to update the status panes telling what a handle does, as well as clearing them when you let go.

One example of an action you could perform when the mouse button is released is shown below:

aHandle releaseAction: [Transcript show: 'I let go'; cr].

To take some action when a handle is invoked, you can handle the event #handleInvoked, which is triggered by the GFDrawingInterface.


Setting the Handles for a GO

Whenever a GO needs handles (i.e., whenever it is selected), it triggers the event #generateHandles.  If  you do not handle the event, you will get the default set of handles for the GO.  The default handles will work fine for initial development of an application, but they are almost guaranteed to be inappropriate for your end application.  To specify the handles that are appropriate for your application's GO's, write a method that returns a collection of handles:

Simple Sizing Handles on Corners

| aGO myApp |
aGO := GFEllipseGO ellipse: (0@0 extent: 25@25).
myApp := MyApplication new.
"simple position tracking handles on the corners"
aGO
when: #generateHandles
send: #allCornersOf:
to: GFSelectionTrackHandle
with: aGO.

The result of the above code snippet is that, when you select the GO, the following message is evaluated:

GFSelectionTrackHandle allCornersOf: aGO.

and it returns a collection of handles at the corners of the GO.

Letting the Application Decide

Often, the state of an application determines what handles are appropriate.  Also, since handles have so much behavior themselves, you may need to decide in your application logic what events handles will be handling, and what kinds of actions they will invoke.  The typical way to deal with the problem is to write a handlesFor: method in your application.  Then, when you create the GO, simply tell it:

aGO
when: #generateHandles
send: #handlesFor:
to: self
with: aGO.


Setting the Position Handle for a GO

A position handle is the handle that is created when a GO is to be moved.  While position handles are primarily used to move a GO, you may find other uses for them in your application.  For example, you may want to send some message to another object whenever you let go of a the GO you are positioning.  In that case, you could create the GFPositionHandle in a positionHandleFor: method in your application, and specify the releaseAction for it when you create the handle:

aGO
when: #needsPositionHandle
send: #positionHandleFor:
to: self
with: aGO.


Event-Related How To's

Events are central to GF/ST architecture, so you'll be using them a lot whether you know it or not.  Refer to The GF/ST Event Model for more information on the basics of events.  If you want to know when or why an event is triggered, refer to Events Explained in the GF/ST Class Hierarchy section.  In this section, we'll try to anticipate some of the questions you might have about events when you start developing an application using GF/ST.

Related Topics:

Tracing the Effects of an Event

Adding New Events


Tracing the Effects of an Event

We're all used to looking at senders and implementors to track how messages flow through Smalltalk.  When you use events, an event may be triggered in a method whose name is different than the event name, and it may result in execution of a method that is different than the event name.  Consider the case when you use the #getMenu event to be able to provide your own menu to your drawing.

interface drawing
when: #getMenu
send: #networkMenu
to: self

If you look for implementors of getMenu, you will find that nobody implements it.  When you look for senders of getMenu, you will find the methods that trigger the event.  These methods are not sending the message getMenu, they are sending the message triggerEvent:, with #getMenu as the argument.  Fortunately, "senders" locates all methods that use the symbol #getMenu.

A note for Visual Smalltalk users.  The "Enterprise" versions of Visual Smalltalk (and all other vendors for that matter) provide a menu pick to find senders and implementors.  If you're using the non-Enterprise version, you can see the same thing by evaluating:

Smalltalk sendersOf: aSymbol

or

Smalltalk implementorsOf: aSymbol.

If you look at senders of networkMenu , you'll only find the method where you set up the dependency; i.e., the method that does the when:send:to:.  Let's take a look at the #getMenu event to see what's really happening.  We'll use VisualWorks as our example, although you'll see similar results elsewhere.

Examining senders of getMenu, we see the only interesting case is the menu method in GFGraphicObject:

menu
"Answer the menu of the receiver"
^self triggerEvent: #getMenu
ifNotHandled: [self standardMenu].

Now, if you put a halt in your networkMenu method, this is the stack you'll see:

Line	Context Stack
1	'Halt encountered.'
2	GFNetworkEditor(Object)>>halt
3	GFNetworkEditor>>networkMenu
4	MessageSend>>evaluate
5	GFDrawing(PsiEventModel)>>triggerEvent:ifNotHandled:
6	GFDrawing(GFGraphicObject)>>menu
7	GFDrawingInterface>>popupMenu
8	GFSelectionTool(GFTool)>>button2Down:
9	GFDrawingInterface>>button2Down:
10	GFDrawingPane>>button2Down:
Some of the above stack is VisualWorks specific, but it's pretty close to what you'll see in all versions.  Looking at line 6, you can see that the menu method in GFDrawing (inherited from GFGraphicObject) ends up invoking the networkMenu method in GFNetworkEditor.  It does this not by saying:

aGFNetworkEditor networkMenu

but by triggering the #getMenu event:

self
triggerEvent: #getMenu
ifNotHandled: [self standardMenu].

This means that if you look for senders of networkMenu, you will not find the menu method in GFGraphicObject.  Instead, you must trace it via the when:send:to: and the method that triggers the event #getMenu.

In line 5, the triggerEvent:ifNotHandled: method, we determine that the instance of GFNetworkEditor is handling the event, and we evaluate the "Action" for it.  This action was created when we originally sent the message when:send:to: to the instance of GFDrawing.

If everything goes well, you need only know how to use the event model.  However, when things go awry, you may need to understand how it works in order to debug.  If you don't understand how the event model works, set up a hook to some event, put in a halt, and watch the code execution just as we did above.


Adding New Events

Remember from the discussion in The GF/ST Event Model that there are two sides to events:  first, registering a public interface as to what events may be triggered in a subclass of PsiEventModel, and second, triggering the event at the appropriate time.

To register a new event in a subclass of PsiEventModel, you must add it to the list which is returned from the class method eventsTriggered.  For example, the eventsTriggered method in GFLineGO looks like this:

eventsTriggered
"Answer the events triggered"
eventsTriggered isNil ifTrue: [
(eventsTriggered := Set new)
addAll: self superclass eventsTriggered;
add: #moveStart;
add: #moveEnd.
].
^eventsTriggered

Note that the variable eventsTriggered is a class instance variable.  Class instance variables are not class variables (which are visible to the class and all subclasses), they are instance variables for the class itself.  Therefore, when we ask the class GFLineEndGO for its eventsTriggered, we will get a different set of events than we get for GFGraphicObject.

All eventsTriggered methods which we supply with GF/ST use a lazy initialization technique (so if eventsTriggered is nil, it initializes itself before returning).  We also always inherit the events held in eventsTriggered in all superclasses.

When you want to add an event to the list in eventsTriggered in an existing class in GF/ST, you should first edit the class method eventsTriggered to add a new event symbol, and then reset the eventsTriggered instance variable for that class and all of its subclasses via:

TheClass resetEventsTriggered.

If you do not send the message resetEventsTriggered, neither the class nor any of its subclasses will return the new event in their list (since they use the class instance variable, not a class variable).  As a matter of fact, it is safest simply to evaluate:

PsiEventModel resetEventsTriggered.

Once you add in the symbol to the eventsTriggered and you reset the class instance variable, you can use triggerEvent: in a method of the class, and you can set up notifications to dependents using the when:send:to: protocol.  It is possible to trigger an event which nobody cares about, and it happens all the time.  However, when you send the message when:send:to: to an instance of a PsiEventModel subclass, we check to see if the event is in the list of eventsTriggered for the class.  If it is not, you will receive an error:

Bad event: theEventSymbol.

Fix the problem by adding the event in, as discussed about, or by fixing the argument in when:send:to: to use a supported event.


GF/ST Class Hierarchy

The tables below, broken into page-size chunks, show the base GF/ST classes in hierarchical order.  Base image classes from which GF/ST classes inherit are shown in italics.

GF/ST modifies no base image methods on any platform.  For a full listing of added classes and methods, refer to the .SUM files supplied on the distribution media.  Common protocol for GFGraphicObject is summarized in the description for that class, in the next section.

For clarity of presentation, we have provided two page-size tables summarizing the GF/ST classes that have a common hierarchy on all platforms.  Next, we provide one table for each Smalltalk platform supported by GF/ST, showing the specific hierarchy for GF/ST classes on each platform.  For example, each Smalltalk implements a different Collection hierarchy.  Our GFBoundedStack class is buried at the appropriate spot in the Collection hierarchy, which differs between Smalltalks.

GF/ST Class Hierarchy Common to All Platforms
(Other than PsiEventModel)

Object

EvaluableAction1

GFConstraint

GFMultiheadedConstraint

GFPositionConstraint

GFAbstractEndPlug

GFEvaluableActionEndPlug

GFGoEndPlug

GFCategory

GFCoordinateControl

GFGraphicsDisplaySystem

GFDoubleBufferingGraphicsDisplaySystem

GFHandle

GFConnectionHandle

GFTrackHandle

GFParameterizedTrackHandle

GFSelectionTrackHandle

GFAbsoluteTrackHandle

GFLogicalUnit

GFHundredthMillimeter

GFThousandthInch

GFTwip

GFMemento

GFTool

GFCreationTool

GFDrawingActionTool

GFGOActionTool

GFScrollingTool

GFSelectionTool

GFFastSelectionTool

GFFocusTool

1  EvaluableAction is a base image class only in Visual Smalltalk.

GF/ST Classes Common to All Platforms
(PsiEventModel Hierarchy Only)

Object

PsiEventModel

GFDrawingInterface

GFGraphicObject

GFAbstractPathGO

GFClosedPathGO

GFEllipseGO

GFRectangleGO

GFRoundedRectangleGO

GFDirectedPathGO

GFLineGO

GFDependentLineGO

GFPolylineGO

GFBezierGO

GFOrthogonalPathGO

GFDependentOrthogonalPathGO

GFSplineGO

GFCachedGO

GFImageGO

GFButtonGO

GFGroupGO

GFCompositeGO

GFDrawing

GFHostWidgetGO

GFTextGO

GFToolPalette

GF/ST Class Hierarchy Specific to Visual Smalltalk

Object

Collection

IndexedCollection

OrderedCollection

GFBoundedStack

EvaluableAction

Message

GFMessage

GFLocator

GraphicsMedium

Bitmap

GFOpaqueBitmap

GFScratchBitmap

GraphicsTool

TextTool

Pen

GFPen

GFPrinterPen

Rectangle

GFDamagedRegion

GFQuadTreeElement

ViewManager

GFDrawingView

Window

ApplicationWindow

TopPane

GFFloatingPaletteWindow

SubPane

GFDrawingPane

GFOwnDCDrawingPane

GroupPane

GFPalettePane

WindowPolicy

StandardWindowPolicy

GFWindowPolicy

GF/ST Class Hierarchy Specific to VisualWorks

Object

Collection

SequenceableCollection

ArrayedCollection

Array

ActionSequence

OrderedCollection

GFBoundedStack

Controller

WidgetController

GFPaneController

Geometric

Rectangle

GFDamagedRegion

GFQuadTreeElement

GFRectangle

GraphicsContext

ScreenGraphicsContext

GFScalingPen

GraphicsMedium

DisplaySurface

UnmappableSurface

Pixmap

GFPixmap

Message

MessageSend

GFMessage

GFLocator

Model

ApplicationModel

GFDrawingEditor

SimpleDialog

ColorChooser

GFFontChooser

UIPalette

GFFloatingPalette

GF/ST Class Hierarchy Specific to VisualWorks
(Continued)

Object

Paint

SimplePaint

ColorValue

Color

RGBColor

VisualComponent

OpaqueImage

GFOpaqueBitmap

VisualPart

DependentPart

View

GFDrawingPane

GFDrawingView

GF/ST Class Hierarchy Specific to VisualAge

Object

CgGC

GFGC

GFTranslatingGC

GFScalingGC

GFPrintingGC

CgID

CgDrawable

CgPixmap

GFPixmap

GFOpaqueBitmap

GFScratchPixmap

CgRGBColor

Color

RGBColor

Collection

SequenceableCollection

AdditiveSequenceableCollection

OrderedCollection

GFBoundedStack

ArrayedCollection

Array

ActionSequence

ColorDialog

CwWidget

CwBasicWidget

CwComposite

CwDrawingArea

GFDrawingPane

GFOwnDCDrawingPane

GFApplicationWindowES

GFDrawingView

GFFloatingPalette

GFCursorManager

GFEmptyRectangle

GFPrintDialog

GF/ST Class Hierarchy Specific to VisualAge
(Continued)

Object

GFWidgetCreator

GFButtonCreator

GFEntryFieldCreator

GFListPaneCreator

GFTextEditCreator

Message

DirectedMessage

GFMessage

GFLocator

Rectangle

GFDamagedRegion

GFQuadTreeElement

RectangleFromUserWindow


Description of GF/ST Classes

In this section, we provide an alphabetically sorted list of all base GF/ST classes, together with a brief description of each.  The descriptions should all be contained in the class COMMENT method, but you may find this alphabetical sort easier to use.  We adhere to the ParcPlace commenting conventions in GF/ST, showing the kind of object expected in an instance variable in angle brackets (i.e., <>).  Also, when we indicate that an object is an <Action>, we mean that it is a block or an EvaluableAction of some kind - anything that responds to the protocol of EvaluableAction.  EvaluableAction is a base image class in Visual Smalltalk -- the superclass of Message.  Like a block in Smalltalk, an EvaluableAction responds to value or more specifically for EvaluableActions, evaluate.  See the class description in this section for more discussion.

Related Topics:

ActionSequence

Color

ColorChooser

ColorDialog

EvaluableAction

GFAbsoluteTrackHandle

GFAbstractEndPlug

GFAbstractPathGO

GFApplicationWindowES

GFBezierGO

GFBoundedStack

GFButtonCreator

GFButtonGO

GFCachedGO

GFCategory

GFClosedPathGO

GFCompositeGO

GFConnectionHandle

GFConstraint

GFCoordinateControl

GFCreationTool

GFCursorManager

GFDamagedRegion

GFDependentLineGO

GFDependentOrthogonalPathGO

GFDirectedPathGO

GFDoubleBufferingGraphicsDisplaySystem

GFDrawing

GFDrawingActionTool

GFDrawingEditor

GFDrawingInterface

GFDrawingPane

GFDrawingView

GFEllipseGO

GFEmptyRectangle

GFEntryFieldCreator

GFEvaluableActionEndPlug

GFFastSelectionTool

GFFloatingPaletteWindow

GFFocusTool

GFGC

GFGOActionTool

GFGoEndPlug

GFGraphicObject

GFGraphicsDisplaySystem

GFGroupGO

GFHandle

GFHostWidgetGO

GFHundredthMillimeter

GFImageGO

GFLineGO

GFListPaneCreator

GFLocator

GFLogicalUnit

GFMemento

GFMultiheadedConstraint

GFOpaqueBitmap

GFOrthogonalPathGO

GFOwnDCDrawingPane

GFPalettePane

GFPaneController

GFParameterizedTrackHandle

GFPen

GFPixmap

GFPolylineGO

GFPositionConstraint

GFPrintDialog

GFPrinterPen

GFPrintingGC

GFQuadTreeElement

GFRectangle

GFRectangleGO

GFRoundedRectangleGO

GFScalingGC

GFScalingPen

GFScratchBitmap

GFScratchPixmap

GFSelectionTool

GFSelectionTrackHandle

GFSplineGO

GFTextEditCreator

GFTextGO

GFThousandthInch

GFTool

GFToolPalette

GFTrackHandle

GFTranslatingGC

GFTwip

GFWidgetCreator

GFWindowPolicy

PsiEventModel

RectangleFromUserWindow


ActionSequence

Superclass:  Array

Subclasses:  None

Platform Specifics:  ActionSequence is a base image class under Visual Smalltalk only.

Description:  An ActionSequence is a collection of EvaluableActions.  ActionSequences respond to evaluate and return the result of the last EvaluableAction.

Instance Variables:  None


Color

Superclass:  Varies by platform

Subclasses:  Varies by platform

Platform Specifics:  Color is a base image class in Visual Smalltalk.  Superclass is Object in Visual Smalltalk.  Superclass is CgRGBColor in VisualAge.  Superclass is ColorValue in VisualWorks.  The subclass RGBColor is supplied by GF/ST for platforms other than Visual Smalltalk.

Description:  Except for Visual Smalltalk, Color class is provided only for compatibility and centralized, convenient accessing methods for accessing and instantiating colors.  For example, Color red returns an appropriate object for each platform.

Instance Variables:  None


ColorChooser

Superclass:  SimpleDialog

Subclasses:  None

Platform Specifics:  VisualWorks only

Description:  The ColorChooser is a VisualWorks-only GF/ST-supplied dialog for displaying the color palette and allowing you to select a color.  It is used from the Color class method pickAColor, which is invoked from various default menus.

Instance Variables:

colorName - <ValueHolder on: String>

colorValue - <ValueHolder on: RGBColor>

cubesBrightness - <ValueHolder on: Float>  The floating point representation of brightness

cubeColors - <OrderedCollection of: (ValueHolder on: RGBColor)>

returnHolder - <ValueHolder on: RGBColor>


ColorDialog

Superclass:  Object

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The ColorDialog is a VisualAge-only GF/ST-supplied dialog for displaying the color palette and allowing you to select a color.  It is used from the Color class method pickAColor, which is invoked from various default menus.

Instance Variables:  None


EvaluableAction

Superclass:  Object

Subclasses: GFConstraint GFMultiheadedConstraint GFPositionConstraint

Instance Variables:  None

Platform Specifics:  Message is also a subclass in Visual Smalltalk only.  EvaluableAction is a base image class in Visual Smalltalk only.

Description:  EvaluableAction is an abstract class whose subclasses understand action-type protocol, such as evaluate, evaluateWithArguments:.  In Visual Smalltalk, Messages are kinds of EvaluableActions.  In GF/ST implementations other than Visual Smalltalk, EvaluableAction is a simple shell class used to group together GF constraint classes and support coercion methods.  Blocks, while somewhat interchangeable in terms of their utility with EvaluableActions, are more "expensive" to use.

Instance Variables:  None.


GFAbsoluteTrackHandle

Superclass:  GFSelectionTrackHandle

Subclasses:  None

Description:  GFAbsoluteTrackHandle is a specialization that operates with absolute mouse positions, rather than deltas from the previous mouse position.

Instance Variables:  None


GFAbstractEndPlug

Superclass:  Object

Subclasses: GFEvaluableActionEndPlug GFGoEndPlug

Description:  The GFAbstractEndPlug is the abstract superclass for the two kinds of end plugs used at the ends of path GO's.  End plugs allow you to specify via an EvaluableAction or a GO what kind of GO should be placed at the end of a line-type graphic object, such as a GFLineGO.

Instance Variables:

connectionOffset - <Point | GFLocator>  The connectionOffset is the offset from the displayPoint to where the line should start drawing itself from.

displayOffset - <Point | GFLocator>  The displayOffset is the offset from the displayPoint to the origin of the graphicObject that is being used in the lineEndGOPlug.

displayPoint - <Point | GFLocator>  The displayPoint is the point where the line end is located.  It will normally be the start or stop point of the line for which the plug is going to be used.


GFAbstractPathGO

Superclass:  GFGraphicObject

Subclasses:  GFClosedPathGO GFLineGO GFPolylineGO

Description:  The GFAbstractPathGO is the abstract path that models graphic objects based on paths.  No instances are expected to be created of this class.  The responsibilities of the class are related to the path color, width and style.  The display methods in this class merely set the pen information correctly, without actually displaying anything.

Instance Variables:

width - <Integer>  The width of the path

color - <Color>  The color of the path

lineStyle - <Integer>  The line style of the path


GFApplicationWindowES

Superclass:  Object

Subclasses:  GFDrawingView GFFloatingPalette

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  GFApplicationWindowES is a "glue" class for VisualAge only, similar to ViewManager in Visual Smalltalk and ApplicationModel in VisualWorks.  It provides helper methods for the basic GF/ST Drawing Editor user interfaces.

Instance Variables:

shell - <CwTopLevelShell | CwDialogShell>  Either a standard shell or a dialog shell for the application window

main - <CwMainWindow>  The main window for the shell

form - <CwForm>  The form for the shell


GFBezierGO

Superclass:  GFPolylineGO

Subclasses:  None

Description:  The GFBezierGO class represents a Bezier curve with two control points.  The points of the class represent the approximation of the Bezier curve.

Instance Variables:

start - <Point>  The start position

stop - <Point>  The stop position

controlPoint1 - <Point>  The control point, which together with the start point specifies the tangent to the curve at the start point.

controlPoint2 - <Point> The control point, which together with the end point specifies the tangent to the curve at the end point.


GFBoundedStack

Superclass:  OrderedCollection

Subclasses:  None

Description:  The GFBoundedStack represents a stack that has a limit to how far it can grow.  When this bound is reached, and new elements are added, the least recently added elements are discarded.

Instance Variables:

bound - <Integer | UndefinedObject>  The limit of the number of elements stored.  The bounded stack is a subclass of OrderedCollection, but elements should only be added to the stack by the method push:, not add:.


GFButtonCreator

Superclass:  GFWidgetCreator

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The wrapper class for the CwPushButton which is displayed using a GFHostWidgetGO.

Instance Variables:  None


GFButtonGO

Superclass:  GFImageGO

Subclasses:  None

Description:  The GFButtonGO class wraps an image (or creates an image based on a GO) with a border, as specified by the selector held in borderSelector.  It also knows how to create handles that will give the GO a "button-like" behavior when the action for the button is defined (use whenClickedSend:to: to define the clicked action).  The GFButtonGO also has an offset that can be used for positioning where the image is displayed within the button border.

Instance Variables:

borderSelector - <Symbol>  The selector that is sent to the pen to draw the appropriate border on the button.

centerOffset - <Point>  The amount to offset the displaying of the image within the bounds of the button.


GFCachedGO

Superclass:  GFGraphicObject

Subclasses:  GFImageGO PsiAbstractObjectGO

Description:  GFCachedGO represents a graphic object that caches its display with a Bitmap.  This allows complicated graphics to be created once, and quickly displayed.

Instance Variables:

cache - <Bitmap | GFGraphicObject>  Used to represent the display

origin - <Point>  The origin of the graphic object


GFCategory

Superclass:  Object

Subclasses:  None

Description:  The GFCategory represents a category of tools used by a palette.

Instance Variables:

icon - <Bitmap | Icon | Pixmap | CgPixmap>  The visual representation of the category

name - <String>  The textual name of the category

tools - <Array of: GFTool>  The tools of the category

defaultTool - <GFTool>  The default tool of the category


GFClosedPathGO

Superclass:  GFAbstractPathGO

Subclasses:  GFEllipseGO GFRectangleGO

Description:  The GFClosedPathGO is an abstract class that models the common behavior and state of figures that are closed paths.  The primary responsibility of this class is to keep track of the fill color used in the graphic object, and set up the pen correctly based on this information.

Instance Variables:

fillColor - <Color>  The color to fill the closed area with.


GFCompositeGO

Superclass:  GFGraphicObject

Subclasses:  GFDrawing

Description:  GFCompositeGO represents a composition of graphic objects.  These GO's may then be manipulated as a group, while maintaining the ability to manipulate individual  GO's contained within the grouping.  The display of the contained GO's is clipped to the visible area of the composite.

Instance Variables:

displayBox - <Rectangle>  The cached display area

graphicObjects - <SortedCollection of: GFGraphicObject>  Graphic objects sorted in Z order

visibleArea - <Rectangle>  The clipping region

showVisibleArea - <Boolean>  Determines whether to display the bounds of the visible area


GFConnectionHandle

Superclass:  GFHandle

Subclasses:  None

Description:   GFConnectionHandle represents a manipulator used to connect two graphic objects.

Instance Variables:

findTargetAction - <Action>  The action to be evaluated that finds the target graphic object.  The action accepts two arguments, the GFDrawingInterface and the target point currently under the mouse point.

targetPositionAction - <Action>  The action to be evaluated that returns the target position of the connection.  The action accepts two arguments, the graphic object to be connected, and the target point under the mouse point.

connectAction - <Action>  The action to be evaluated that produces a connection graphic object.  The action accepts two arguments, the start location and the stop location.

locationGenerationAction - <Action>  The action to be evaluated that produces a locator for the stop location.  The action accepts two arguments, the graphic object, and the target point under the mouse point.

connectedAction - <Action>  The action to be evaluated after the connection has been made.  The action accepts two arguments, the connection produced by the connectAction, and the location produced by the locationGenerationAction.


GFConstraint

Superclass:  EvaluableAction

Subclasses:  None

Description:  GFConstraint is a generic constraint mechanism.  It simply evaluates an action when told to.

Instance Variables:

action - <EvaluableAction>  The action to evaluate when the constraint is fired.  The action takes no arguments.

observedObjects - <Array of: Object>  The objects that the constraint is dependent upon.


GFCoordinateControl

Superclass:  Object

Subclasses:  None

Description:  The GFCoordinateControl represents a coordinate system, and the behavior associated with it.  The GFCoordinateControl uses the native coordinate system of the host system, and has methods for converting to and from the native coordinate system and the user defined coordinate system.  The system also controls the setup of pens to allow WYSIWYG displays, scaling, etc.

Instance Variables:

logicalUnit - <GFLogicalUnit>  The logical units of the pixels

origin - <Point>  The origin of the defined coordinate system referenced to the native coordinate system

axisDirection - <Point>  The sign change required to convert from the native coordinate systems axis to the defined coordinate system

scale - <Point>  The multiplicative scale imposed on the system

extent - <Point>  The extent of the native coordinate system

grid - <Point>  The grid modulus

useGrid - <Boolean>  Determines whether to snap points to the grid modulus


GFCreationTool

Superclass:  GFTool

Subclasses:  None

Description:  GFCreationTool is used to create GFGraphicObjects.

Instance Variables:

className - <Symbol>  The symbol of the class to create

creationSelector - <Symbol>  The selector to send to the class


GFCursorManager

Superclass:  Object

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  A shell class that provides the common behavior to set the cursor and restore it when a set of expressions or block is done executing.  The equivalent in VisualWorks is sending the message showWhile: to a Cursor.

Instance Variables:

cursorConstant - <SmallInteger>  The cursor constant which will be restored when a block is done executing.


GFDamagedRegion

Superclass:  Rectangle

Subclasses:  None

Description:  GFDamagedRegion is a specialized rectangle to prevent the needless, and expensive creation of rectangles during damage operations

Instance Variables:

valid - <Boolean>  Flag to signal whether the receiver contains a valid region or not.


GFDependentLineGO

Superclass:  GFLineGO

Subclasses:  None

Description:  The GFDependentLineGO is used to represent an line GO whose position is constrained.  This class only moves and updates it's position by these constraints.  This class knows how to transform itself into other dependent connection objects, such as GFDependentOrthogonalPathGO.

Instance Variables:

startConstraint - <GFPositionConstraint>  The constraint that determines the start position

stopConstraint - <GFPositionConstraint>  The constraint that determines the stop position


GFDependentOrthogonalPathGO

Superclass:  GFOrthogonalPathGO

Subclasses:  None

Description:  The GFDependentOrthogonalPathGO is used to represent an orthogonal path GO whose position is constrained.  This class only moves and updates its position by these constraints.  This class knows how to transform itself into other dependent connection objects, such as GFDependentLineGO.

Instance Variables:

startConstraint - <GFPositionConstraint>  The constraint that determines the start position

stopConstraint - <GFPositionConstraint>  The constraint that determines the stop position


GFDirectedPathGO

Superclass:  GFAbstractPathGO

Subclasses:  GFLineGO GFPolylineGO

Description:  The GFDirectedPathGO class is the abstract superclass of all path-type GO's which have directionality; i.e., a start point and a stop point.  Subclasses actually implement start point and end points according to the kind of GO they are, since it is awkward to let, for example, GFPolylineGO's only hold onto the points in between the start and stop point which they could inherit from this class.

Instance Variables:

startPlug - <nil | Subclass of: GFAbstractEndPlug> - The end plug at the start point

stopPlug - <nil | Subclass of: GFAbstractEndPlug> - The end plug at the end point


GFDoubleBufferingGraphicsDisplaySystem

Superclass:  GFGraphicsDisplaySystem

Subclasses:  None

Description:  GFDoubleBufferingGraphicsDisplaySystem provides the smooth double buffering display.  It also consumes more resources.

Instance Variables:

pen - <GFPen | GFScalingPen | GFScalingGC>  The pen or GC of the displayPane, depending on the platform

scratch - <GFScratchBitmap>  The bitmap used for double buffering display


GFDrawing

Superclass:  GFCompositeGO

Subclasses:  None

Description:  GFDrawing represents the top level of the display hierarchy.  As such, it is responsible for maintaining the display of the components.  One major difference between GFDrawing and all other GFGraphicObjects is that the container of the GFDrawing is the GFDrawingInterface operating on the drawing.

Instance Variables:

firstGO - <GFGraphicObject>  The bottom of the z-order

lastGO - <GFGraphicObject>  The top of the z-order

coordSystem - <GFCoordinateControl>  The coordinate system

displayGrid - <Boolean>  Determines whether to display the grid

gridColor - <Color>  The color of the displayed grid


GFDrawingActionTool

Superclass:  GFTool

Subclasses:  None

Description:  GFDrawingActionTool is used to perform actions on the drawing as a whole

Instance Variables:

activateBlock - <Action>  The action to perform when activated

deactivateBlock - <Action>  The action to perform when deactivated


GFDrawingEditor

Superclass:  ApplicationModel

Subclasses:  None

Platform Specifics:  VisualWorks only

Description:  The GFDrawingEditor is a VisualWorks-only class for the Drawing Editor.  Other than the interface and view (a GFDrawingPane), its aspects are used for display of information in the status pane at the bottom.

Instance Variables:

interface - <GFDrawingInterface>

gfView - <GFDrawingPane>

currentCategory - <ValueHolder on: String>  The name of the currently selected GFCategory

handle - <ValueHolder on: String>  The description of the active handle

currentPoint - <ValueHolder on: String>  The current mouse point as a string

currentTool - <ValueHolder on: String>  The description of the currently selected tool


Class Hierarchy, Protocol, and Events - Description of GF/ST Classes - GFDrawingInterface
GFDrawingInterface

Superclass:  PsiEventModel

Subclasses:  None

Description:  GFDrawingInterface is the "glue" class that ties together the GF/ST framework.  It is the central point used to manipulate a drawing and the display thereof.  It ties together the drawing, the display, the tools and the handles used.  Its primary responsibilities are the correct manipulation of the drawing, the handling and tracking of selections in the drawing, the management of handles, and selection of tools.

Instance Variables:

palette - <GFPalette>  The palette of tools available to the interface

selections - <Collection of: GFGraphicObject>  The currently selected graphic objects in the drawing

handles - <Collection of: GFHandle>  The currently active handles

drawing - <GFDrawing>  The drawing being manipulated

display - <GFGraphicsDisplaySystem>  The display system used to display the drawing

hideHandles - <Integer>  A count to ensure correct display of handles

memento - <GFBoundedStack>  Used to hold the undo information


Class Hierarchy, Protocol, and Events - Description of GF/ST Classes - GFDrawingPane
GFDrawingPane

Superclass:  Varies by platform

Subclasses:  GFOwnDCDrawingPane

Platform Specifics:  Superclass is SubPane in Visual Smalltalk, View in VisualWorks, and CwDrawingArea in VisualAge.  GFDrawingPane should probably be called GFDrawingView in VisualWorks, since it is a View, but the GFDrawingPane name has been retained for compatibility across platforms.

Description:  GFDrawingPane is the surface upon which the drawing is displayed.  Its sole purpose in life is to correctly scroll, and pass on relevant events to the display system, which is the behavior model for the GFDrawingPane.

Instance Variables:

displaySystem - <GFGraphicsDisplaySystem> This is the model of the drawing pane.

scrollPoint - <Point> The amount to scroll in the x and y direction

pagePoint - <Point> The amount to page in the x and y direction

pen - <GFScalingPen>  For VisualWorks only.


GFDrawingView

Superclass:  Varies by platform

Subclasses:  Varies by platform

Platform Specifics:  Superclass is ViewManager for Visual Smalltalk and GFApplicationWindowES for VisualAge.  GFDrawingViewWithStatus is a subclass if you have the GF/ST demo applications loaded under Visual Smalltalk.  This is because "status panes" is an optionally-loaded SLL in Visual Smalltalk.  VisualWorks users should refer to GFDrawingEditor, a subclass of ApplicationModel.

Description:  GFDrawingView is the basic "Drawing Editor" application for Visual Smalltalk and VisualAge.  Under VisualWorks, we use the class GFDrawingEditor.  (Sorry about the confusion).  It is included as part of the base GF/ST framework because it is so useful for helping to develop new applications and test new GO's.  From a development standpoint, this class gives you what its name implies: a view of a drawing.

Instance Variables (Visual Smalltalk):

interface - <GFDrawingInterface>  The instance of GFDrawingInterface for the drawing

label - <String>  The label for the Window

Instance Variables (VisualAge):

interface - <GFDrawingInterface>  The instance of GFDrawingInterface for the drawing

paletteWindow - <GFFloatingPalette>  The floating palette for this Drawing Editor

gfView - <GFDrawingPane>

titleString - <String>  The label

instDict - <Dictionary with key: String and value: CwWidget>  A dictionary to hold instances of the child widgets used in the status bar


GFEllipseGO

Superclass:  GFClosedPathGO

Subclasses:  None

Description:  The GFEllipseGO represents a graphic object of an ellipse.  The class keeps track of the rectangle that represents the ellipse.

Instance Variables:

ellipse - <Rectangle>  The rectangle that represents the major and minor axis


GFEmptyRectangle

Superclass:  Object

Subclasses:  None

Description:  The GFEmptyRectangle lets us merge with another rectangle and respond with that rectangle.  The merge operation is common in GF/ST.  Rather than initialize an instance variable to a Rectangle with origin at 0@0 and no extent, we initialize empty rectangles to GFEmptyRectangles.

Instance Variables:  None


GFEntryFieldCreator

Superclass:  GFWidgetCreator

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The wrapper class for the CwText which is being displayed using a GFHostWidgetGO.

Instance Variables:  None


GFEvaluableActionEndPlug

Superclass:  GFAbstractEndPlug

Subclasses:  None

Description:  The GFEvaluableActionEndPlug lets you supply an arbitrary EvaluableAction to build the line ending appropriate for a path GO.  For example, the arrow heads at the ends of lines are built using a GFEvaluableActionEndPlug.  Look in the method GFDirectedPathGO>>endArrowPlug to see how the GFEvaluableActionEndPlug is used for arrow-type line endings.

Instance Variables:

action - <EvaluableAction>  The action draws the end plug.

shapeAction - <EvaluableAction>  The shapeAction draws the mask for the end plug.

extent  - <Point | GFLocator>  The extent of the end plug.


GFFastSelectionTool

Superclass:  GFSelectionTool

Subclasses:  None

Description:  GFFastSelectionTool is a specialization used to move graphic objects by displaying their outlines, rather than redrawing every movement.


GFFloatingPaletteWindow

Superclass:  TopPane

Subclasses:  None

Platform Specifics:  Visual Smalltalk only

Description:  The GFFloatingPaletteWindow provides the dreaded floating palette behavior.  Although it is only used in the Drawing Editor, it is included as part of the framework because we want to make life a little easier for those of you who want a floating palette in your own applications, something which is non-trivial to figure out on your own in Visual Smalltalk.

Instance Variables:

palette - <GFToolPalette>  The model for the floating palette

categoryPane - <GFPalettePane>  The view of the categories

toolPane - <GFPalettePane>  The view of the tools


GFFocusTool

Superclass:  GFSelectionTool

Subclasses:  None

Description:  GFFocusTool is a specialization that disallows the selection of groups of graphic objects


GFGC

Superclass:  CgGC

Subclasses:  GFTranslatingGC

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The GFGC class is an abstract superclass to supports platform-independent protocols for GF/ST graphics contexts (or Pens under Visual Smalltalk) and caching of colors.

Instance Variables:

position - <Point>

pixelValueCache - <Dictionary with key: CgRGBColor value: CgRGBColor>  A dictionary that maps instances of CgRGBColor to the closest matching CgRGBColor in the palette.

pixmap - <KindOf:  CgDrawable> - The drawable for the GC.

fillColor <CgRGBColor> - The fill color used for filled GO's


GFGOActionTool

Superclass:  GFTool

Subclasses:  None

Description:  GFGOActionTool is used to perform arbitrary actions on selected graphic objects.

Instance Variables:

actionBlock - <Action>  The action to evaluate on the selections


GFGoEndPlug

Superclass:  GFAbstractEndPlug

Subclasses:  None

Description:  The GFGoEndPlug lets you supply an arbitrary graphic object as the line ending appropriate for a path GO.  For examples and graphically displayed documentation, see the following class methods in GFDrawingInterface:  rectangleLineEndExample, triangleLineEndExample, and circleLineEndExample.

Instance Variables:

go - <KindOf:  GFGraphicObject>  The GO which is used as the end plug


Class Hierarchy, Protocol, and Events - Description of GF/ST Classes - GFGraphicObject
GFGraphicObject

Superclass:  PsiEventModel

Subclasses:  GFAbstractPathGO GFCachedGO GFCompositeGO GFHostWidgetGO GFTextGO

Description:  The GFGraphicObject represents the abstract behavior of all graphic objects.  Since the class is an abstract class, no instances are ever created.  GFGraphicObjects can display themselves using a pen, generate handles, etc.  Every graphic object has a container.  Graphic objects are kept in a linked list in the containing drawing (the root of the display hierarchy) in z-order.  The head of the linked list is the bottom of the display, and the last GO in the list is at the top of the display.

All graphic objects are described in what is known as the native coordinate system.  This coordinate system is set up depending on the platform.  On all platforms and windowing systems except for Visual Smalltalk under OS/2, this coordinate system is set up with 0@0 in the upper left corner of the surface with the x axis increasing to the right, and the y axis increasing downward.  Visual Smalltalk for OS/2 adheres to the native OS/2 convention of 0@0 in the lower left, x increasing to the right, and y increasing upward.  All points that graphic objects use must be integers, and cannot be negative values.

Rather than impose a heavyweight coordinate system on each graphic object, the framework relies on the drawing which contains the GO's to supply a coordinate system.  Therefore, GFGraphicObject supplies a protocol for converting between the native coordinate system and the actual coordinate system (user coordinate system).  The default coordinate system is isomorphic to the native coordinate system.  For more information on the coordinate system used, see the class GFCoordinateControl.  When manipulating coordinate systems, you have to be very careful.  It's easy to get confused as to what coordinate system a point is defined in, as the point itself has no knowledge of this, nor should it.  Points are always interpreted relative to a coordinate system, and you have to know which coordinate system you're referencing when using a point.  Be warned.

Instance Variables:

container - <KindOf: GFGraphicObject>  The container of the graphic object.

prev - <KindOf: GFGraphicObject>  The figure that is behind me in Z-Order.

next - <KindOf: GFGraphicObject>  The figure that is in front of me in Z-Order.

Protocol Categories:  (Partial List)

Display

displayWith:

displayWith:at:

displayWith:window:

displayShapeWith:

displayShapeWith:at:

redraw

willDamage

willDamage:

damaged

damaged:

Hit Testing

containedBy:

containsPoint:

intersects:

graphicObjectAt:

Coordinate Mapping

coordinateToNative:

nativeToCoordinate:

Utility

absolute:

align:with:

disableDefaultInteraction

disableHandles

disableMenu

disablePressHandle

enableDefaultInteraction

enableHandles

enableMenu

enablePressHandle

offCenter:

offCorner:

offOrigin:

relative:

Accessing

bottom

bottomCenter

bottomLeft

bottomRight

boundingBox

center

container

corner

displayBox

extent

grid

gridPoints

left

leftCenter

locator

metaObject

origin

right

rightCenter

top

topCenter

topLeft

topRight

Positioning

translateBy:

translateTo:

snapToGrid

Handles

generateHandles

generatePressHandle


GFGraphicsDisplaySystem

Superclass:  Object

Subclasses:  GFDoubleBufferingGraphicsDisplaySystem

Description:  GFGraphicsDisplaySystem is the model of the display system for GF/ST.  It coordinates the display of the interface, scrolling action, mouse tracking action, etc.  In the great tradition of models, this is the model for the GFDrawingPane.  All the action happens here.

Instance Variable

displayPane - <GFDrawingPane>  The host object used to display

interface - <GFDrawingInterface>  The interface connected

damagedRegion - <GFDamagedRegion>  The region currently damaged

mousePoint - <Point>  The current point

minFactor - <Point>  Minimum factor used to prevent round off errors

visibleArea - <Rectangle>  The visible scrolling area

hostActions - <OrderedCollection of: Action>  Actions to evaluate for host widget display


GFGroupGO

Superclass:  GFImageGO

Subclasses:  None

Description:  GFGroupGO represents the grouping of graphic objects in a drawing.  The figures cannot be manipulated individually, and their representation may be cached by a bitmap.

Instance Variables:

graphicObjects - <IndexedCollection of: GFGraphicObject>  The components

cacheFlag - <Boolean>  Determines whether the display is cached or not

hostWidgets - <IndexedCollection of: GFHostWidgetGO>  Special handling for host widgets


Class Hierarchy, Protocol, and Events - Description of GF/ST Classes - GFHandle
GFHandle

Superclass:  Object

Subclasses:  GFConnectionHandle GFTrackHandle

Description:  GFHandle represents a manipulator on a GFGraphicObject that responds to mouse input.  GFHandles know how to display themselves, give a description of their function, find out where they are located, generate mementos, and whether or not to snap the mouse point to the grid of the drawing or limit the mouse point to the drawing's extent.

Instance Variables:

description - <String>  A short description of the handle

releaseAction - <Action>  The action to be performed when the handle is released

mementoType - <Symbol>  The type of memento generated by changes

displayGO - <GFGraphicObject>  The graphic object used to (optionally) display the handle

snapToGrid - <Boolean>  Flag to snap the mouse point to the grid of the drawing

limit - <Boolean>  Flag to limit the mouse point to the extent of the drawing


GFHostWidgetGO

Superclass:  GFGraphicObject

Subclasses:  None

Description:  GFHostWidgetGO is the graphic object representation of host operating system widgets.  The major difference between regular GO's and host widget GO's is that host widgets are always in the foreground.  You can't overlap them with graphic objects.  This is a fundamental limitation that doesn't appear to be solvable.  However, any suggestions, solutions you might have, please forward them to Polymorphic.  Also, because we can't really control the drawing of host GO's as efficiently as regular GO's, they flash noticeably when displayed.  The support provided is pretty basic.  This class is really a proof of concept, and a skeleton of what is needed to supply this kind of support under GF/ST.  Note that GFHostWidgetGO's in VisualWorks are actually VisualWorks widgets, not host window system widgets as are supported under Visual Smalltalk and VisualAge.

Instance Variables:

area - <Rectangle>  The rectangle that represents the visible area of the widget

hostWidget - <Window>  The host component


GFHundredthMillimeter

Superclass:  GFLogicalUnit

Subclasses:  None

Description:  GFHundredthMillimeter represents logical units measured in 100ths of a millimeter.  This class is a stop gap until real units are implemented (That means a major overhaul of Number, Point, and Rectangle arithmetic).

Instance Variables:  None


GFImageGO

Superclass:  GFCachedGO

Subclasses:  GFButtonGO GFGroupGO

Description:  GFImageGO represents an image (Bitmap or Icon).  The image is stretched to fit the desired area.

Instance Variables:

extent - <Point>  The extent of the displayed region


GFLineGO

Superclass: GFDirectedPathGO

Subclasses: GFDependentLineGO

Description:  The GFLineGO class represents the graphic object of a line.

Instance Variables:

start - <Point>  The start position of the line

stop - <Point>  The stop position of the line


GFListPaneCreator

Superclass:  GFWidgetCreator

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The wrapper class for the CwList which is being displayed using a GFHostWidgetGO.

Instance Variables:

list - <OrderedCollection of: String>  The initial list displayed in the CwList.


Class Hierarchy, Protocol, and Events - Description of GF/ST Classes - GFLocator
GFLocator

Superclass:  Message

Subclasses:  None

Description:  The GFLocator class is a Message that evaluates to a point.  Some specialized methods for copying and instance creation are provided.  Other than that, it's just a Message.

Instance Variables:  None


GFLogicalUnit

Superclass:  Object

Subclasses:  GFHundredthMillimeter GFThousandthInch GFTwip

Description:  GFLogicalUnit is a stop gap measure until real units are implemented.

Instance Variables:

resolution - <Number>


GFMemento

Superclass:  Object

Subclasses:  None

Description:  GFMemento allows for the recording of state information while trying to preserve encapsulation.  It should only be created via one of the #originator:state: variants.  GFMemento will only allow access to the type and state information if the conditions for allowing access are met.  Those conditions are:  if a key symbol is provided, the originator and the object requesting the information must respond to the symbol in an = test.  If the key is nil, then the requesting object must be == to the originator.

Instance Variables:

type - <Symbol | nil>  Should be a selector that the restoring object will understand.  If a type is specified, it is used as a selector sent to the restoring object with state as the parameter.  An example of this would be if state was a Point, and the restoring object was a Rectangle, and type was #extent:.  Given this setup, when the memento is restored, the restoring object (a Rectangle) is told to perform #extent: with the point as the argument.

state - <OrderedCollection of:  Message>  When type is not nil, the restoring object is told there to perform the type selector with state as the argument.  When type is nil, state is an OrderedCollection of Messages which are evaluated in order.


GFMultiheadedConstraint

Superclass:  EvaluableAction

Subclasses:  None

Description:  GFMultiheadedConstraint is used to implement a constraint with multiple sources.

Instance Variables:

sources - <Array of: Object>  These are the sources of the constraint.

sink - <Object>  This is the target of the constraint

action - <EvaluableAction>  This is the action to be evaluated that implements the constraint.  It takes two arguments, the first being the sources, the second being the sink.

selector - <Symbol>  This is the selector of the message that the sink performs to enforce the constraint.  The selector takes one argument, which is the result of evaluating the action.


GFOpaqueBitmap

Superclass:  Varies by platform

Subclasses:  None

Platform Specifics:  Superclass is Bitmap for Visual Smalltalk, OpaqueImage for VisualWorks, and GFPixmap for VisualAge.

Description:  GFOpaqueBitmap is a bitmap that allows transparency (see-through areas).  It is used in GFGroupGO's.

Instance Variables:  None


GFOrthogonalPathGO

Superclass:  GFPolylineGO

Subclasses:  GFDependentOrthogonalPathGO

Description:  The GFOrthogonalPathGO class represents the shortest orthogonal path between two points.  These paths must have a start direction and a stop direction.  These directions are encoded with integers, codified in the class variables: Bottom, Left, Right, Top.  Any reference to these directions should be made from the accessing methods for these directions on the class.

Instance Variables:

start - <Point>  The start position of the path

stop - <Point>  The stop position of the path

startDirection - <Integer>  The direction from which the path originates

stopDirection - <Integer>  The direction from which the path terminates


GFOwnDCDrawingPane

Superclass:  GFDrawingPane

Subclasses:  None

Description:  The GFOwnDCDrawingPane is just a GFDrawingPane with its own device context, something which is necessary for double-buffering.  It uses a GFDoubleBufferingGraphicsDisplaySystem as its model.

Instance Variables:  None


GFPalettePane

Superclass:  GroupPane

Subclasses:  None

Description:  GFPalettePane is the view of a palette of tools or categories.  It can resize itself depending on the number of icons in the display

Instance Variables:

buttonPressed - <Integer>  The index of the current button pressed.

selectedTool - <Integer>  The index of the currently selected tool.

tools - <Array of: GFCategory or GFTool>  The tools being displayed.

toolsRects -<Array of: Rectangle>  The bounding boxes of the tools.

colors - unused.

toolsBitmap - <Bitmap>  The cached display of the tools displayed.


GFPaneController

Superclass:  WidgetController

Subclasses:  None

Platform Specifics:  VisualWorks only

Description:  This class implements the modifications of the WidgetController class which are required for the GFDrawingPane direct-manipulation behavior.

Instance Variables:

doubleClickEvent - <Association with key: #doubleCick and value: Point>  The Point value of this association is the location of the double-click which has occurred.

activateControlLoop - <Boolean>  A flag used as an indication that when a double click event (or something similar) is registered via some means (i.e. #downcastEvent*) other than provided by the superclass, WidgetController.


GFParameterizedTrackHandle

Superclass:  GFTrackHandle

Subclasses:  None

Description:  GFParameterizedTrackHandle is a specialization that allows an invariant parameter to be passed to the change action, which now accepts two arguments.

Instance Variables:

changeParameter - <Object>  The object to be passed to the change action along with the result of the sense action


GFPen

Superclass:  Pen

Subclasses:  None

Description:  A GFPen is a specialization of Pen that allows quicker access to the device context handle than the standard Visual Smalltalk implementation.

Instance Variables:  None


GFPixmap

Superclass:  Varies by platform

Subclasses:  None

Platform Specifics:  Superclass is Pixmap for VisualWorks and CgPixmap for VisualAge.

Description:  This class implements the modifications to the platform-specific superclass, primarily to handle issues associated with scaling.

Instance Variables:

pen - <GFScalingPen | GFScalingGC>  The scaling pen or graphics context used to render the pixmap


GFPolylineGO

Superclass: GFDirectedPathGO

Subclasses:  GFBezierGO GFOrthogonalPathGO GFSplineGO

Description:  The GFPolylineGO represents graphic objects composed of multiple, connected line segments.

InstanceVariables:

points - <IndexedCollection>  The points that define the line segments

origin - <Point>  The origin of the display area defined by the line segments

extent - <Point>  The extent of the display area defined by the line segments

closed - <Boolean>  Whether the line segments form a closed path or not

fillColor - <Color>  The fill color to use if closed


Class Hierarchy, Protocol, and Events - Description of GF/ST Classes - GFPositionConstraint
GFPositionConstraint

Superclass:  EvaluableAction

Subclasses:  None

Description:  GFPositionConstraint is an action that, when evaluated, enforces a constraint of position.

Instance Variables:

receiver - <GFGraphicObject>  This is the object whose position is constrained.

locator - <GFLocator>  This is the locator of the constraint, which provides the constraint point.

settingMessage - <Symbol>  This is the selector the receiver performs to implement the constraint.  The argument of this message is the point provided by the locator.


GFPrintDialog

Superclass:  Object

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The GFPrintDialog is a simple class used to coordinate potentially multi-page printing of drawings with the standard printer shell facilities of IBM Smalltalk.  It coordinates callbacks from the printer shell.  The skeleton for the approach in this class was lifted from pages 314-316 in the IBM Smalltalk Programmer's Reference V3.0.

Instance Variables:

done - <Boolean>  Set to true when done

gc - <CgGC>  The graphics context for the printer shell window

printTarget <GFDrawing> - The drawing to be printed


GFPrinterPen

Superclass:  Pen

Subclasses:  None

Description:  A GFPrinterPen is a specialization of Pen used for GF/ST.  The specializations allow for the correct printing of Bitmaps.

Instance Variables:  None


GFPrintingGC

Superclass:  GFScalingGC

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The GFPrintingGC is a specialization of the GFScalingGC used for printing.  It ensures proper rounding of point values and bypasses the cache of pixelValueForColor.


GFQuadTreeElement

Superclass:  Rectangle

Subclasses:  None

Description:  GFQuadTreeElement represents a node in a Quad Tree.  A Quad Tree organizes its elements by area, and therefore can efficiently access information by point queries, or by area queries.  The class is not as general as it could be, and is highly specialized for use by GFDrawing.

Instance Variables:

upperL - <GFQuadTreeElement>  The upper left child

upperR - <GFQuadTreeElement>  The upper right child

lowerL - <GFQuadTreeElement>  The lower left child

lowerR - <GFQuadTreeElement>  The lower right child

leafData - <Set>  The collection of elements held by the tree

extent - <Point>  The cached extent


GFRectangle

Superclass:  Rectangle

Subclasses:  None

Platform Specifics:  VisualWorks only

Description:  GFRectangle is a specialized Rectangle that holds onto a scale value.  It is used internally by GFTextGO's to handle the problems associated with scaling.  Fonts, when scaled, may only be approximate, or extend beyond the bounds of the scaled GO.

Instance Variables:

scale - <Point>  A Point whose x and y values are Floats, specifying a scale factor in the x and y direction respectively.


GFRectangleGO

Superclass:  GFClosedPathGO

Subclasses:  GFRoundedRectangleGO

Description:  The GFRectangleGO class represents the graphic object of a rectangle.

Instance Variables:

rectangle - <Rectangle>  The rectangle


GFRoundedRectangleGO

Superclass:  GFRectangleGO

Subclasses:  None

Description:  The GFRoundedRectangleGO class represents the graphic object of a rectangle that has rounded corners.

Instance Variables:

ellipse - <Point>  The point describing the ellipse used to generate the rounded corners


GFScalingGC

Superclass:  GFTranslatingGC

Subclasses:  GFPrintingGC

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The GFScalingGC class adds scaling capabilities to the GFTranslatingGC by caching a scale factor and applying it in pixel and point transformations.  It also caches font information.

Instance Variables:

scale - <Number>  The scale factor applied to transformations

cachedFonts - <IdentityDictionary with key: (CgFont | CgFontStruct) and value: (CgFont | CgFontStruct)>  An IdentityDictionary used to map the unscaled font to the closest matching scaled font or font struct


GFScalingPen

Superclass:  ScreenGraphicsContext

Subclasses:  None

Platform Specifics:  VisualWorks only

Description:  The GFScalingPen is the scaling ScreenGraphicsContext used to implement scaling in VisualWorks.  Host windowing systems typically support scaling directly, but VisualWorks graphics contexts do not.

Instance Variables:

scale - <Boolean>  The scale is true if scaleX or scaleY is other than 1.0; else, false


GFScratchBitmap

Superclass:  Bitmap

Subclasses:  None

Platform Specifics:  Visual Smalltalk only

Description:  GFScratchBitmap is used to correct problems associated with saving image when using ownDC bitmaps.

Instance Variables:  None


GFScratchPixmap

Superclass:  GFPixmap

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The GFScratchPixmap is used for double-buffering in the GFGraphicsDisplaySystem.  It uses a GFTranslatingGC as its pen, rather than a GFGC.

Instance Variables:  None


GFSelectionTool

Superclass:  GFTool

Subclasses:  GFFastSelectionTool GFFocusTool

Description:  GFSelectionTool is used to select, and manipulate graphic objects.

Instance Variables:  None


GFSelectionTrackHandle

Superclass:  GFTrackHandle

Subclasses:  GFAbsoluteTrackHandle

Description:  GFSelectionTrackHandle is a specialization that operates on the selections of a drawing as a whole, rather than on an individual basis.

Instance Variables:  None


GFSplineGO

Superclass:  GFPolylineGO

Subclasses:  None

Description:  The GFSplineGO represents the approximation of a spline function.  The points of the class represent the line segments of this approximation.

Instance Variables:

controlPoints - <IndexedCollection of: Point>  The control points of the spline

derivatives - <IndexedCollection of: Number>  The derivatives at each knot


GFTextEditCreator

Superclass:  GFWidgetCreator

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The wrapper class for the CwText which is displayed using a GFHostWidgetGO.

Instance Variables:  None


GFTextGO

Superclass:  GFGraphicObject

Subclasses:  None

Description:  GFTextGO represents a single line of text in a drawing.

Instance Variables:

box - <Rectangle>  The box that exactly surrounds the text.

color - <Color>  The color of the text.

font - <Font>  The font used for text display.

text - <String>  The text displayed by the GO.


GFThousandthInch

Superclass:  GFLogicalUnit

Subclasses:  None

Description:  GFThousandthInch represents logical units measured in 1000ths of an inch.  This class is a stop gap until real units are implemented (That means a major overhaul  of Number, Point, and Rectangle arithmetic).

Instance Variables:  None


Class Hierarchy, Protocol, and Events - Description of GF/ST Classes - GFTool
GFTool

Superclass:  Object

Subclasses:  GFCreationTool GFDrawingActionTool GFGOActionTool GFSelectionTool

Description:  GFTool represents the mode of interaction with the drawing.  It receives all the user input, filters it, and determines who to send the input to (whether to the selections, or the handles), and can perform actions on its own.  It is a relatively simple class, but without it, representation of the current mode of interaction would be extremely hard to model.

Instance Variables:

icon - <Icon> The icon that represents the tool.

cursor - <CursorManager> The cursor used by the tool.

interface - <GFDrawingInterface> The interface the tool is for.

name - <String>  The textual description of the tool.


GFToolPalette

Superclass:  PsiEventModel

Subclasses:  None

Description:  The GFToolPalette is a non-visual class that represents the notion of a palette of tools.  The tools are held in categories.  When a tool is activated, it becomes the 'activeTool', and when a category is activated, it becomes the 'activeCategory'.  The GFToolPalette can be displayed in a GFFloatingPaletteWindow; however, you can use a GFToolPalette in your application to help organize and track tool selections without using the other class.

Instance Variables:

activeTool - <GFTool>  The currently active tool.

activeCategory - <GFCategory>  The currently active tool category.

categories - <OrderedCollection of:  GFCategory>  Holds the available categories that can be selected.


Class Hierarchy, Protocol, and Events - Description of GF/ST Classes - GFTrackHandle
GFTrackHandle

Superclass:  GFHandle

Subclasses:  GFParameterizedTrackHandle GFSelectionTrackHandle

Description:  GFTrackHandle represents direct manipulation action that tracks mouse movement.  When the mouse position is changed, the sense action is evaluated with the delta point of this change.  The result of this action is then sent to the change action.

Instance Variables:

sense - <Action>  An action evaluated with the delta point of the mouse movement.  If there is no target for this action, the owner of the handle is used.  If no sense action is defined, the result of the sense is the delta point.

change - <Action>  An action evaluated with the result of the sense action.  If the change has no target, the GO selected is used as the target of the action.

scrollFlag - <Boolean>  Determines whether to scroll the display to track the mouse.


GFTranslatingGC

Superclass:  GFGC

Subclasses:  GFScalingGC

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The GFTranslatingGC tracks and accounts for the offset used for drawing operations, since the drawing size can exceed the displayable area shown on the screen.

Instance Variables:

offset - <Point>  The display offset used for drawing operations


GFTwip

Superclass:  GFLogicalUnit

Subclasses:  None

Description:  GFTwip represents logical units measured in 1440ths of an inch.  This class is a stop gap until real units are implemented (That means a major overhaul of Number, Point, and Rectangle arithmetic).

Instance Variables:  None


GFWidgetCreator

Superclass:  Object

Subclasses: GFButtonCreator GFEntryFieldCreator GFListPaneCreator GFTextEditCreator

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The GFWidgetCreator class is an abstract class.  It supports common protocol for the wrapper classes that are used to wrap specific host widgets displayed by the GFHostWidgetGO under VisualAge.

Instance Variables:

widget - <KindOf: CwWidget>  The widget

labelString - <String>  The label for the widget, specific uses of which are subclass dependent

rectangle - <Rectangle>  The rectangle specifying the widget x, y, width, and height

framingBlock - Not used at present.

fontList - <CwFontList>  The font list for the widget

attributeAction - <Action>  An action which can be evaluated with the widget as the argument to specify its attributes


GFWindowPolicy

Superclass:  StandardWindowPolicy

Subclasses:  None

Platform Specifics:  Visual Smalltalk only

Description:  The GFWindowPolicy is a specialization of the StandardWindowPolicy to override a few of the default values in StandardWindowPolicy.

Instance Variables:  None


Class Hierarchy, Protocol, and Events - Description of GF/ST Classes - PsiEventModel
PsiEventModel

Superclass:  Object

Subclasses:  GFDrawingInterface GFGraphicObject GFToolPalette

Description:  PsiEventModel is the abstract superclass of all GFGraphicObjects and other classes in GF/ST.  It tracks event handlers using its own event table instead of the class variable in Object, as is done with standard Visual Smalltalk events.  In this way, we can depend on garbage collection to clean up events when a GO is no longer referenced by anyone.  Without this approach, you would be forced to send the #release message to all GO's when you were done with them, and efficiency would be compromised.

Instance Variables:

eventTable - <IdentityDictionary with key: Symbol and value: ActionSequence>  Holds the equivalent of the Visual Smalltalk class variable EventHandlers in Object.  When you triggerEvent: aSymbol, the ActionSequence for aSymbol is evaluated.


RectangleFromUserWindow

Superclass:  Object

Subclasses:  None

Platform Specifics:  IBM Smalltalk/VisualAge only

Description:  The RectangleFromUserWindow implements the behavior necessary to prompt the user for an area of the screen from which to obtain a GFPixmap.  This behavior is present in other Smalltalks in the standard fromUser method implemented in  Image or other classes.

Instance Variables:

shell - <CwTopLevelShell>  The shell

main - <CwMainWindow>  The main window

trackingState - <Symbol>  Either #tracking or #done

startPoint - <Point>  The point where tracking initiates

cornerPoint - <Point>  The point where tracking is done

gc - <CgGC>  The graphics context


Summary of Events

Many GF/ST classes, including GFGraphicObject and GFDrawingInterface, are subclasses of PsiEventModel.  These classes always inherit all events from their superclass via the eventsTriggered class method.  The list below summarizes the events which are added at each level of the PsiEventModel hierarchy.

PsiEventModel
changed
changed:

GFDrawingInterface
getMenu
handleInvoked
mousePointChanged
noHandleInvoked
resizeVisibleArea
select

GFGraphicObject
attributeState
button1DoubleClick
damaged
deselect
generateHandles
getMenu
getMetaObject
needsPositionHandle
positionState
releasing
select
translate

GFAbstractPathGO

GFClosedPathGO

GFEllipseGO
resize

GFRectangleGO
resize

GFRoundedRectangleGO

GFDirectedPathGO

GFLineGO
moveEnd
moveStart

GFDependentLineGO

GFPolylineGO
resize
rotate

GFBezierGO
moveEnd
moveStart

GFOrthogonalPathGO
moveEnd
moveStart

GFDependentOrthogonalPathGO

GFSplineGO

GFCachedGO
resize

GFGroupGO

GFImageGO

GFButtonGO

GFCompositeGO
resetVisibleArea
resize

GFDrawing
damageRegion
redraw

GFHostWidgetGO
resize

GFTextGO

GFToolPalette
addedCategory
addedToolToCategory
removedCategory
removedToolFromCategory
selectCategory
selectTool

Related Topics:

Events Explained


Events Explained

From the hierarchy given above, you can see what classes and their subclasses will trigger events.  The table below summarizes the events by name, indicating the condition that causes the event to be triggered.

Event	Description
addedCategory	A GFCategory was added to a GFToolPalette.
addedToolToCategory	A GFTool was added to a GFCategory in a GFToolPalette.
attributeState	An undo operation requested attribute information.  Handlers of this event must return the attribute state information for the attribute GFMemento to be put on the undo stack.
Event	Description
button1DoubleClick	A left mouse button double-click was performed with the mouse positioned on a GO.
changed	Retained for compatibility with the standard changed-update dependency mechanism in Object.
changed:	Retained for compatibility with the standard changed-update dependency mechanism in Object.
damaged	Triggered when a GO has damaged its area or changed its state in some way.
damageRegion	Triggered when a particular area of a drawing will be damaged.
deselect	Triggered when a GO has been deselected.
generateHandles	Triggered when a GO requires handles.  Handlers of this event must return a collection of GFHandles for the GO.
getMenu	Triggered when a GO requires a menu (i.e., when the user pops up a menu via the right mouse button).  Handlers of this event must return an instance of Menu.
getMetaObject	Triggered when you send #metaObject to a GO.  Handlers of this event must return an object which is the metaObject for the GO (i.e., typically a domain model of some kind).
Event	Description
handleInvoked	Triggered when you invoke a handle by clicking on it.  Can be used to provide help in a status bar, as in the Drawing Editor.
mousePointChanged	Triggered by a GFDrawingInterface when you move the mouse position.  Can be used to provide feedback on mouse position, as in the Drawing Editor.
moveEnd	Triggered by a GFLineGO when the ending position changes.
moveStart	Triggered by a GFLineGO when the starting position changes.
needsPositionHandle	Triggered by a GO when the user is going to move it.  Handlers must return an instance of GFHandle.  Default handling uses a GFSelectionTrackHandle to position the GO.
noHandleInvoked	Triggered when no handles are invoked for the GO; i.e., when no handle operations are ongoing.  Can be used to know when to clear help information from a status pane.
positionState	An undo operation requested position information.  Handlers of this event must return the position state information for the position GFMemento to be put on the undo stack.
redraw	Triggered when a drawing will be redrawn.
releasing	Triggered when a GO is released; i.e., at the beginning of the #release method.
Event	Description
removedCategory	Triggered when a GFCategory was removed from a GFToolPalette.
removedToolFromCategory	Triggered when a GFTool was removed from a GFCategory.
resetVisibleArea	Triggered when a GFCompositeGO (or which a GFDrawing is one) has changed in a manner that causes the visible area to change (e.g., via scrolling or resizing).
resize	A GO was resized.
rotate	A GFPolylineGO was rotated.
select	A GO was selected.
selectCategory	Triggered when a GFCategory is selected in a GFToolPalette.
selectTool	Triggered when a GFTool is selected in a GFCategory of a GFToolPalette.
translate	A GO was moved.


What is the Visual Inspector?

The Visual Inspector™ is a utility provided by Polymorphic Software, Inc., and it comes with full source when you buy GF/ST.  It is a learning tool, a productivity tool, and a better inspector, and we believe it will be a valuable addition to your Smalltalk environment.  It provides a graphical presentation of any objects in Smalltalk, showing the relationships between them and allowing you to modify them directly.

Standard Smalltalk inspectors have three main disadvantages:

1.	You can only inspect one object in one window.

2.	You cannot see how objects connect to one another.

3.	It is difficult to make one object an instance variable of another from an inspector.

Because you can only inspect one object in one inspector window, you must open many windows at once, or you must open and close many windows before you locate the object you are after.  You often need to be able to tell if the object you are inspecting in one place is, in fact, exactly the same object you are holding onto in another inspector.  The way most people deal with this problem is by assigning a global variable to hold onto one object in one inspector:

Test := self.

and then going to the other inspector and evaluating:

self == Test

to see if they are the same object.  Similarly, you may want to place one object into the instance variable of another as you are inspecting.  With the standard Smalltalk inspectors, there is often no way to perform such a feat without resorting to use of globals.

The Visual Inspector cures all of these problems, and in fact, makes them incredibly easy to do, without sacrificing the things you like in a standard inspector.  We expect you will still have a use for the standard inspectors, so we have made them easy to get to from the Visual Inspector.

When you purchase GF/ST, you get the Visual Inspector with full source code.  It is installed by default when you install GF/ST.  Polymorphic also provides a separate non-source version of the Visual Inspector as a freeware Smalltalk tool on some platforms.


Visually Inspecting Objects

You can start the Visual Inspector from the GF/ST launcher.  Alternatively, you can send the message psiVisualInspect to any object.  Consult the README file for specific information about hooking the Visual Inspector into base image "inspect" menus.

Here is what the Visual Inspector looks like initially when you evaluate:

Transcript psiVisualInspectt.

We're going to use the Visual Smalltalk implementation of the Transcript for our explanation.  You should try this exercise with the Transcript on your platform; the differences are enlightening.

In Visual Smalltalk, the Transcript is an instance of TranscriptWindow.  The default Visual Inspector display of an object shows a chiseled box with text displaying the class of the object.

You can move objects on the screen by holding down the left mouse button while the cursor is inside of the object, and dragging the object to where you want it.  You can select multiple objects on the screen by rubber-banding around them.  When multiple objects are selected, you can move them as a group by moving any one of them.

Select the box by clicking inside it.  It will show a red highlight around its edge, and some handles will appear on it.  The handles perform the actions shown below:

When you click on the Show non-nil handle, the object display changes to show all non-nil instance variables:

Notice that the Hide all handle now shows up, too.  Only the views instance variable of the Transcript is non-nil.  If you want to see the others, click on the Show all handle to expand the list.  The Show none handle brings it back to the original display.  The Visual Inspector always shows the instance variables in sorted order.  The little Inspect handle on the right of the instance variable name will open another object in the same Visual Inspector window and indicate the relationship between the objects:

In this case, the kind of object you have added is an OrderedCollection; i.e., the Transcript holds onto an instance of OrderedCollection in its views instance variable.  OrderedCollections do not have named instance variables, so when you select the Show non-nil handle on it, you will see the one index in the collection that holds onto a non-nil object.  If you, in turn, inspect it, you will eventually see the following:

Now, you can instantly tell that the TopPane's owner is the Transcript.  Expanding the display further (and using the
Display ⇒ Collapsed pop-up menu), you can see the basic relationship between a ViewManager (the TranscriptWindow), its TopPane, and any SubPanes (the TextPane).  In this case, the ViewManager holds onto a collection of views, of which there is only one, an instance of TopPane.  The TopPane's owner is the ViewManager.  The TopPane holds onto a collection of children, of which there is only one, an instance of TextPane.  The TextPane's parent is the TopPane.

To see the same kind information in a standard inspector, you would be looking at this:

and you would have to evaluate expressions like:

Test := self.

in the TopPane inspector, and:

self parent == Test

in the TextPane inspector to determine if, in fact, they are the same object.

Now, this is not to say that the standard inspectors in Smalltalk are not what you want sometimes - they display a lot of information in a compact form.  However, they provide no information on how objects are connected to one another, and you can only inspect one object in one window.


Specific Inspectors for Specific Objects

You will see as you use the Visual Inspector that there are different displays for the different kinds of Smalltalk objects.  In the base image, the default inspector for all objects is the Inspector.  OrderedCollections use an OrderedCollectionInspector, and Dictionaries use a DictionaryInspector.  The Visual Inspector uses a different visual display and specific menu for these kinds of objects, too.

When you inspect a variable byte object of some kind (like a String or Symbol), you will find additional menu items in the pop-up menu for that object (see Object-Specific Menu Options).  These options let you display the object in character format, or in binary, octal, hexadecimal, and decimal radix format.  You can also specify whether the display is based on 1, 2, or 4 bytes.


Changing Object State

A common operation in an object inspector is to change one of the object's instance variables (i.e., change its state).  You can still reach the standard inspector from the Visual Inspector (see Open Menu).  However, when you already have several objects in the display, it is very simple to make an instance variable of one object hold onto another object.  To reassign an instance variable, just hold down the left mouse button on the Inspect inst var handle on the right side of a field, and drag over to another object.

The instance variable connecting line will snap to objects as you drag over them, and the display will reflect the new assignment when you release the mouse button.

The Visual Workspace™ is an enhanced version of the Visual Inspector that is available from Polymorphic.  It combines the most effective visual techniques with the most effective textual techniques in a single integrated environment.


Appendix A - The Visual Inspector - Visual Inspector Menu
Visual Inspector Menu

There is a VisualInspector pull-down menu that is the same as the pop-up menu you will see when you click the right mouse button outside of any object in the inspector:

Scale	⇒

Zoom In	PgUp

Zoom Out	PgDn

Add Object...

Delete Selections	Del

Update

Set Font...

Set Selection Color...

Print

Paths	⇒

Scale - A submenu displaying a set of predefined scales is displayed, ranging from 25% to 200%.

Zoom In - Increase magnification by 10%.

Zoom Out - Decrease magnification by 10%.

Add Object... - A prompter will appear.  Type in a Smalltalk expression to be evaluated, and the resulting object will be placed in the Visual Inspector.

Delete Selections - Delete all selected objects from the display.  Select multiple objects by rubber-banding completely around them.

Update - Update the display in the Visual Inspector.

Set Font... - A standard dialog will appear, allowing you to choose the font used in the Visual Inspector.

Set Selection Color... - A standard dialog will appear, allowing you to choose the color to indicate the selected object in the Visual Inspector.  The default color is red.

Print - Print the display.  What-you-see-is-what-you-get (WYSIWYG) printing is disabled for the free version of the Visual Inspector, but it is available to owners of GF/ST.

Paths - The submenu allows you to change all paths showing object connectivity to use straight lines with and without arrowheads, or orthogonal lines with and without arrowheads.


Object-Specific Menu Options

When you hold your cursor over the top of an object in the display and pop-up the menu using the right mouse button, you will see the following menu:

Display	⇒

Open	⇒

Delete

Remove Field	⇒

Show Fields	⇒

Visual Inspector	⇒

Note that the pop-up menu will operate on the specific object your cursor was over when you popped up the menu, even if it is not the selected object (indicated with a red border, by default).

The Delete option is the same as pressing the red "x" handle in the upper left corner of the object.  The submenus provide access to different display modes for objects and let you open other inspectors.

Related Topics:

Display Menu

Open Menu

Remove Field Menu

Show Fields Menu

Visual Inspector Menu


Display Menu

The Display menu lets you modify the default way an object displays in the Visual Inspector.  By default, the class of the object is shown, and the full set of inspecting abilities are enabled, such as being able to show instance variables.  You can also collapse the object to a tiny box, or display it as any arbitrary string in a box.  Alternatively, you can keep the full range of inspecting options available, but change the title on the box.

Collapsed

As String

Full

Title	⇒

Collapsed - Display in Collapsed mode.  The object will show up as a small square box and cannot be expanded to show instance variables.  Connecting objects will still be connected to the Collapsed display version.

As String - Display the "printString" result for the object.  The resulting display cannot be expanded to show instance variables.  Connecting objects will still be connected to the As String display version.

Full - Display in the Full mode, with a title of your choice (the class name is the default) and with access to the instance variables via handles or the pop-up menu.

The default Full, Collapsed, and As String displays.
Title - When using the "Full" display mode, the Title submenu lets you modify the title at the top of the object display.  You will still have access to the object's instance variables.

The Full display, using a user-supplied title.


Open Menu

Visual Inspector

Standard Inspector

Basic Inspector

Class Browser

Visual Inspector - Open a new Visual Inspector on the object.  This option would be the equivalent of sending the message psiVisualInspect to the object.

Standard Inspector - Open a standard inspector on the object.  This option would be the equivalent of sending the message inspect to the object.

Basic Inspector -  Open a standard inspector on the object.  This option would be the equivalent of sending the message basicInspect to the object.

Class Browser -  Open a class browser on the object.  This option would be the equivalent of sending the message browse to the object.


Remove Field Menu

The Remove Field menu contents shows the instance or indexed instance variables which are currently showing.  If no instance variables are showing for the object, the menu option will be disabled.  When you select a field, the instance variable is deleted from the list.  Choosing a field from the submenu is the same as clicking the red "x" at the left edge of the list.


Show Fields Menu

None

Non-Nil

Connected

All

Specific	⇒

None - Show no fields.

Non-Nil -  Only show fields that are not nil.

Connected - Show only the fields that are connected to other objects in the display.

All - Show all fields.

Specific - Show a submenu with all fields not currently being displayed.  Selecting an item from the submenu will add it to the current list.


Appendix A - The Visual Inspector - Object-Specific Menu Options - Visual Inspector Menu
Visual Inspector Menu

When you use the pop-up menu on a specific object, this menu is the same as the pull-down Visual Inspector menu.