- Chapter 17 The Programming Interface
The Programming Interface
This chapter shows how a programmer adds new classes to the system and then tests and debugs their behavior using the Smalltalk-80 programming environment. The chapter presents a scenario of how a programmer might add class FinancialHistory to the system. FinancialHistory was used in the first part of this book as an example class. Its protocol and implementation descriptions can be found inside the front cover of this book. This example scenario is not intended as an exhaustive survey of the Smalltalk-80 programming interface. It is intended as an overview that provides motivation for the kinds of graphics support described in subsequent chapters.
A user and the Smalltalk-80 programming environment interact through a bitmap display screen, a keyboard, and a pointing device. The display is used to present graphical and textual views of information to the user. The keyboard is used to present textual information to the system. The pointing device is used to select information on the display screen. Smalltalk-80 uses an indirect pointing device called a mouse. A cursor on the screen shows the location currently being pointed to by the mouse, The cursor is moved by moving the mouse over a flat surface. The mouse has three buttons, which are used to make different kinds of selection.
The display screen contains one or more rectangular areas called views. The views are displayed on a gray background and may overlap. Each view has a title shown at its upper left corner. Figure 17.1 shows the Smalltalk screen with two overlapping views on it. Their titles are Workspace and System Browser.These two views contain only text; other views might contain pictures or both text and pictures.
The view toward the top of the figure is a workspace. It contains text that can be edited or evaluated. The view towards the bottom of the figure is a system browser. It allows the class descriptions in the system to be viewed and edited. The arrow in the lower right part of the browser is the cursor. It shows the current location of the mouse. At the lower right corner of each figure in this chapter will be a small rectangle containing three ovals arranged side by side. These ovals represent the three mouse buttons. When one of the buttons is pressed, the corresponding oval will be filled in. The buttons will be referred to as the left, middle, and right buttons, even though they may not be arranged side by side on some mice.
A variety of information is typically visible on the Smalltalk-80 display screen. In order to take some action, the user indicates what part of the visible information should be affected. The general activity of directing attention to a particular piece of information is called selection. The system gives visual feedback to indicate the current selection. The most common feedback mechanism is to complement a rectangular area or the screen, changing black to white and white to black. To begin using the system, one of the views is selected. The selected view is indicated by complementing only its title. The selected view will be completely displayed, obscuring the overlapping parts of any other views. In Figure 17.1, the browser is the selected view.
A different view can be selected by moving the cursor into part of its rectangular frame that hasn't been overlapped by other views, and then pressing the left button on the mouse. In Figure 17.2, the workspace has been selected. Note that the left mouse button is pressed. The workspace now obscures the overlapped part of the browser.
The Smalltalk-80 text editor provides the ability to select text and to perform editing operations on that selected text. For example, to replace the sequence of characters the standard with my special in the workspace, the old characters are selected and then the new characters are typed.
Characters are selected using the left mouse button. The cursor is positioned at one end of the selection and the mouse button is pressed (Figure 17.3).
The text selection is now empty--it contains no characters. The position of an empty selection is shown with a carat (an inverted "v"). The carat is partially obscured by the cursor in Figure 17.3. While the mouse button remains pressed, the cursor is moved to the other end of the characters to be selected. The selected characters are shown in a complemented rectangle (Figure 17.4).
When the button is released, the selection is complete (Figure 17.5). When characters are typed on the keyboard, they replace the selected characters. After typing the new characters, the selection is empty and positioned at the end of the new characters (Figure 17.6).
Another kind of selection used in the user interface is called menu selection. The middle and right mouse buttons are used to select commands from one of two menus. When one of these buttons is pressed, a menu appears at the location of the cursor. The menu obtained by pressing the middle button contains commands relevant to the contents of the selected view. When the view contains editable text, as does the workspace, these commands relate to text manipulation. The menu obtained by pressing the right button contains commands relevant to the selected view itself. The middle-button menu may be different in different views, but the right-button menu is always the same.
Characters can be deleted from a piece of text by selecting the characters and then invoking the cut command from the middle-button menu. In the next picture, the characters special have been selected and the middle button has been pressed. The menu of commands relevant to the contents of the view has appeared. While the button is held down, the cursor is moved to select the cut command in the menu (Figure 17.7). When the button is released, the selected command is carried out. In this example, the selected text is removed (Figure 17.8).
A text selection can be treated as a Smalltalk-80 expression and evaluated. There are two commands in the middle-button menu to carry out such an operation, doIt and printIt. Selecting doIt simply evaluates the selected expression and discards the resulting value. Selecting printIt evaluates the selected expression and prints its value after the expression. For example, after typing and selecting the expression Time now, printIt will print out the resulting new instance of Time (Figure 17.9). The printed result becomes the current text selection (Figure 17.10).
If the cursor is moved outside the menu before the button is released, no command is carried out.
A browser is a view of the classes in the Smalltalk-80 system. Existing classes are examined and changed using a browser. New classes are added to the system using a browser. A browser consists of five rectangular subviews. Along the top are four subviews showing lists. Each list may or may not have one of its items selected. The selected item in each list is complemented. The contents of the list cannot be edited through the view, they can only be selected. Below the four list subviews is a subview showing some text. That subview is similar to the workspace, allowing the text to be edited. The selections in the four lists determine what text is visible in the lower subview. When a selection has been made in all four lists, the lower subview shows a Smalltalk-80 method. The method is found in a class determined by the selections in the two lists on the left. The method within that class is determined by the selections in the two lists on the right. The browser in Figure 17.11 is showing the method used by Rectangles to respond to the message center.
The classes in the system are organized into categories. The leftmost list in the browser shows the categories of classes in the system. When a category is selected, the classes in that category are shown in the next list to the right. In the example, the category Graphics-Primitives is selected. That category has four classes in it. When one of these classes is selected, its message categories are shown in the next list to its right.
Since Rectangle is selected, the categories in its instance protocol are displayed. At the bottom of the second list, two rectangular areas are labeled instance and class. One of these will be selected at all times. If class is selected, then the next list to the right shows the categories of class messages; if instance is selected, the list shows the categories of instance messages. When a message category is selected, the selectors of messages in that category are shown in the rightmost list. When one of these message selectors is selected, the corresponding method is displayed in the subview at the bottom of the browser. The method displayed can be edited and the old version can be replaced by the edited version, if desired.
A selection is made in a list by placing the cursor over an item and then pressing and releasing the left mouse button. In Figure 17.12, another item is selected in the browser's rightmost list. Therefore, another method is presented in the lower text subview.
If the left button is pressed and released while the cursor is over the item already selected, that item is deselected (Figure 17.13).
When a message category is selected, but none of its message selectors have been selected in the rightmost list, the lower subview contains some text describing the various syntactic parts of a method. This text can be replaced with a new method to be added to the system. The new method will be added to the selected category.
If a class category has been selected, but none of its classes has been selected, the lower subview contains some text describing the various parts of a class definition. This text is in the form of a message to a class (Object, in this case) asking it to create a new subclass of itself (Figure 17.14).
A view may not be large enough to show all of the information it might. For example, many of the lists viewed by the browser are too long to be completely displayed in the space available. The view can be positioned on different parts of the list by using a scroll bar. A scroll bar is a rectangular area that appears to the left of the subview containing the cursor. The gray box in the scroll bar indicates which part of the total list is visible in the view. The height of the scroll bar represents the length of the entire list. The part of the scroll bar occupied by the gray box indicates the part of the list that is visible.
By moving the mouse into the scroll bar, another part of the list can be shown. This is called scrolling. When the cursor is in the right hall of the scroll bar, it takes the shape of an upward pointing arrow. If the left mouse button is pressed, the items in the list appears to move up in the subview and new items become visible at the bottom. When the cursor is in the left half of the scroll bar, its shape is a downward pointing arrow; pressing the left button makes an earlier part of the list visible. For example, the browser's leftmost list can be scrolled to show categories earlier in the list (Figure 17.15).
Views containing text can also be scrolled if the view is too small to show all the text.
A new class can be added to the system by selecting a class category and editing the text describing the parts of a class definition. The FinancialHistory, example will be added to the category named New Projects.
While text is being changed in the lower subview, it may not accurately represent a class definition or a method. The accept command in the middle-button menu is used to indicate that the editing has been completed and the class definition or method should be added to the system (Figures 17.16 and 17.17).
The menu that appears when the middle button is pressed is different in each of the browser's subviews. In the subview showing the classes in a category, the menu includes an item called definition. This command causes the class definition of an existing class to be displayed (Figures 17.18 and 17.19).
This class definition can then be modified with the standard text editing operations. After changing the text, accept must be selected again in the middle-button menu. For example, an instance variable could be added to all Rectangles by adding its name to the appropriate place in the class definition.
Another item in the middle-button menu for the class list is categories. When it is selected, the message categorization is shown in the bottom subview (Figures 17.20 and 17.21).
The new class has a single, empty message category called As yet unclassified.
The categorization can be changed by editing the text and selecting accept (Figures 17.22 and 17.23). Notice the change in the third subview from the left of Figure 17.23. There are now three categories, transaction recording, inquiries, and private.
After a new class has been added to the system, methods can be added by selecting categories and editing method templates (Figures 17.24 and 17.25).
Notice the change in the rightmost subview of Figure 17.25. The selector of the new method is added to the (previously empty) list and becomes the current selection.
After the methods shown in Chapter 5 have been added to FinancialHistory, instances can be created and tested by sending them messages. First, a new global variable will be added to the system by sending the message at:put: to the dictionary of global variables whose name is Smalltalk. The first argument of at:put: is the name of the global variable and the second is the initial value. This global variable will be used to refer to the instance being tested (Figure 17.26).
Messages are sent to HouseholdFinances by typing expressions in the workspace and evaluating them by invoking the commands doIt or printIt (Figure 17.27). Several expressions can be selected and evaluated at one time. The expressions are separated by periods (Figure 17.28).
Selecting printIt instead of doIt displays the result following the expression (Figures 17.29 and 17.30).
An inspector is a view of an object's instance variables. An inspector is created by sending inspect to the object whose instance variables are to be viewed (Figure 17.31).
After inspect has been sent, the user is prompted for a rectangular area in which to display the inspector. The shape of the cursor is changed to indicate that the upper left corner of the rectangular area should be specified (Figure 17.32).
The cursor is moved to the desired location and the left mouse button is pressed and held down. The shape of the cursor is changed again to indicate that the lower right corner of the rectangular area should now be specified. As long as the left mouse button remains pressed, the prospective new rectangular frame is displayed (Figure 17.33).
When the button is released, the inspector is displayed in the selected area (Figure 17.34).
The title of the inspector is the name of the inspected object's class.
An inspector has two subviews. The left subview shows a list containing self and the names of the object's instance variables. When one of the elements of the list is selected, the corresponding value is printed in the subview to the right (Figures 17.35 and 17.36). The text that appears in the righthand subview is obtained as a result of sending printString to the selected object. Selecting the element self at the top of the list, prints the object being inspected (Figure 17.37).
When an error is encountered, the process in which the error occurred is suspended and a view of that process is created. Suspended processes can be viewed in two ways, with notifiers and with debuggers. A notifier provides a simple description of the process at the time of the error. A debugger provides a more detailed view and also the ability to change the state of the suspended process before resuming it.
As an example of error reporting, we will follow the addition and debugging of several new methods in FinancialHistory. The following methods contain several errors which will be "discovered" in the testing process. The intention of these new methods is to allow a FinancialHistory to give a summary report of its state.
report | reportStream | reportStream ← WriteStream on: (String new: 10). reportStream cr. reportStream nextPutAll: 'Expenses'. reportStream cr. self expenseReasons do: [ :reason | reportStream tab. reportStream nextPutAll: reason. reportStream tab. reportStream nextPutAll: (self totalSpentFor: reason). reportStream cr]. reportStream nextPutAll: 'Incomes'. reportStream cr. self incomeSources do: [ :source | reportStream tab. reportStream nextPutAll: source. reportStream tab. reportStream nextPutAll: (self totalReceivedFrom: source). reportStream cr]. ↑reportStream contents incomeSources ↑incomes keys expenditureReasons ↑expenditures keys
A new category is added and the new methods typed in and accepted (Figure 17.38).
After adding the new methods, the instance of FinancialHistory can be asked for a report by evaluating an expression in the workspace (Figure 17.39), Instead of printing the report, a notifier appears on the screen (Figure 17.40).
A notifier is a simple view of a process suspended after an error. The notifier's title indicates the nature of the error. Notifiers are created by sending an object the message error:. The argument of the message becomes the title of the notifier. The notifier shown in Figure 17.40 indicates that the message expenseReasons was sent to an object that did not understand it. The list visible in the notifier shows part of the state of the suspended process.
The cause of this error is evident from the title of the notifier. The message added to FinancialHistory was expenditureReasons not expenseReasons. The notifier and the erroneous process can be discarded by selecting the command close in the right mouse-button menu (Figure 17.41).
The misspelling in report can be corrected in the browser (Figures 17.42 and 17.43).
After fixing the misspelling in the browser, the original expression can be evaluated in the workspace again (Figures 17.44 and 17.45).
This creates another notifier. The cause of this error is not as obvious. The message do: was sent to an object that did not understand it. In order to learn more about the error, a more detailed view of the suspended process can be obtained by selecting the command debug in the middle-button menu.
A debugger is a view of a suspended process that reveals more details than a notifier reveals. When debug is selected in a notifier's middle-button menu, a debugger is created viewing the same process the notifier viewed (Figure 17.46). After selecting debug, the user is prompted to supply a rectangular area in which to display the debugger. The rectangle is specified in the same way that the rectangular frame for a new inspector is specified (Figure 17.47).
The debugger has six subviews. The top subview shows the same list that was visible through the notifier. This list gives a partial history of the process in which the error occurred. Each item corresponds to a message that was sent and whose response is not yet completed. The item contains the name of the receiver's class and the selector of the message, separated by ">>". The last item visible in the list, FinancialHistory>> report, indicates that an instance of FinancialHistory received the message report. This message was sent when the middle-button menu command printIt was selected while the expression HouseholdFinances report was the text selection. When one of the items in the debugger's upper list is selected, the method invoked by the corresponding message is displayed in the subview immediately below.
When a method is displayed, the last message sent before the process was suspended is complemented. Figure 17.48 shows that the message do: was sent to the result of the expression self expenditureReasons. The next item up on the list, Set >> do:, indicates that the receiver of do: was an instance of Set. The method invoked can be seen by selecting Set >> do: in the list.
Figure 17.49 shows that this method sent a message to the object 1. The next item up on the list, SmallInteger(Number)>> to:do: shows that the receiver was an instance of SmallInteger. When the method invoked by a message was found in a superclass of the receiver's class, the name of that superclass is included in parentheses after the receiver's direct class. In the example, the method for to:do: was found in class Number.
The top item on the list, SmallInteger(Object) >> doesNotUnderstand:, shows the last thing that happened before the process was suspended--an instance of SmallInteger received the message doesNotUnderstand:. This message was sent by the system when the do: message was not found in SmallInteger or in any of its superclasses. The doesNotUnderstand: message invoked a method that suspended the process and created the notifier viewing it. The second item from the top of the list, WriteStream(Stream)>> nextPutAll:, indicates that the misunderstood do: message was sent from the method for nextPutAll: in class Stream. Figure 17.50 shows the debugger with that item selected. The method displayed shows that do: was sent to the object named aCollection, which was provided as the argument of nextPutAll:.
The lower four subviews of a debugger are used to find the value of the variables used in the method. They function like two inspectors. The leftmost subview shows a list of the receiver (self) and its instance variable names. The third subview from the left shows the argument names and temporary variable names. When a name is selected in either one of these lists, the value of the associated variable is shown in the subview to its right. To display the receiver of the do: message, aCollection is selected in Figure 17.50.
The source of this error appears to be that the Stream was expecting a collection of elements as the argument of nextPutAll: and it got a number, 700, instead. Selecting the next item down from the top list shows where the argument came from. The argument was the result of evaluating the expression (self totalSpentFor: reason). In Figure 17.51, selections have been made in the bottom subviews to display the values of the instance variable, expenditures, and the argument, reason.
When text is selected and evaluated in the method displayed in the browser, the variable names are interpreted in that context. So the argument to nextPutAll: can be found by re-executing the expression (self totalSpentFor: reason) and printing the result (Figure 17.52).
The result is 700, as expected (Figure 17.53).
The report method had intended that the character representation of 700 be appended to reportStream, but it appended the number itself instead. This bug is fixed by sending the number the message printString. The correction can be made in the debugger (Figure 17.54). Now the expression can be evaluated again. The report is now successfully printed in the workspace (Figure 17.55).
This completes the overview of the Smalltalk-80 programming interface. The ability to support this type of interaction with the user motivates the nature of many of the graphics classes discussed in subsequent chapters.