Introducing GF/ST
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.
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
GF/ST is available as a VA Smalltalk feature. You can also load it via configuration maps. GF/ST is composed of all of the following configuration maps:
•GF Calendar Goodie IBM/ST
•GF/ST Graph Layout Demo IBM/ST
•GF/ST Graph Layout IBM/ST
•GF/ST Demo IBM/ST
•GF/ST IBM/ST
•GF/ST IBM/ST - Runtime
•Visual Inspector (TM) IBM/ST
To load all of GF/ST and the demos, you can select 'load with all required maps' after selecting the 'GF/ST Demo IBM/ST' configuration map. To load the minimum required for base GF/ST functionality, load only the 'GF/ST IBM/ST' configuration map.
Note:
The Drawing Editor and the Demo Launcher for GF/ST relies on bitmaps files in the RESOURCE directory: <vas>\bitmaps\resource directory where <vas> is the installation directory for VA Smalltalk.
The RESOURCE subdirectory contains bitmap files in BMP format which are used in various areas of GF/ST. The bitmaps are held externally and converted to Images in a lazy initialization technique, as they are needed.
Conventions Used in This Document
We set any Smalltalk code and method selectors in a monospace font, likeThis, so you can differentiate them from more normal text.
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.
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.
You will use an
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. 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, read on, because unless you understand events, everything in GF/ST may look like it is happening by magic.
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 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:
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
GF/ST Application Design & Decisionmaking. 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
Class Hierarchy, Protocol and Events 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.
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 30 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 Instantiations-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 e-mail at vast-support@instantiations.com. Describe your problem in detail.
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 prefixed with “GF”. Non-GF-specific classes (such as PsiEventModel), are prefixed with “Psi”, reflecting their heritage from Polymorphic Software (the original developers of GF/ST). 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.
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
Class Hierarchy, Protocol and Events 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.
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.
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.
Use Double-buffered graphics when you need it
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.
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).
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.
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
Class Hierarchy, Protocol and Events 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 GF/ST, the event model (PsiEventModel) sets up constraints using:
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.
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.
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.
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.
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
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 all versions.
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.
Why Do You Use That Syntax?
GF/ST modifies no base image methods on the VA Smalltalk platform.
Because GF/ST was designed to gain maximum portability across Smalltalk platforms, the 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:.
We could have written:
findTargetAction := GFMessage
receiver: self
selector: #graphicObjectIn:at:.
where, 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!
Licensing
GF/ST software is copyrighted and owned by ObjectShare, Inc. The VA Smalltalk version is also partially copyrighted by Instantiations, Inc. which acts as its sole distributor (under license from ObjectShare) GF/ST is written entirely in Smalltalk and is licensed to you with full source code upon purchase and acceptance of the Instantiations License Agreement that accompanies its delivery. Details of licensing and distribution of applications based on GF/ST are provided in the Instantiations License Agreement at the beginning of this document.
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 Instantiations. You may not distribute any source code provided with, taken from, or derived from GF/ST without express written consent from Instantiations, 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.